From fcb3973abdaeaa0988d8a13d1b536f9b3759e6f5 Mon Sep 17 00:00:00 2001 From: John Wiseman Date: Mon, 2 Jun 2025 18:43:33 +0100 Subject: [PATCH] 6.0.24.72 --- ...d2266f8ab7e39728796f66092f2be0511.svn-base | 3356 ++ ...e1f1f816128476fe53b8ac9e78d519dea.svn-base | 13528 +++---- ...c4cd0f242aaba204301966dc699e1ff06.svn-base | 4358 +-- ...55b0f09c6edbdbc765bbd700d7d4d582a.svn-base | 804 +- ...04ab29bd13f02a3d71b7d4be4b771493b.svn-base | 6274 +-- ...ab4fc086c05a5870e1f63d0706d6449c3.svn-base | 4344 +-- ...92ce6b8878c3ec022bb10c4789a6bed84.svn-base | 12944 +++---- ...8e866f3bfdc3f0e0165284537a082c940.svn-base | 4580 +++ ...63ff51f065faa04896011ca001f649084.svn-base | 5666 +-- ...46860a29581f11d388b03c6edce6b1ba7.svn-base | 6606 ++-- ...64b812bc35275cb94be12d218bdde8695.svn-base | 434 +- ...43e92df43bad8b89ae05eebc20785616f.svn-base | 7212 ++++ ...7b92d792fe75a9e475ab1bc1659e7cbe9.svn-base | 5684 +-- ...0584b9f0e052dc45954051f1ed529bd4e.svn-base | 2845 ++ ...b302e19f6b95826756b666ea5160017b1.svn-base | 31952 ++++++++-------- ...337476ec2437b08e91867ce55dd520d9c.svn-base | 3138 +- ...373c35ae42dedfb37211ae3c1344e9bc0.svn-base | 4780 +-- ...7fd023912bcc03705c2840b02ad1c28a0.svn-base | 207 + ...a1897ba7460f3a7cb65433525d6ebe1bf.svn-base | 4334 +-- ...0146a010c7b19d3474dd95106c4091790.svn-base | 6242 +-- ...8d0b061edf74eec91683457323986b811.svn-base | 12878 +++---- ...4d98e728faaea7427c1617e1d9a448d35.svn-base | 11816 +++--- ...2e24b5192ec6392fa27fcff7a3591faf6.svn-base | 7478 ++-- ...a08ce63307eed7efc5218070bd857ff44.svn-base | 868 +- ...7ad7d5100707ee83b6973c8d6b39d06af.svn-base | 10560 ++--- ...1765d2582d83d719015a50f469a644d6a.svn-base | 252 +- ...d010c0714be4be7546a03ef5f3a49a2ad.svn-base | 126 + ...bf279d46a785152e3f1eead48b0a500aa.svn-base | 3270 +- ...1e5765526a2550c590f7d5d85af8bd68a.svn-base | 4338 +-- ...251a58ba45f1c372fc50dbb81839ab68f.svn-base | 2942 +- ...d081ba69e9967156cb17f2667db92dae5.svn-base | 8754 ++--- ...28b0cbe547f7e4f7aaa5d5ad4c71b40a7.svn-base | 11730 +++--- ...c632ceb9b93c89ae688ba6c75c292d65d.svn-base | 11324 +++--- ...4cfaa1e28b5d322f032bbd3b78c960f16.svn-base | 9098 ++--- ...2d0e2bf2b3ce3b157ec788450a8c5c1be.svn-base | 412 +- ...c7f89cdc49114a5927759769be30c8ed3.svn-base | 1472 + ...0778eb428d4d3834c5690806ce79ee309.svn-base | 6296 +-- ...ae8ef3084987ca2b4453cb62e2504160d.svn-base | 5764 +-- ...bc4e69bc6df441bb7cebf2f19cb6ecb3c.svn-base | 1598 +- ...5caa184ca0c786999f6e9e50dbe277532.svn-base | 6678 ++-- ...0733a02cf60084bbc7c91d0ce1d5db13c.svn-base | 6772 ++++ ...4b089aaf7e45a1f6684efb3e14627a22b.svn-base | 11338 +++--- ...c0611e79b6262965a7219f720c15467fa.svn-base | 2808 +- ...d15f4b26153ff6a040c05402e17b135a0.svn-base | 451 + ...861fce1cf458b5e18f02ae85b5ee49b00.svn-base | 9158 ++--- ...e43670502eb3c5e97b8ee5916fefe10ef.svn-base | 3038 +- ...ec86fd47c85ed25d607ebe9193dcca91f.svn-base | 13536 +++---- ...bd66b6129ae2b185b6606c1a3bbb9c6e5.svn-base | 5669 +++ ...f22583c8c49000f0df6186ce56601cfdb.svn-base | 3073 +- ...cb47bd75bfc868415b91a1ec6b3f630d5.svn-base | 432 +- ...3ca0a919f9983b238ae37e00b12d88c86.svn-base | 252 +- ...61943edea930acfa991c1eb65abfc7bcc.svn-base | 2940 +- ...17b5b8b9d748ab6ad95d7c33fb472f9ab.svn-base | 6296 +-- ...ea83bcbb7de3066bd72fd0647f6d547e4.svn-base | 870 +- ...a176386802a2865c3d08b10bd7cff49d9.svn-base | 3149 ++ .svn/wc.db | Bin 278528 -> 278528 bytes Bpq32.c | 4 + CommonCode.c | 2 +- L2Code.c | 5 +- L3Code.c | 29 +- MHSave.txt | 0 TelnetV6.c | 127 +- Versions.h | 4 +- asmstrucs.h | 1 + bpqaxip.c | 47 +- cMain.c | 3 + cheaders.h | 5 +- config.c | 9 +- configstructs.h | 1 + 69 files changed, 176418 insertions(+), 140543 deletions(-) create mode 100644 .svn/pristine/04/04f3425d2266f8ab7e39728796f66092f2be0511.svn-base create mode 100644 .svn/pristine/21/21025538e866f3bfdc3f0e0165284537a082c940.svn-base create mode 100644 .svn/pristine/2c/2cf9dba43e92df43bad8b89ae05eebc20785616f.svn-base create mode 100644 .svn/pristine/33/335b5720584b9f0e052dc45954051f1ed529bd4e.svn-base create mode 100644 .svn/pristine/4a/4a5594e7fd023912bcc03705c2840b02ad1c28a0.svn-base create mode 100644 .svn/pristine/97/97ea175d010c0714be4be7546a03ef5f3a49a2ad.svn-base create mode 100644 .svn/pristine/b8/b885885c7f89cdc49114a5927759769be30c8ed3.svn-base create mode 100644 .svn/pristine/cb/cbc21d00733a02cf60084bbc7c91d0ce1d5db13c.svn-base create mode 100644 .svn/pristine/ce/cebc2c3d15f4b26153ff6a040c05402e17b135a0.svn-base create mode 100644 .svn/pristine/e1/e15058cbd66b6129ae2b185b6606c1a3bbb9c6e5.svn-base rename L3Code.c.bak => .svn/pristine/e4/e45be3cf22583c8c49000f0df6186ce56601cfdb.svn-base (94%) create mode 100644 .svn/pristine/fb/fb58941a176386802a2865c3d08b10bd7cff49d9.svn-base delete mode 100644 MHSave.txt diff --git a/.svn/pristine/04/04f3425d2266f8ab7e39728796f66092f2be0511.svn-base b/.svn/pristine/04/04f3425d2266f8ab7e39728796f66092f2be0511.svn-base new file mode 100644 index 0000000..8d243b1 --- /dev/null +++ b/.svn/pristine/04/04f3425d2266f8ab7e39728796f66092f2be0511.svn-base @@ -0,0 +1,3356 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide AXIP support for G8BPQ switch in a +// 32bit environment, +// +// Uses BPQ EXTERNAL interface +// + +// Version 1.1 August 2001 +// +// Send to all matching entries in map table +// (Mainly for NODES braodcasts to multiple stations) +// + +// Version 1.2 September 2001 +// +// Support UDP as well as raw IP + +// Version 1.3 October 2001 +// +// Allow host names as well as numeric IP addresses +// +// Version 1.4 November 2002 +// +// Implement keepalive for NAT routers +// + +// Version 1.5 December 2004 +// +// Implement a "MHEARD" facility +// + + +// Version 1.6 August 2005 +// +// Treat NULL string in Registry as use current directory + + +// Version 1.7 December 2005 +// +// Create a separate thread to open sockets to avoid hang on XP SP2 + + +// Version 1.8 January 2006 +// +// Get config file location from Node (will check bpq directory) + + +// Version 1.9 March 2006 +// +// Allow multiple listening UDP ports +// Kick off resolver on EXTRESTART +// Remove redundant DYNAMIC processing + +// Version 1.10 October 2006 +// +// Add "Minimize to Tray" option +// Write diagnostics to BPQ console window instead of STDOUT + +// Version 1.11 October 2007 +// +// Sort MHeard and discard last entry if full +// Add Commands to re-read config file and manually add an ARP entry + +// Version 1.12 February 2008 +// +// Check received length +// Changes for unload of bpq32.dll +// Add Close Driver function +// Dynamic Load of bpq32.dll + +// Version 1.13 October 2008 +// +// Add Linux-style config of broadcast addressess + +// Version 1.13.2 January 2009 +// +// Add Start Minimized Option + +// Version 1.13.3 February 2009 +// +// Save Window positions + +// Version 1.13.4 March 2009 +// +// Fix loop on config file error + +// Version 1.14.1 April 2009 +// +// Add option to reject messages if sender is not in ARP Table +// Add option to add received calls to ARP Table + +// Version 1.15.1 May 2009 +// +// Add IP/TCP option + +// Version 1.15.2 August 2009 +// +// Extra Debug Output in TCP Mode +// Fix problem if TCP entry was first in table +// Include TCP sessions in MHEARD +// Add T flag to Resolver window fot TCP Sessions +// Set SO_KEEPALIVE and SO_CONDITIONAL_ACCEPT socket options + +// Version 1.15.4 August 2009 + +// Recycle Listening Socket if no connect for 60 mins +// Clear data connections if no data for 60 mins +// Repaint MH Window after clear. + +// Version 1.15.5 Spetmber 2010 + +// Add option to get config from bpq32.dll +// Moved to BPQ32.dll - no separate version number onw. + +// October 2010 + +// Allow multiple axip ports. + +// June 2011 + +// Add IPv6 support + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#ifndef WIN32 +#include +#include +#include +#endif +#include "bpq32.h" + +#ifndef LINBPQ +#include "kernelresource.h" +#include +#endif + +#include + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +// Cater for only systems without IPV6_V6ONLY + +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 0x27 +#endif + +#ifndef MAXGETHOSTSTRUCT +#define MAXGETHOSTSTRUCT 1024 +#endif + + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +int ResolveDelay = 0; + + +extern BOOL StartMinimized; + +VOID * zalloc(int len); + +int ResetExtDriver(int num); +BOOL ProcessConfig(); +VOID FreeConfig(); + +extern UCHAR BPQDirectory[]; + + +extern int OffsetH, OffsetW; + +static void ResolveNames(struct AXIPPORTINFO * PORT); +void OpenSockets(struct AXIPPORTINFO * PORT); +void CloseSockets(struct AXIPPORTINFO * PORT); + + +static int CONVFROMAX25(char * incall, char * outcall); +void CreateMHWindow(struct AXIPPORTINFO * PORT); +int Update_MH_List(struct AXIPPORTINFO * PORT, UCHAR * ipad, char * call, char proto, short port, BOOL IPv6); +int Update_MH_KeepAlive(struct AXIPPORTINFO * PORT, struct in_addr ipad, char proto, short port); +unsigned short int compute_crc(unsigned char *buf,int l); +unsigned int find_arp(unsigned char * call); +BOOL add_arp_entry(struct AXIPPORTINFO * PORT, unsigned char * call, UCHAR * ip, int len, int port,unsigned char * name, + int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPMode, int SourcePort, BOOL IPv6, int noUpdate, int useSourcePort); +BOOL add_bc_entry(struct AXIPPORTINFO * PORT, unsigned char * call, int len); +BOOL convtoax25(unsigned char * callsign, unsigned char * ax25call, int * calllen); +static BOOL ReadConfigFile(int Port); +static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT); +int CheckKeepalives(struct AXIPPORTINFO * PORT); +BOOL CopyScreentoBuffer(char * buff, struct AXIPPORTINFO * PORT); +int DumpFrameInHex(unsigned char * msg, int len); +VOID SendFrame(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp_table, UCHAR * buff, int txlen); +BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int FromPort, VOID * rxaddr, int ToPort); +int DataSocket_Read(struct arp_table_entry * sockptr, SOCKET sock); +int GetMessageFromBuffer(struct AXIPPORTINFO * PORT, char * Buffer); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int KissDecode(UCHAR * inbuff, int len); +int Socket_Accept(int SocketId); +int Socket_Connect(int SocketId, int Error); +int Socket_Data(int sock, int error, int eventcode); +VOID TCPConnectThread(struct arp_table_entry * arp); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); +BOOL OpenListeningSocket(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp); +VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6); +static void CreateResolverWindow(struct AXIPPORTINFO * PORT); +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized); +VOID SaveAXIPCache(struct AXIPPORTINFO * PORT); +VOID GetAXIPCache(struct AXIPPORTINFO * PORT); + + +union +{ + struct sockaddr_in sinx; + struct sockaddr_in6 sinx6; +} sinx; +/* +union +{ + struct sockaddr_in destaddr; + struct sockaddr_in6 destaddr6; +} destaddr; +*/ + +#define IP_AXIP 93 // IP Protocol for AXIP + +#pragma pack(1) + +struct iphdr { +// unsigned int version:4; // Version of IP +// unsigned int h_len:4; // length of the header + unsigned char h_lenvers; // Version + length of the header + unsigned char tos; // Type of service + unsigned short total_len; // total length of the packet + unsigned short ident; // unique identifier + unsigned short frag_and_flags; // flags + unsigned char ttl; + unsigned char proto; // protocol (TCP, UDP etc) + unsigned short checksum; // IP checksum + + unsigned int sourceIP; + unsigned int destIP; + +}; + +#pragma pack() + + +#define TCPMaster 1 +#define TCPSlave 2 + +#define TCPListening 1 +#define TCPConnecting 2 +#define TCPConnected 4 + +#ifndef LINBPQ + +LOGFONT LFTTYFONT ; + +extern HFONT hFont ; + +RECT ResRect; +RECT MHRect; + +extern HKEY REGTREE; + +extern HWND ClientWnd, FrameWnd; +extern HMENU hMainFrameMenu, hBaseMenu, hWndMenu; +extern HBRUSH bgBrush; + +#endif + +//struct tagMSG Msg; + +//char buf[MAXGETHOSTSTRUCT]; + +int addrlen6 = sizeof(struct sockaddr_in6); +int addrlen = sizeof(struct sockaddr_in); + +extern unsigned short CRCTAB[]; +unsigned int AXIPInst = 0; + +char CantReplyList[512] = ""; // To suppress duplicate "Can't Reply" messages + +DWORD n; + +struct AXIPPORTINFO * Portlist[MaxBPQPortNo + 1]; + +int InitAXIP(int Port); + +int CurrentResEntries; + +static char ConfigClassName[]="CONFIG"; + +HANDLE hInstance; + +VOID SaveAXIPWindowPos(int port) +{ +#ifndef LINBPQ + struct AXIPPORTINFO * PORT; + char Key[80]; + + PORT = Portlist[port]; + + if (PORT == NULL) + return; + + sprintf(Key, "PACTOR\\PORT%d", port); + + SaveMDIWindowPos(PORT->hMHWnd, Key, "MHSize", PORT->MHMinimized); + SaveMDIWindowPos(PORT->hResWnd, Key, "ResSize", PORT->ResMinimized); +#endif + return; +} + + +static size_t ExtProc(int fn, int port, PMESSAGE buff) +{ + struct iphdr * iphdrptr; + int len,txlen=0,err,index,digiptr,i; + unsigned short int crc; + char rxbuff[5000]; + char axcall[7]; + char errmsg[100]; + union + { + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; + } RXaddr; + struct AXIPPORTINFO * PORT = Portlist[port]; + + switch (fn) + { + case 1: // poll + + // + // Check Keepalive timers + // + time(&PORT->ltime); + + if (PORT->ltime-PORT->lasttime >9 ) + { + PORT->lasttime=PORT->ltime; + CheckKeepalives(PORT); + } + + if (PORT->needip) + { + char call[7]; + + len = recvfrom(PORT->sock,rxbuff,500,0,(struct sockaddr *)&RXaddr.rxaddr,&addrlen); + + if (len == -1) + { + err = WSAGetLastError(); + } + else + { + iphdrptr=(struct iphdr *)&rxbuff; + + if (len == ntohs(iphdrptr->total_len)) + { + len-=20; // IP HEADER + + if (memcmp(&rxbuff[20], "Keepalive", 9) == 0 ) + { + if (PORT->MHEnabled) + Update_MH_KeepAlive(PORT, RXaddr.rxaddr.sin_addr,'I',93); + + return 0; + } + crc = compute_crc(&rxbuff[20], len); + + if (crc == 0xf0b8) // Good CRC + { + len-=2; // Remove CRC + + if (len > MAXDATA) + { + sprintf(errmsg,"BPQAXIP Invalid Msg Len=%d Source=%s",len,inet_ntoa(RXaddr.rxaddr.sin_addr)); + OutputDebugString(errmsg); + DumpFrameInHex(&rxbuff[20], len); + return 0; + } + + memcpy(&buff->DEST, &rxbuff[20],len); + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)buff, len); // Needed for arm5 portability + + memcpy(call, &buff->ORIGIN, 7); + call[6] &= 0x7e; // Mask End of Address bit + + // + // Do MH Proccessing if enabled + // + + if (PORT->MHEnabled) + Update_MH_List(PORT, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, &buff->ORIGIN[0], 'I', 93, 0); + + // Check Exclude + + if (CheckExcludeList(call) == 0) + return 0; + + + if (PORT->Checkifcanreply) + { + if (CheckSourceisResolvable(PORT, call, 0, &RXaddr, 0)) + + return 1; + + else + // Can't reply. If AutoConfig is set, add to table and accept, else reject + + if (PORT->AutoAddARP) + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, 0, inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, 0, FALSE, 0, 0); + else + { + char From[11] = "|"; + From[ConvFromAX25(call, &From[1]) + 1] = 0; + if (strstr(CantReplyList, From) == 0) + { + if (strlen(CantReplyList) < 500); + strcat(CantReplyList, From); + Debugprintf("AXIP Packet from %s dropped - can't reply", &From[1]); + } + return 0; + } + } + else + return(1); + } + // + // CRC Error + // + + sprintf(errmsg,"BPQAXIP Invalid CRC=%d Source=%s",crc,inet_ntoa(RXaddr.rxaddr.sin_addr)); + OutputDebugString(errmsg); + + return (0); + } + + // + // Bad Length + // + + return (0); + } + } + + for (i=0;iNumberofUDPPorts;i++) + { + char call[7]; + + if (PORT->IPv6[i]) + len = recvfrom(PORT->udpsock[i],rxbuff,500,0,(struct sockaddr *)&RXaddr.rxaddr, &addrlen6); + else + len = recvfrom(PORT->udpsock[i],rxbuff,500,0,(struct sockaddr *)&RXaddr.rxaddr, &addrlen); + + if (len == -1) + { + err = WSAGetLastError(); + } + else + { + if (memcmp(rxbuff, "Keepalive", 9) == 0 ) + { + if (PORT->MHEnabled) + Update_MH_KeepAlive(PORT, RXaddr.rxaddr.sin_addr, 'U', PORT->udpport[i]); + + continue; + } + + crc = compute_crc(&rxbuff[0], len); + + if (crc == 0xf0b8) // Good CRC + { + len-=2; // Remove CRC + + if (len > MAXDATA) + { + sprintf(errmsg,"BPQAXIP Invalid Msg Len=%d Source=%s Port %d",len,inet_ntoa(RXaddr.rxaddr.sin_addr),PORT->udpport[i]); + OutputDebugString(errmsg); + DumpFrameInHex(&rxbuff[0], len); + return 0; + } + + memcpy(&buff->DEST, &rxbuff[0], len); + + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)buff, len); + + memcpy(call, &buff->ORIGIN, 7); + + call[6] &= 0x7e; // Mask End of Address bit + + // + // Do MH Proccessing if enabled + // + + if (PORT->MHEnabled) + if (PORT->IPv6[i]) + Update_MH_List(PORT, (UCHAR *)&RXaddr.rxaddr6.sin6_addr, &buff->ORIGIN[0], 'U', PORT->udpport[i], TRUE); + else + Update_MH_List(PORT, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, &buff->ORIGIN[0], 'U', PORT->udpport[i], FALSE); + + // Check Exclude + + if (CheckExcludeList(call) == 0) + return 0; + + if (PORT->Checkifcanreply) + { + if (CheckSourceisResolvable(PORT, call, htons(RXaddr.rxaddr.sin_port), &RXaddr, PORT->udpport[i])) + return 1; + else + { + // Can't reply. If AutoConfig is set, add to table and accept, else reject + + if (PORT->AutoAddARP) + if (PORT->IPv6[i]) + { + char Addr[80]; + Format_Addr((UCHAR *)&RXaddr.rxaddr6.sin6_addr, Addr, TRUE); + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr6.sin6_addr, 7, htons(RXaddr.rxaddr6.sin6_port), Addr, 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], TRUE, 0, 0); + } + else + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, htons(RXaddr.rxaddr.sin_port), inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], FALSE, 0, 0); + else + { + char From[11] = "|"; + From[ConvFromAX25(call, &From[1]) + 1] = 0; + if (strstr(CantReplyList, From) == 0) + { + if (strlen(CantReplyList) < 500); + strcat(CantReplyList, From); + Debugprintf("AXIP Packet from %s dropped - can't reply", &From[1]); + } + return 0; + } + } + } + else + return(1); + } + + // + // CRC Error + // + + sprintf(errmsg,"BPQAXIP Invalid CRC=%d Source=%s Port %d",crc,inet_ntoa(RXaddr.rxaddr.sin_addr),PORT->udpport[i]); + Debugprintf(errmsg); + rxbuff[len] = 0; + Debugprintf(rxbuff); + + + return (0); + } + } + + if (PORT->NeedTCP) + { + len = GetMessageFromBuffer(PORT, rxbuff); + + if (len) + { + len = KissDecode(rxbuff, len-1); // Len includes FEND + len -= 2; // Ignore Checksum + + if (len < MAXDATA) + { + memcpy(&buff->DEST, &rxbuff[0], len); + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)buff, len); // fix big endian issue + return 1; + } + else + { + Debugprintf("Oversized AX/TCP frame %d", len); + return 0; + } + } + } + + return (0); + + case 2: // send + +// txlen=(buff[6]<<8) + buff[5] - 5; // Len includes buffer header (7) but we add crc + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - (MSGHDDRLEN - 2); // 2 for CRC + + crc=compute_crc(&buff->DEST[0], txlen - 2); + crc ^= 0xffff; + + buff->DEST[txlen - 2] = (crc & 0xff); + buff->DEST[txlen - 1] = (crc >> 8); + + memcpy(axcall, &buff->DEST, 7); // Set to send to dest addr + + // if digis are present, scan down list for first non-used call + + if (buff->ORIGIN[6] == 0) + { + // end of addr bit not set, so scan digis + + digiptr = 13; // start of first digi + + while (((buff->ORIGIN[digiptr] & 0x80) == 0x80) && ((buff->ORIGIN[digiptr] & 0x1) == 0)) + { + // This digi has been used, and it is not the last + + digiptr+=7; + } + + // if this has not been used, use it + + if ((buff->ORIGIN[digiptr] & 0x80) == 0) + memcpy(axcall,&buff->ORIGIN[digiptr - 6], 7); // get next call + } + + axcall[6] &= 0x7e; + +// If addresses to a broadcast address, send to all entries + + for (i=0; i< PORT->NumberofBroadcastAddreses; i++) + { + if (memcmp(axcall, PORT->BroadcastAddresses[i].callsign, 7) == 0) + { + for (index = 0; index < PORT->arp_table_len; index++) + { + if (PORT->arp_table[index].BCFlag) + SendFrame(PORT, &PORT->arp_table[index], &buff->DEST[0], txlen); + } + return 0; + } + } + +// Send to all matching calls in arp table + + index = 0; + + while (index < PORT->arp_table_len) + { + if (memcmp(PORT->arp_table[index].callsign,axcall,PORT->arp_table[index].len) == 0) + { + SendFrame(PORT, &PORT->arp_table[index],&buff->DEST[0], txlen); + } + index++; + } + return (0); + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + case 4: // reinit + + CloseSockets(PORT); + + if (ProcessConfig()) + { + FreeConfig(); + ReadConfigFile(port); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + _beginthread(OpenSockets, 0, PORT ); + + GetAXIPCache(PORT); + + ResolveDelay = 2; +#ifndef LINBPQ + InvalidateRect(PORT->hResWnd,NULL,TRUE); +#endif + break; + + case 5: // Terminate + + CloseSockets(PORT); +#ifndef LINBPQ + SendMessage(PORT->hMHWnd, WM_CLOSE, 0, 0); + SendMessage(PORT->hResWnd, WM_CLOSE, 0, 0); +#endif + break; + } + return (0); +} + +VOID SendFrame(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp_table, UCHAR * buff, int txlen) +{ + int i; + SOCKET txsock, SourceSocket; + + if (arp_table->TCPMode) + { + if (arp_table->TCPState == TCPConnected) + { + char outbuff[1000]; + int newlen; + + newlen = KissEncode(buff, outbuff, txlen); + send(arp_table->TCPSock, outbuff, newlen, 0); + } + + return; + } + + // Select source port by choosing right socket + + // First Set Default for Protocol + + for (i = 0; i < PORT->NumberofUDPPorts; i++) + { + if (PORT->IPv6[i] == arp_table->IPv6) + { + SourceSocket = PORT->udpsock[i]; // Use as source socket, therefore source port + break; + } + } + + for (i = 0; i < PORT->NumberofUDPPorts; i++) + { + if (PORT->udpport[i] == arp_table->SourcePort && PORT->IPv6[i] == arp_table->IPv6) + { + SourceSocket = PORT->udpsock[i]; // Use as source socket, therefore source port + break; + } + } + + if (arp_table->error == 0) + { + int sent = 0; + + if (arp_table->port == 0) txsock = PORT->sock; else txsock = SourceSocket; + + if (arp_table->IPv6) + sent = sendto(txsock, buff, txlen, 0, (struct sockaddr *)&arp_table->destaddr6, sizeof(arp_table->destaddr6)); + else + if (arp_table->destaddr.sin_addr.s_addr) + sent = sendto(txsock, buff, txlen, 0, (struct sockaddr *)&arp_table->destaddr, sizeof(arp_table->destaddr)); + + if (sent != txlen) + { + int i = GetLastError(); +// perror("Sendto"); + } + + // reset Keepalive Timer + + arp_table->keepalive=arp_table->keepaliveinit; + } +} + +unsigned short int compute_crc_ccitt(unsigned char *buf, int len); +unsigned short CCCITTChecksum(unsigned char* data, unsigned int length); + +VOID * AXIPExtInit(struct PORTCONTROL * PortEntry) +{ +// char Msg[10] = {0xD0, 01, 00, 0x11, 00, 0x0B}; +// unsigned short crc; + +// crc = CCCITTChecksum(Msg, 4); + +// crc = CalcCRC(Msg, 4); + + WritetoConsole("AXIP "); + + InitAXIP(PortEntry->PORTNUMBER); + + WritetoConsole("\n"); + + return ExtProc; +} + +int InitAXIP(int Port) +{ + struct AXIPPORTINFO * PORT; + + // + // Read config first, to get UDP info if needed + // + + if (!ReadConfigFile(Port)) + return (FALSE); + + PORT = Portlist[Port]; + + if (PORT == NULL) + return FALSE; + + PORT->Port = Port; + + GetAXIPCache(PORT); // Prime resolver from cache + + // + // Start Resolver Thread if needed + // + + if (PORT->NeedResolver) + { + CreateResolverWindow(PORT); + _beginthread(ResolveNames, 0, PORT ); + } + + time(&PORT->lasttime); // Get initial time value + + _beginthread(OpenSockets, 0, PORT ); + + // Start TCP outward connect threads + // + // Open MH window if needed + + if (PORT->MHEnabled) + CreateMHWindow(PORT); + + return (TRUE); +} + +void OpenSockets(struct AXIPPORTINFO * PORT) +{ + char Msg[255]; + int err; + u_long param=1; + BOOL bcopt=TRUE; + int i; + int index = 0; + struct arp_table_entry * arp; + + // Moved from InitAXIP, to avoid hang if started too early on XP SP2 + + // Create and bind socket + + if (PORT->needip) + { + PORT->sock=socket(AF_INET,SOCK_RAW,IP_AXIP); + + if (PORT->sock == INVALID_SOCKET) + { + err = WSAGetLastError(); + sprintf(Msg, "AXIP Failed to create RAW socket - Error %d\n", err); + WritetoConsole(Msg); + } + else + { + + ioctl (PORT->sock,FIONBIO,¶m); + + setsockopt (PORT->sock,SOL_SOCKET,SO_BROADCAST,(const char FAR *)&bcopt,4); + + sinx.sinx.sin_family = AF_INET; + sinx.sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sinx.sin_port = 0; + + if (bind(PORT->sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // + // Bind Failed + // + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for RAW socket - error code = %d", err); + WritetoConsole(Msg); + return; + } + } + } + + for (i=0;iNumberofUDPPorts;i++) + { + int ret; + + if (PORT->IPv6[i]) + PORT->udpsock[i]=socket(AF_INET6,SOCK_DGRAM,0); + else + PORT->udpsock[i]=socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->udpsock[i] == INVALID_SOCKET) + { + WritetoConsole("Failed to create UDP socket"); + err = WSAGetLastError(); + continue; + } + + ioctl (PORT->udpsock[i],FIONBIO,¶m); + + setsockopt (PORT->udpsock[i],SOL_SOCKET,SO_BROADCAST,(const char FAR *)&bcopt,4); + +#ifndef WIN32 + + if (PORT->IPv6[i]) + if (setsockopt(PORT->udpsock[i], IPPROTO_IPV6, IPV6_V6ONLY, ¶m, sizeof(param)) < 0) + perror("setting option IPV6_V6ONLY"); + +#endif + + if (PORT->IPv6[i]) + { + sinx.sinx.sin_family = AF_INET6; + memset (&sinx.sinx6.sin6_addr, 0, 16); + } + else + { + sinx.sinx.sin_family = AF_INET; + sinx.sinx.sin_addr.s_addr = INADDR_ANY; + } + + sinx.sinx.sin_port = htons(PORT->udpport[i]); + + if (PORT->IPv6[i]) + ret = bind(PORT->udpsock[i], (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx6)); + else + ret = bind(PORT->udpsock[i], (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx)); + + if (ret != 0) + { + // Bind Failed + + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP socket %d - error code = %d", PORT->udpport[i], err); + WritetoConsole(Msg); + continue; + } + } + + // Open any TCP sockets + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index++]; + + if (arp->TCPMode == TCPMaster) + { + arp->TCPBuffer=malloc(4000); + arp->TCPState = 0; + + if (arp->TCPThreadID == 0) + { + arp->TCPThreadID = _beginthread(TCPConnectThread, 0, arp); + Debugprintf("TCP Connect thread created for %s Handle %x", arp->hostname, arp->TCPThreadID); + } + continue; + } + + if (arp->TCPMode == TCPSlave) + { + OpenListeningSocket(PORT, arp); + } + } +} +int OpenListeningSocket(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp) +{ + char Msg[255]; + struct sockaddr_in * psin; + BOOL bOptVal = TRUE; + struct sockaddr_in local_sin; /* Local socket - internet style */ + u_long param = 1; + arp->TCPBuffer = malloc(4000); + arp->TCPState = 0; + + arp->TCPListenSock = socket(AF_INET, SOCK_STREAM, 0); + + ioctl (arp->TCPListenSock, FIONBIO, ¶m); + + if (arp->TCPListenSock == INVALID_SOCKET) + { + sprintf(Msg, "socket() failed error %d", WSAGetLastError()); + WritetoConsole(Msg); + return FALSE; + } + +// Debugprintf("TCP Listening Socket Created - socket %d port %d ", arp->TCPListenSock, arp->port); + + setsockopt (arp->TCPListenSock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = htonl(INADDR_ANY); // Local Host Only + + psin->sin_port = htons(arp->port); /* Convert to network ordering */ + + if (bind(arp->TCPListenSock , (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(Msg, "bind(sock) failed Port %d Error %d\n", arp->port, WSAGetLastError()); + Debugprintf(Msg); + closesocket(arp->TCPListenSock); + + return FALSE; + } + + if (listen(arp->TCPListenSock, 1) < 0) + { + sprintf(Msg, "listen(sock) failed Error %d", WSAGetLastError()); + Debugprintf(Msg); + closesocket(arp->TCPListenSock); + return FALSE; + } + + arp->TCPState = TCPListening; + return TRUE; +} + +void CloseSockets(struct AXIPPORTINFO * PORT) +{ + int i; + int index = 0; + struct arp_table_entry * arp; + + if (PORT->needip) + closesocket(PORT->sock); + + for (i=0;iNumberofUDPPorts;i++) + { + closesocket(PORT->udpsock[i]); + } + + // Close any open or listening TCP sockets + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index++]; + + if (arp->TCPMode == TCPMaster) + { + if (arp->TCPState) + { + closesocket(arp->TCPSock); + arp->TCPSock = 0; + } + continue; + } + + if (arp->TCPMode == TCPSlave) + { + if (arp->TCPState) + { + closesocket(arp->TCPSock); + arp->TCPSock = 0; + } + + closesocket(arp->TCPListenSock); + continue; + } + } + return ; +} + +#ifndef LINBPQ + +static LRESULT CALLBACK AXResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + char line[100]; + char outcall[10]; + int index,displayline; + struct AXIPPORTINFO * PORT; + MINMAXINFO * mmi; + int nScrollCode,nPos; + int i, Port; + char Flags[10]; + struct arp_table_entry * arp; + + // Find our PORT Entry + + for (Port = 1; Port < 33; Port++) + { + PORT = Portlist[Port]; + if (PORT == NULL) + continue; + + if (PORT->hResWnd == hWnd) + break; + } + + if (PORT == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + i=1; + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 600; + mmi->ptMaxSize.y = PORT->MaxResWindowlength; + mmi->ptMaxTrackSize.x = 55600; + mmi->ptMaxTrackSize.y = PORT->MaxResWindowlength; + + break; + + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT_PTR)PORT->hResMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + + DrawMenuBar(FrameWnd); + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + } + + case WM_CHAR: + + if (PORT->MHEnabled == FALSE && PORT->MHAvailable) + { + PORT->MHEnabled=TRUE; + CreateMHWindow(PORT); + ShowWindow(PORT->hMHWnd, SW_RESTORE); // In case Start Minimized set + } + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + + if (wmId == BPQREREAD) + { + CloseSockets(PORT); + + if (ProcessConfig()) + { + FreeConfig(); + ReadConfigFile(Port); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + _beginthread(OpenSockets, 0, PORT); + + ResolveDelay = 2; + InvalidateRect(hWnd,NULL,TRUE); + + return 0; + } + + if (wmId == BPQADDARP) + { + if (PORT->ConfigWnd == 0) + { + PORT->ConfigWnd=CreateDialog(hInstance, ConfigClassName, 0, NULL); + + if (!PORT->ConfigWnd) + { + return (FALSE); + } + ShowWindow(PORT->ConfigWnd, SW_SHOW); + UpdateWindow(PORT->ConfigWnd); + } + + SetForegroundWindow(PORT->ConfigWnd); + + return(0); + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_RESTORE: + + PORT->ResMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + + case SC_MINIMIZE: + + PORT->ResMinimized = TRUE; + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + PORT->baseline--; + if (PORT->baseline <0) + PORT->baseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + PORT->baseline++; + if (PORT->baseline > PORT->arp_table_len) + PORT->baseline = PORT->arp_table_len; + } + + if (nScrollCode == SB_THUMBTRACK) + { + PORT->baseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,PORT->baseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + index = PORT->baseline; + displayline=0; + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index]; + + Flags[0] = 0; + + if (arp->BCFlag) + strcat(Flags, "B "); + + if (arp->TCPState == TCPConnected) + strcat(Flags, "C "); + + if (arp->AutoAdded) + strcat(Flags, "A"); + + if (arp->ResolveFlag && arp->error != 0) + { + // resolver error - Display Error Code + sprintf(PORT->hostaddr,"Error %d",arp->error); + } + else + { + if (arp->IPv6) + Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, PORT->hostaddr, TRUE); + else + Format_Addr((unsigned char *)&arp->destaddr.sin_addr, PORT->hostaddr, FALSE); + } + + CONVFROMAX25(arp->callsign,outcall); + + if (arp->port == arp->SourcePort) + i=sprintf(line,"%.10s = %.64s %d = %-.30s %s ", + outcall, + arp->hostname, + arp->port, + PORT->hostaddr, + Flags); + else + i=sprintf(line,"%.10s = %.64s %d<%d = %-.30s %s ", + outcall, + arp->hostname, + arp->port, + arp->SourcePort, + PORT->hostaddr, + Flags); + + TextOut(hdc, 0, (displayline++)*14+2, line, i); + + index++; + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + +} + +LRESULT FAR PASCAL ConfigWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) +{ + int cmd,id,i; + HWND hwndChild; + BOOL OK1,OK2,OK3; + + char call[10], host[65]; + int Interval; + int calllen; + int port; + char axcall[7]; + BOOL UDPFlag, BCFlag; + struct AXIPPORTINFO * PORT; + int useSourcePort = 0; + + for (i=1; i <= MAXBPQPORTS; i++) + { + PORT = Portlist[i]; + if (PORT == NULL) + continue; + + if (PORT->ConfigWnd == hWnd) + break; + } + + switch (message) + { + case WM_CTLCOLORDLG: + + return (LRESULT)bgBrush; + + case WM_COMMAND: + + id = LOWORD(wParam); + hwndChild = (HWND)(UINT)lParam; + cmd = HIWORD(wParam); + + switch (id) + { + case ID_SAVE: + + OK1=GetDlgItemText(PORT->ConfigWnd,1001,(LPSTR)call,10); + OK2=GetDlgItemText(PORT->ConfigWnd,1002,(LPSTR)host,64); + OK3=1; + + for (i=0;i<7;i++) + call[i] = toupper(call[i]); + + UDPFlag=IsDlgButtonChecked(PORT->ConfigWnd,1004); + BCFlag=IsDlgButtonChecked(PORT->ConfigWnd,1005); + + if (UDPFlag) + port=GetDlgItemInt(PORT->ConfigWnd,1003,&OK3,FALSE); + else + port=0; + + Interval=0; + + if (OK1 && OK2 && OK3==1) + { + if (convtoax25(call,axcall,&calllen)) + { + add_arp_entry(PORT, axcall,0,calllen,port,host,Interval, BCFlag, FALSE, 0, port, FALSE, 0, useSourcePort); + ResolveDelay = 2; + return(DestroyWindow(hWnd)); + } + } + + // Validation failed + + if (!OK1) SetDlgItemText(PORT->ConfigWnd,1001,"????"); + if (!OK2) SetDlgItemText(PORT->ConfigWnd,1002,"????"); + if (!OK3) SetDlgItemText(PORT->ConfigWnd,1003,"????"); + + break; + + case ID_CANCEL: + + return(DestroyWindow(hWnd)); + } + break; + +// case WM_CLOSE: + +// return(DestroyWindow(hWnd)); + + case WM_DESTROY: + + PORT->ConfigWnd=0; + + return(0); + + } + + return (DefWindowProc(hWnd, message, wParam, lParam)); + +} +#endif + +static void CreateResolverWindow(struct AXIPPORTINFO * PORT) +{ +#ifndef LINBPQ + + int WindowParam; + WNDCLASS wc; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + + HWND hResWnd; + char Key[80]; + RECT Rect = {0, 0, 300, 300}; + int Top, Left, Width, Height; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", PORT->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"ResSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &PORT->ResMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + Width = Rect.right - Left; + Height = Rect.bottom - Top; + + // Register the window classes + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)AXResWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "AXAppName"; + + RegisterClass(&wc); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpfnWndProc = ConfigWndProc; + wc.lpszClassName = ConfigClassName; + RegisterClass(&wc); + + WindowParam = WS_OVERLAPPEDWINDOW | WS_VSCROLL; + + sprintf(WindowTitle,"AXIP Port %d Resolver", PORT->Port); + + PORT->hResWnd = hResWnd = CreateMDIWindow("AXAppName", WindowTitle, WindowParam, + Left - (OffsetW /2), Top - OffsetH + 4, Width, Height, ClientWnd, hInstance, 1234); + + + PORT->hResMenu = CreatePopupMenu(); + AppendMenu(PORT->hResMenu, MF_STRING, BPQREREAD, "ReRead Config"); + AppendMenu(PORT->hResMenu, MF_STRING, BPQADDARP, "Add Entry"); + + SetScrollRange(hResWnd,SB_VERT, 0, PORT->arp_table_len, TRUE); + + if (PORT->ResMinimized) + ShowWindow(hResWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hResWnd, SW_RESTORE); +#endif +} +extern HWND hWndPopup; + + +static void ResolveNames(struct AXIPPORTINFO * PORT) +{ + int count = 0; + + PORT->ResolveNamesThreadId = GetCurrentThreadId(); // Detect if another started + + while(TRUE) + { + count++; // So we can trap first few loops + + ResolveDelay = 15 * 60; + + for (PORT->ResolveIndex=0; PORT->ResolveIndex < PORT->arp_table_len; PORT->ResolveIndex++) + { + struct arp_table_entry * arp = &PORT->arp_table[PORT->ResolveIndex]; + + if (arp->ResolveFlag) + { + struct addrinfo hints, *res = 0; + int n; + BOOL UseV6 = FALSE; + BOOL ForceV4 = FALSE; + + if (_memicmp(arp->hostname, "ipv6:", 5) == 0) + UseV6 = TRUE; + else if (_memicmp(arp->hostname, "ipv4:", 5) == 0) + ForceV4 = TRUE; + + memset(&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_DGRAM; + + if (UseV6) + { + hints.ai_family = AF_INET6; // use IPv6 + n = getaddrinfo(&arp->hostname[5], NULL, &hints, &res); + } + else if (ForceV4) + { + hints.ai_family = AF_INET; // use IPv4 + n = getaddrinfo(&arp->hostname[5], NULL, &hints, &res); + } + else if (PORT->PortIPv6) // Can use IPv6 + { + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + n = getaddrinfo(arp->hostname, NULL, &hints, &res); + } + else + { + hints.ai_family = AF_INET; // use IPv4 only + n = getaddrinfo(arp->hostname, NULL, &hints, &res); + } + if (res) + { + arp->error = 0; + if (res->ai_family == AF_INET) + { + memcpy(&arp->destaddr.sin_addr.s_addr, &res->ai_addr->sa_data[2], 4); + arp->IPv6 = FALSE; + arp->destaddr.sin_family = AF_INET; +// Debugprintf("AXIP %s = %d.%d.%d.%d", arp->hostname, (UCHAR)res->ai_addr->sa_data[2], +// (UCHAR)res->ai_addr->sa_data[3], (UCHAR)res->ai_addr->sa_data[4], (UCHAR)res->ai_addr->sa_data[5]); + + } + else + { + struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *)res->ai_addr; + + memcpy(&arp->destaddr6.sin6_addr, &sa6->sin6_addr, 16); + arp->IPv6 = TRUE; + arp->destaddr.sin_family = AF_INET6; + } + arp->destaddr.sin_port = htons(arp->port); + freeaddrinfo(res); + } + else + { + PORT->arp_table[PORT->ResolveIndex].error = WSAGetLastError(); + + if (count < 4) + ResolveDelay = 30; // if errors try again soon + } + } + } + + SaveAXIPCache(PORT); + +#ifndef LINBPQ + InvalidateRect(PORT->hResWnd,NULL,TRUE); +#endif + while(ResolveDelay-- > 0) + { + if (pthread_equal(PORT->ResolveNamesThreadId, GetCurrentThreadId()) == FALSE) + { + Debugprintf("AXIP Resolve thread %x redundant - closing", GetCurrentThreadId()); + return; + } + Sleep(1000); + } + } + Debugprintf("AXIP Resolve thread exitied"); +} + +#ifndef LINBPQ + +LRESULT CALLBACK MHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + char line[100]; + char outcall[10]; + HGLOBAL hMem; + struct AXIPPORTINFO * PORT; + int index,displayline; + MINMAXINFO * mmi; + int nScrollCode,nPos; + + + int i; + + for (i=1; i <= MAXBPQPORTS; i++) + { + PORT = Portlist[i]; + if (PORT == NULL) + continue; + + if (PORT->hMHWnd == hWnd) + break; + } + + if (PORT == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 600; + mmi->ptMaxSize.y = PORT->MaxMHWindowlength; + mmi->ptMaxTrackSize.x = 600; + mmi->ptMaxTrackSize.y = PORT->MaxMHWindowlength; + break; + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)PORT->hMHMenu, "Edit"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case BPQCLEAR: + memset(PORT->MHTable, 0, sizeof(PORT->MHTable)); + InvalidateRect(hWnd,NULL,TRUE); + return 0; + + case BPQCOPY: + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, MaxMHEntries * 100); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { + CopyScreentoBuffer(GlobalLock(hMem), PORT); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + } + return 0; + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_RESTORE: + + PORT->MHMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + + case SC_MINIMIZE: + + PORT->MHMinimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + PORT->mhbaseline--; + if (PORT->mhbaseline <0) + PORT->mhbaseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + PORT->mhbaseline++; + if (PORT->mhbaseline > PORT->CurrentMHEntries) + PORT->mhbaseline = PORT->CurrentMHEntries; + } + + if (nScrollCode == SB_THUMBTRACK) + { + PORT->mhbaseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,PORT->mhbaseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + hOldFont = SelectObject( hdc, hFont) ; + + index = PORT->mhbaseline; + displayline=0; + + PORT->CurrentMHEntries = 0; + + while (index < MaxMHEntries) + { + if (PORT->MHTable[index].proto != 0) + { + char Addr[80]; + + Format_Addr((unsigned char *)&PORT->MHTable[index].ipaddr6, Addr, PORT->MHTable[index].IPv6); + + CONVFROMAX25(PORT->MHTable[index].callsign,outcall); + + i=sprintf(line,"%-10s%-15s %c %-6d %-25s%c",outcall, + Addr, + PORT->MHTable[index].proto, + PORT->MHTable[index].port, + asctime(gmtime( &PORT->MHTable[index].LastHeard )), + (PORT->MHTable[index].Keepalive == 0) ? ' ' : 'K'); + + line[i-2]= ' '; // Clear CR returned by asctime + + TextOut(hdc,0,(displayline++)*14+2,line,i); + PORT->CurrentMHEntries ++; + } + index++; + } + + if (PORT->MaxMHWindowlength < PORT->CurrentMHEntries * 14 + 40) + PORT->MaxMHWindowlength = PORT->CurrentMHEntries * 14 + 40; + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + + PORT->MHEnabled=FALSE; + + break; + + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); +} + +#endif + +BOOL CopyScreentoBuffer(char * buff, struct AXIPPORTINFO * PORT) +{ + int index; + char outcall[10]; + + index = 0; + + while (index < MaxMHEntries) + { + if (PORT->MHTable[index].proto != 0) + { + CONVFROMAX25(PORT->MHTable[index].callsign,outcall); + + buff+=sprintf(buff,"%-10s%-15s %c %-6d %-26s",outcall, + inet_ntoa(PORT->MHTable[index].ipaddr), + PORT->MHTable[index].proto, + PORT->MHTable[index].port, + asctime(gmtime( &PORT->MHTable[index].LastHeard ))); + } + *(buff-2)=13; + *(buff-1)=10; + index++; + + } + *(buff)=0; + + return 0; +} + +void CreateMHWindow(struct AXIPPORTINFO * PORT) +{ +#ifndef LINBPQ + + WNDCLASS wc; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + HWND hMHWnd; + char Key[80]; + RECT Rect = {0, 0, 300, 300}; + int Top, Left, Width, Height; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", PORT->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"MHSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &PORT->MHMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + Width = Rect.right - Left; + Height = Rect.bottom - Top; + + PORT->MaxMHWindowlength = Height; + + wc.style = CS_HREDRAW | CS_VREDRAW ;//| CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)MHWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "MHAppName"; + + RegisterClass(&wc); + + sprintf(WindowTitle,"AXIP Port %d MHEARD", PORT->Port); + + PORT->hMHWnd = hMHWnd = CreateMDIWindow("MHAppName", WindowTitle, + WS_OVERLAPPEDWINDOW | WS_VSCROLL, + Left - (OffsetW /2), Top - OffsetH, Width, Height, ClientWnd, hInstance, 1234); + + PORT->hMHMenu = CreatePopupMenu(); + AppendMenu(PORT->hMHMenu, MF_STRING, BPQCOPY, "Copy"); + AppendMenu(PORT->hMHMenu, MF_STRING, BPQCLEAR, "Clear"); + + if (PORT->MHMinimized) + ShowWindow(hMHWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hMHWnd, SW_RESTORE); +#endif +} + +unsigned short int compute_crc(unsigned char *buf,int len) +{ + unsigned short fcs = 0xffff; + int i; + + for(i = 0; i < len; i++) + fcs = (fcs >>8 ) ^ CRCTAB[(fcs ^ buf[i]) & 0xff]; + + return fcs; +} + +/* + +static const unsigned short ccittTab[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; + +unsigned short int compute_crc_ccitt(unsigned char *buf, int len) +{ + int i; + unsigned short fcs = 0; + + for(i = 0; i < len; i++) + fcs = (fcs >>8 ) ^ ccittTab[(fcs ^ buf[i]) & 0xff]; + + return fcs; +} + + + union { + unsigned short m_crc16; + unsigned char m_crc8[2U]; + } fcs; + + +unsigned short CCCITTChecksum(unsigned char* data, unsigned int length) +{ + int i; + + fcs.m_crc16 = 0; + + for (i = 0U; i < length; i++) + fcs.m_crc16 = (fcs.m_crc8[0U] << 8) ^ ccittTab[fcs.m_crc8[1U] ^ data[i]]; + + return fcs.m_crc16; +} + +*/ + +static BOOL ReadConfigFile(int Port) +{ + +/* Linux Format + +broadcast QST-0 NODES-0 +# +# ax.25 route definition, define as many as you need. +# format is route (call/wildcard) (ip host at destination) +# ssid of 0 routes all ssid's +# +# route [flags] +# +# Valid flags are: +# b - allow broadcasts to be transmitted via this route +# d - this route is the default route +# +#route vk2sut-0 44.136.8.68 b +#route vk5xxx 44.136.188.221 b +#route vk2abc 44.1.1.1 +# +*/ + +//UDP 9999 # Port we listen on +//MAP G8BPQ-7 10.2.77.1 # IP 93 for compatibility +//MAP BPQ7 10.2.77.1 UDP 2222 # UDP port to send to +//MAP BPQ8 10.2.77.2 UDP 3333 # UDP port to send to + + char buf[256],errbuf[256]; + HKEY hKey=0; + char * Config; + struct AXIPPORTINFO * PORT; + + Config = PortConfig[Port]; + + if (Portlist[Port]) // Already defined, so must be re-read + { + PORT = Portlist[Port]; + + PORT->NumberofBroadcastAddreses = 0; + PORT->needip = FALSE; + PORT->NeedTCP = FALSE; + PORT->MHAvailable = FALSE; + PORT->MHEnabled = FALSE; + PORT->NumberofUDPPorts = 0; + PORT->NeedResolver = FALSE; + PORT->arp_table_len = 0; + memset(PORT->arp_table, 0, sizeof(struct arp_table_entry) * MAX_ENTRIES); + PORT->AutoAddARP = FALSE; + PORT->AutoAddBC = FALSE; + } + else + { + Portlist[Port] = PORT = zalloc(sizeof (struct AXIPPORTINFO)); + } + + PORT->Checkifcanreply = TRUE; + + if (Config) + { + char * ptr1 = Config, * ptr2; + + // Using config from bpq32.cfg + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf, PORT)) + { + WritetoConsole("BPQAXIP - Bad config record"); + WritetoConsole(errbuf); + WritetoConsole("\n"); + } + } + + if (PORT->NumberofUDPPorts > MAXUDPPORTS) + { + n=sprintf(buf,"BPQAXIP - Too many UDP= lines - max is %d\n", MAXUDPPORTS); + WritetoConsole(buf); + } + return TRUE; + } + + WritetoConsole("No Configuration info in bpq32.cfg"); + + return FALSE; +} + +static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT) +{ + char * ptr; + char * p_call; + char * p_ipad; + char * p_UDP; + char * p_udpport; + char * p_Interval; + + int calllen; + int port, SourcePort; + int bcflag; + char axcall[7]; + int Interval; + int noUpdate=FALSE; + int TCPMode; + int useSourcePort = 0; + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"UDP") == 0) + { + if (PORT->NumberofUDPPorts > MAXUDPPORTS) PORT->NumberofUDPPorts--; + + p_udpport = strtok(NULL, " ,\t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + PORT->udpport[PORT->NumberofUDPPorts] = atoi(p_udpport); + + if (PORT->udpport[PORT->NumberofUDPPorts] == 0) return (FALSE); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr && _stricmp(ptr, "ipv6") == 0) + { + PORT->PortIPv6 = TRUE; + PORT->IPv6[PORT->NumberofUDPPorts] = TRUE; + } + + PORT->NumberofUDPPorts++; + + return (TRUE); + } + + if(_stricmp(ptr,"MHEARD") == 0) + { + PORT->MHEnabled = TRUE; + PORT->MHAvailable = TRUE; + + return (TRUE); + } + + if(_stricmp(ptr,"DONTCHECKSOURCECALL") == 0) + { + PORT->Checkifcanreply = FALSE; + return (TRUE); + } + + if(_stricmp(ptr,"AUTOADDMAP") == 0) + { + PORT->AutoAddARP = TRUE; + PORT->AutoAddBC = TRUE; + return (TRUE); + } + + if(_stricmp(ptr,"AUTOADDQUIET") == 0) + { + PORT->AutoAddARP = TRUE; + PORT->AutoAddBC = FALSE; + return (TRUE); + } + + if(_stricmp(ptr,"MAP") == 0) + { + p_call = strtok(NULL, " \t\n\r"); + + if (p_call == NULL) return (FALSE); + + _strupr(p_call); + + if (_stricmp(p_call, "DUMMY") == 0) + { + Consoleprintf("MAP DUMMY is no longer needed - statement ignored"); + return TRUE; + } + + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_UDP = strtok(NULL, " \t\n\r"); + + Interval=0; + port=0; // Raw IP + bcflag=0; + TCPMode=0; + SourcePort = 0; + useSourcePort = 0; + +// +// Look for (optional) KEEPALIVE, DYNAMIC, UDP or BROADCAST params +// + while (p_UDP != NULL) + { + if (_stricmp(p_UDP,"NOUPDATE") == 0) + { + noUpdate = TRUE; + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"KEEPALIVE") == 0) + { + p_Interval = strtok(NULL, " \t\n\r"); + + if (p_Interval == NULL) return (FALSE); + + Interval = atoi(p_Interval); + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"UDP") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + if (_stricmp(p_udpport,"FROMPORT") == 0) + { + useSourcePort = TRUE; + port = 0; + } + else + port = atoi(p_udpport); + + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"SOURCEPORT") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + SourcePort = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"TCP-Master") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + port = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + + TCPMode=TCPMaster; + + continue; + } + + if (_stricmp(p_UDP,"TCP-Slave") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + port = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + + TCPMode = TCPSlave; + continue; + + } + + + if (_stricmp(p_UDP,"B") == 0) + { + bcflag =TRUE; + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if ((*p_UDP == ';') || (*p_UDP == '#')) break; // Comment on end + + return FALSE; + + } + + if (convtoax25(p_call,axcall,&calllen)) + { + if (SourcePort == 0) + SourcePort = port; + + add_arp_entry(PORT, axcall, 0, calllen, port, p_ipad, Interval, bcflag, FALSE, TCPMode, SourcePort, FALSE, noUpdate, useSourcePort); + return (TRUE); + } + } // End of Process MAP + + if(_stricmp(ptr,"BROADCAST") == 0) + { + p_call = strtok(NULL, " \t\n\r"); + + if (p_call == NULL) return (FALSE); + + if (convtoax25(p_call,axcall,&calllen)) + { + add_bc_entry(PORT, axcall,calllen); + return (TRUE); + } + + + return (FALSE); // Failed convtoax25 + } + + // + // Bad line + // + return (FALSE); +} + +int CONVFROMAX25(char * incall, char * outcall) +{ + int in,out=0; + unsigned char chr; +// +// CONVERT AX25 FORMAT CALL IN incall TO NORMAL FORMAT IN out +// RETURNS LENGTH +// + memset(outcall,0x20,9); + outcall[9]=0; + + for (in=0;in<6;in++) + { + chr=incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++]=chr; + } + + chr=incall[6]; // ssid + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++]='-'; + if (chr > 9) + { + chr-=10; + outcall[out++]='1'; + } + chr+=48; + outcall[out++]=chr; + } + return (out); +} + + +BOOL convtoax25(unsigned char * callsign, unsigned char * ax25call,int * calllen) +{ + int i; + + memset(ax25call,0x40,6); // in case short + ax25call[6]=0x60; // default SSID + + for (i=0;i<7;i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i+1]); + + if (i < 16) + { + ax25call[6] |= i<<1; + *calllen = 7; // include ssid in test + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == ' ') + { + // + // End of call - no ssid + // + *calllen = 6; // wildcard ssid + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + +BOOL add_arp_entry(struct AXIPPORTINFO * PORT, UCHAR * call, UCHAR * ip, int len, int port, + UCHAR * name, int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPFlag, int SourcePort, BOOL IPv6, int noUpdate, int useSourcePort) +{ + struct arp_table_entry * arp; + + if (PORT->arp_table_len == MAX_ENTRIES) + // + // Table full + // + return (FALSE); + + arp = &PORT->arp_table[PORT->arp_table_len]; + + if (SourcePort) + arp->SourcePort = SourcePort; + else + arp->SourcePort = port; + + arp->PORT = PORT; + + if (port == 0 && arp->replytoSourcePort == 0) + PORT->needip = 1; // Enable Raw IP Mode + + arp->ResolveFlag=TRUE; + PORT->NeedResolver=TRUE; + + memcpy (&arp->callsign,call,7); + strncpy((char *)&arp->hostname,name,64); + arp->len = len; + arp->port = port; + keepalive+=9; + keepalive/=10; + + arp->keepalive = keepalive; + arp->keepaliveinit = keepalive; + arp->BCFlag = BCFlag; + arp->AutoAdded = AutoAdded; + arp->TCPMode = TCPFlag; + arp->noUpdate = noUpdate; + PORT->arp_table_len++; + arp->replytoSourcePort = useSourcePort; + + if (PORT->MaxResWindowlength < (PORT->arp_table_len * 14) + 70) + PORT->MaxResWindowlength = (PORT->arp_table_len * 14) + 70; + + PORT->NeedResolver |= TCPFlag; // Need Resolver window to handle tcp socket messages + PORT->NeedTCP |= TCPFlag; + + if (ip) + { + // Only have an IP address if dynamically added - so update destaddr + + if (IPv6) + { + memcpy(&arp->destaddr6.sin6_addr, ip, 16); + arp->IPv6 = TRUE; + arp->destaddr.sin_family = AF_INET6; + } + else + { + memcpy(&arp->destaddr.sin_addr.s_addr, ip, 4); + arp->IPv6 = FALSE; + arp->destaddr.sin_family = AF_INET; + } + arp->destaddr.sin_port = htons(arp->port); +#ifndef LINBPQ + + SetScrollRange(PORT->hResWnd,SB_VERT, 0, PORT->arp_table_len, TRUE); + InvalidateRect(PORT->hResWnd, NULL, TRUE); +#endif + } + + return (TRUE); +} + +BOOL add_bc_entry(struct AXIPPORTINFO * PORT, unsigned char * call, int len) +{ + if (PORT->NumberofBroadcastAddreses == MAX_BROADCASTS) + // + // Table full + // + return (FALSE); + + memcpy (PORT->BroadcastAddresses[PORT->NumberofBroadcastAddreses].callsign,call,7); + PORT->BroadcastAddresses[PORT->NumberofBroadcastAddreses].len = len; + PORT->NumberofBroadcastAddreses++; + + return (TRUE); +} + + +int CheckKeepalives(struct AXIPPORTINFO * PORT) +{ + int index=0; + SOCKET txsock; + struct arp_table_entry * arp; + + if (PORT->arp_table_len >= MAX_ENTRIES) + { + Debugprintf("arp_table_len corrupt - %d", PORT->arp_table_len); + PORT->arp_table_len = MAX_ENTRIES - 1; + } + + while (index < PORT->arp_table_len) + { + if (PORT->arp_table[index].keepalive != 0) + { + arp = &PORT->arp_table[index]; + arp->keepalive--; + + if (arp->keepalive == 0) + { + // + // Send Keepalive Packet + // + arp->keepalive=arp->keepaliveinit; + + if (arp->error == 0) + { + if (arp->port == 0) txsock = PORT->sock; else txsock = PORT->udpsock[0]; + + sendto(txsock,"Keepalive",9,0,(struct sockaddr *)&arp->destaddr,sizeof(arp->destaddr)); + } + } + } + + index++; + + } + + // Decrement MH Keepalive flags + + for (index = 0; index < MaxMHEntries; index++) + { + if (PORT->MHTable[index].Keepalive != 0) + PORT->MHTable[index].Keepalive--; + } + + return (0); +} + +BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int FromPort, VOID * rxaddr, int ToPort) +{ + // Makes sure we can reply to call before accepting message + + int index = 0; + struct arp_table_entry * arp; + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index]; + + if (memcmp(arp->callsign, call, arp->len) == 0) + { + // Call is present - if AutoAdded, refresh IP address and Port + + // Why not refreesh resolved addresses - if dynamic addr has changed + // this will give quicker response + + if (arp->noUpdate == 0) + { + if (arp->IPv6) + { + struct sockaddr_in6 * SA6 = rxaddr; + memcpy(&arp->destaddr6.sin6_addr, &SA6->sin6_addr, 16); + } + else + { + struct sockaddr_in * SA = rxaddr; + memcpy(&arp->destaddr.sin_addr.s_addr, &SA->sin_addr, 4); + } + // Dont think I should update port unless using source port for dest + + if (arp->replytoSourcePort) + { + arp->port = FromPort; + if (arp->SourcePort == 0) + arp->SourcePort = ToPort; + } + } + arp->LastHeard = time(NULL); + return 1; // Ok to process + } + index++; + } + + return (0); // Not in list +} + +int Update_MH_List(struct AXIPPORTINFO * PORT, UCHAR * ipad, char * call, char proto, short port, BOOL IPv6) +{ + int index; + char callsign[7]; + int SaveKeepalive=0; + struct MHTableEntry * MH; + + memcpy(callsign,call,7); + callsign[6] &= 0x3e; // Mask non-ssid bits + + for (index = 0; index < MaxMHEntries; index++) + { + MH = &PORT->MHTable[index]; + + if (MH->callsign[0] == 0) + { + // empty entry, so call not present. Move all down, and add to front + +#ifdef WIN32 + SetScrollRange(PORT->hMHWnd, SB_VERT, 0, index + 1, TRUE); +#endif + goto MoveEntries; + } + + if (memcmp(MH->callsign,callsign,7) == 0 && + memcmp(&MH->ipaddr, ipad, (MH->IPv6) ? 16 : 4) == 0 && + MH->proto == proto && + MH->port == port) + { + // Entry found, move preceeding entries down and put on front + + SaveKeepalive = MH->Keepalive; + goto MoveEntries; + } + } + + // Table full move MaxMHEntries-1 entries down, and add on front + + index=MaxMHEntries-1; + +MoveEntries: + + // + // Move all preceeding entries down one, and put on front + // + + if (index > 0) + memmove(&PORT->MHTable[1],&PORT->MHTable[0],index*sizeof(struct MHTableEntry)); + + MH = &PORT->MHTable[0]; + + memcpy(MH->callsign,callsign,7); + memcpy(&MH->ipaddr6, ipad, (IPv6) ? 16 : 4); + MH->proto = proto; + + MH->port = port; + time(&MH->LastHeard); + MH->Keepalive = SaveKeepalive; + MH->IPv6 = IPv6; +#ifndef LINBPQ + InvalidateRect(PORT->hMHWnd,NULL,TRUE); +#endif + return 0; + +} + +int Update_MH_KeepAlive(struct AXIPPORTINFO * PORT, struct in_addr ipad, char proto, short port) +{ + int index; + + for (index = 0; index < MaxMHEntries; index++) + { + if (PORT->MHTable[index].callsign[0] == 0) + + // empty entry, so call not present. + + return 0; + + if (memcmp(&PORT->MHTable[index].ipaddr,&ipad,4) == 0 && + PORT->MHTable[index].proto == proto && + PORT->MHTable[index].port == port) + { + PORT->MHTable[index].Keepalive = 30; // 5 Minutes at 10 sec ticks + return 0; + } + } + + return 0; + +} + + +int DumpFrameInHex(unsigned char * msg, int len) +{ + char errmsg[100]; + int i=0; + + for (i=0;iarp_table_len) + { + sockptr = &PORT->arp_table[index++]; + + if (sockptr->TCPMode) + { + if (sockptr->TCPState == TCPListening) + { + int addrlen; + SOCKET sock; + BOOL bOptVal = TRUE; + struct sockaddr sin; + + addrlen = sizeof(struct sockaddr); + + sock = accept(sockptr->TCPListenSock, &sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + continue; + + if (err == 10038 || err == 9) + { + // Not a socket + + closesocket(sockptr->TCPListenSock); + OpenListeningSocket(PORT, sockptr); + + continue; + } + + + Debugprintf("AXIP accept() failed Error %d", err); + continue; + } + + Debugprintf("AXIP Connect accepted - Socket %d Port %d", sock, sockptr->port); + + if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&bOptVal, 4) != SOCKET_ERROR) + Debugprintf("Set SO_KEEPALIVE: ON"); + + sockptr->TCPSock = sock; + sockptr->TCPState = TCPConnected; + } + + if (sockptr->TCPState == TCPConnected) + { + int InputLen; + + // Poll TCP Connection for data + + // May have several messages per packet, or message split over packets + + if (sockptr->InputLen > 3000) // Shouldnt have lines longer than this in text mode + { + sockptr->InputLen = 0; + } + + ioctl(sockptr->TCPSock, FIONBIO, ¶m); + + InputLen = recv(sockptr->TCPSock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); + + if (InputLen == 0) + { + Debugprintf("TCP Close received for socket %d", sockptr->TCPSock); + + if (sockptr->TCPMode == TCPSlave) + sockptr->TCPState = TCPListening; + else + sockptr->TCPState = 0; + closesocket(sockptr->TCPSock); + continue; + } + + if (InputLen < 0) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + InputLen = 0; + else + { + if (sockptr->TCPMode == TCPSlave) + sockptr->TCPState = TCPListening; + else + sockptr->TCPState = 0; + + closesocket(sockptr->TCPSock); + continue; + } + } + + sockptr->InputLen += InputLen; + + if (sockptr->InputLen == 0) + { + sockptr->TCPOK++; + + if (sockptr->TCPOK > 36000) // 60 MINS + { + if (sockptr->TCPSock) + { + Debugprintf("No Data for 60 Mins on Data Sock %d State %d", + sockptr->TCPListenSock, sockptr->TCPSock, sockptr->TCPState); + + sockptr->TCPState = 0; + closesocket(sockptr->TCPSock); + sockptr->TCPSock = 0; + } + + closesocket(sockptr->TCPListenSock); + OpenListeningSocket(PORT, sockptr); + + sockptr->TCPOK = 0; + } + continue; + } + } + + ptr = memchr(sockptr->TCPBuffer, FEND, sockptr->InputLen); + + if (ptr) // FEND in buffer + { + ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; + ptr++; + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + MsgLen = sockptr->InputLen; + sockptr->InputLen = 0; + + if (MsgLen > 1) + { + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + if (PORT->MHEnabled) + Update_MH_List(PORT, (UCHAR *)&sockptr->destaddr.sin_addr.s_addr, &Buffer[7],'T', sockptr->port, 0); + + sockptr->TCPOK = 0; + + return MsgLen; + } + } + else + { + // buffer contains more that 1 message + + MsgLen = sockptr->InputLen - (int)((ptr2-ptr)); + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); + + sockptr->InputLen -= MsgLen; + + if (MsgLen > 1) + { + if (PORT->MHEnabled) + Update_MH_List(PORT, (UCHAR *)&sockptr->destaddr.sin_addr.s_addr, &Buffer[7],'T', sockptr->port, 0); + + sockptr->TCPOK = 0; + + return MsgLen; + } + } + } + } + } + return 0; + +} + +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + outbuff[0]=FEND; + txptr=1; + + for (i=0;iTCPMode == TCPMaster) + { + if (arp->TCPState == 0) + { + arp->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (arp->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for AX/TCP socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + goto wait; + } + + setsockopt (arp->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(arp->TCPSock, SOL_SOCKET, SO_KEEPALIVE, (char*)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(arp->TCPSock, (struct sockaddr *) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for AX/TCP socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + goto wait; + } + + arp->TCPState = TCPConnecting; + + if (connect(arp->TCPSock,(struct sockaddr *) &arp->destaddr, sizeof(arp->destaddr)) == 0) + { + // + // Connected successful + // + + arp->TCPState = TCPConnected; + OutputDebugString("AXTCP Connected\r\n"); + ioctl (arp->TCPSock, FIONBIO, ¶m); + Alerted = 0; + } + else + { + err=WSAGetLastError(); + + // Connect failed + // + + if (Alerted == 0) + { + i = sprintf(Msg, "Connect Failed for AX/TCP port %d - error code = %d\n", htons(arp->destaddr.sin_port), err); + WritetoConsole(Msg); + OutputDebugString(Msg); + Alerted = 1; + } + closesocket(arp->TCPSock); + arp->TCPSock = 0; + arp->TCPState = 0; + } + } +wait: + Sleep (115000); // 2 Mins + } + + Debugprintf("AX/TCP Connect Thread %x Closing", arp->TCPThreadID); + + arp->TCPThreadID = 0; + + return; // Not Used + +} + +VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6) +{ + unsigned char * src; + char zeros[12] = ""; + char * ptr; + struct + { + int base, len; + } best, cur; + unsigned int words[8]; + int i; + + if (IPV6 == FALSE) + { + sprintf((char *)Output, "%d.%d.%d.%d", Addr[0], Addr[1], Addr[2], Addr[3]); + return; + } + + src = Addr; + + // See if Encapsulated IPV4 addr + + if (src[12] != 0) + { + if (memcmp(src, zeros, 12) == 0) // 12 zeros, followed by non-zero + { + sprintf((char *)Output, "::%d.%d.%d.%d", src[12], src[13], src[14], src[15]); + return; + } + } + + // COnvert 16 bytes to 8 words + + for (i = 0; i < 16; i += 2) + words[i / 2] = (src[i] << 8) | src[i + 1]; + + // Look for longest run of zeros + + best.base = -1; + cur.base = -1; + + for (i = 0; i < 8; i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; // New run, save start + else + cur.len++; // Continuation - increment length + } + else + { + // End of a run of zeros + + if (cur.base != -1) + { + // See if this run is longer + + if (best.base == -1 || cur.len > best.len) + best = cur; + + cur.base = -1; // Start again + } + } + } + + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + + if (best.base != -1 && best.len < 2) + best.base = -1; + + ptr = Output; + + for (i = 0; i < 8; i++) + { + /* Are we inside the best run of 0x00's? */ + + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) + { + // Just output one : for whole string of zeros + + *ptr++ = ':'; + i = best.base + best.len - 1; // skip rest of zeros + continue; + } + + /* Are we following an initial run of 0x00s or any real hex? */ + + if (i != 0) + *ptr++ = ':'; + + ptr += sprintf (ptr, "%x", words[i]); + + // Was it a trailing run of 0x00's? + } + + if (best.base != -1 && (best.base + best.len) == 8) + *ptr++ = ':'; + + *ptr++ = '\0'; +} + + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + + +VOID SaveAXIPCache(struct AXIPPORTINFO * PORT) +{ + config_setting_t *root, *group; + config_t cfg; + char ConfigName[256]; + int index=0; + struct arp_table_entry * arp; + unsigned char hostaddr[64]; + char Key[128]; + + if (BPQDirectory[0] == 0) + { + sprintf(ConfigName,"axipcache%d.cfg", PORT->Port); + } + else + { + sprintf(ConfigName,"%s/axipcache%d.cfg", BPQDirectory, PORT->Port); + } + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + if (PORT->arp_table_len >= MAX_ENTRIES) + { + Debugprintf("arp_table_len corrupt - %d", PORT->arp_table_len); + PORT->arp_table_len = MAX_ENTRIES - 1; + } + + while (index < PORT->arp_table_len) + { + char * ptr = Key; + + arp = &PORT->arp_table[index++]; + + if (arp->IPv6) + Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, hostaddr, TRUE); + else + Format_Addr((unsigned char *)&arp->destaddr.sin_addr, hostaddr, FALSE); + + sprintf(Key, "*%s", arp->hostname); + + // libconfig keys can't contain . so replace with * + + while (*ptr) + { + if (*ptr == '.') *ptr = '*'; + ptr++; + } + + SaveStringValue(group, Key, hostaddr); + } + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + +#ifndef LINBPQ + +static BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen) +{ + char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = (char *)config_setting_get_string (setting); + + if (strlen(str) > maxlen) + { + Debugprintf("Suspect config record %s", str); + str[maxlen] = 0; + } + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +#endif + +VOID GetAXIPCache(struct AXIPPORTINFO * PORT) +{ + config_setting_t *group; + config_t cfg; + char ConfigName[256]; + int index=0; + struct arp_table_entry * arp; + unsigned char hostaddr[64]; + char Key[128]; + struct stat STAT; + + if (BPQDirectory[0] == 0) + { + sprintf(ConfigName,"axipcache%d.cfg", PORT->Port); + } + else + { + sprintf(ConfigName,"%s/axipcache%d.cfg", BPQDirectory, PORT->Port); + } + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if (stat(ConfigName, &STAT) == -1) + return; + + if(!config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "AXIP Cache read error line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group == NULL) + { + config_destroy(&cfg); + return; + } + + while (index < PORT->arp_table_len) + { + char * ptr = Key; + + arp = &PORT->arp_table[index++]; + + sprintf(Key, "*%s", arp->hostname); + + // libconfig keys can't contain . so replace with * + + while (*ptr) + { + if (*ptr == '.') *ptr = '*'; + ptr++; + } + + if (GetStringValue(group, Key, hostaddr, 64)) + { + arp->destaddr.sin_addr.s_addr = inet_addr(hostaddr); + } + } + + config_destroy(&cfg); +} + diff --git a/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base b/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base index caa0cc8..6755ea9 100644 --- a/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base +++ b/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base @@ -1,6764 +1,6764 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ -// -// 409l Oct 2001 Fix l3timeout for KISS -// -// 409m Oct 2001 Fix Crossband Digi -// -// 409n May 2002 Change error handling on load ext DLL - -// 409p March 2005 Allow Multidigit COM Ports (kiss.c) - -// 409r August 2005 Treat NULL string in Registry as use current directory -// Allow shutdown to close BPQ Applications - -// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 - -// 409t January 2006 -// -// Add API for Perl "GetPerlMsg" -// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine -// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" -// Add New API "GetBPQDirectory" - Returns location of config file -// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command -// Rename BPQNODES to BPQNODES.dat -// New API "GetAttachedProcesses" - returns number of processes connected. -// Warn if user trys to close Console Window. -// Add Debug entries to record Process Attach/Detach -// Fix recovery following closure of first process - -// 409t Beta 2 February 2006 -// -// Add API Entry "GetPortNumber" -// -// 409u February 2006 -// -// Fix crash if allocate/deallocate called with stream=0 -// Add API to ch -// Display config file path -// Fix saving of Locked Node flag -// Added SAVENODES SYSOP command -// -// 409u 2 March 2006 -// -// Fix SetupBPQDirectory -// Add CopyBPQDirectory (for Basic Programs) -// -// 409u 3 March 2006 -// -// Release streams on DLL unload - -// 409v October 2006 -// -// Support Minimize to Tray for all BPQ progams -// Implement L4 application callsigns - -// 410 November 2006 -// -// Modified to compile with C++ 2005 Express Edition -// Make MCOM MTX MMASK local variables -// -// 410a January 2007 -// -// Add program name to Attach-Detach messages -// Attempt to detect processes which have died -// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt -// Add BCALL - origin call for Beacons -// Fix KISS ACKMODE ACK processing -// - -// 410b November 2007 -// -// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary - -// 410c December 2007 - -// Fix problem with NT introduced in V410a -// Display location of DLL on Console - -// 410d January 2008 - -// Fix crash in DLL Init caused by long path to program -// Invoke Appl2 alias on C command (if enabled) -// Allow C command to be disabled -// Remove debug trap in GETRAWFRAME -// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem -// Move Port statup code out of DLLInit (mainly for perl) -// Changes to allow Load/Unload of bpq32.dll by appl -// CloseBPQ32 API added -// Ext Driver Close routes called -// Changes to release Mutex - -// 410e May 2008 - -// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) -// Fix VCOM Driver (RX Len was 1 byte too long) -// Fix possible crash on L4CODE if L4DACK received out of sequence -// Add basic IP decoding - -// 410f October 2008 - -// Add IP Gateway -// Add Multiport DIGI capability -// Add GetPortDescription API -// Fix potential hangs if RNR lost -// Fix problem if External driver failes to load -// Put pushad/popad round _INITIALISEPORTS (main.asm) -// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) -// Ensure Route Qual is updated if Port Qual changed -// Add Reload Option, plus menu items for DUMP and SAVENODES - -// 410g December 2008 - -// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) -// Fix changed init of BPQDirectory (accidentally changed) -// Fix Checks for lost processes (accidentally deleted) -// Support HDLC Cards on W2K and above -// Delete Tray List entries for crashed processes -// Add Option to NODES command to sort by Callsign -// Add options to save or clear BPQNODES before Reconfig. -// Fix Reconfig in Win98 -// Monitor buffering tweaks -// Fix Init for large (>64k) tables -// Fix Nodes count in Stats - -// 410h January 2009 - -// Add Start Minimized Option -// Changes to KISS for WIn98 Virtual COM -// Open \\.\com instead of //./COM -// Extra Dignostics - -// 410i Febuary 2009 - -// Revert KISS Changes -// Save Window positions - -// 410j June 2009 - -// Fix tidying of window List when program crashed -// Add Max Nodes to Stats -// Don't update APPLnALIAS with received NODES info -// Fix MH display in other timezones -// Fix Possible crash when processing NETROM type Zero frames (eg NRR) -// Basic INP3 Stuff -// Add extra diagnostics to Lost Process detection -// Process Netrom Record Route frames. - -// 410k June 2009 - -// Fix calculation of %retries in extended ROUTES display -// Fix corruption of ROUTES table - -// 410l October 2009 - -// Add GetVersionString API call. -// Add GetPortTableEntry API call -// Keep links to neighbouring nodes open - -// Build 2 - -// Fix PE in NOROUTETODEST (missing POP EBX) - -// 410m November 2009 - -// Changes for PACTOR and WINMOR to support the ATTACH command -// Enable INP3 if configured on a route. -// Fix count of nodes in Stats Display -// Overwrite the worst quality unused route if a call is received from a node not in your -// table when the table is full - -// Build 5 - -// Rig Control Interface -// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) - -// Build 6 - -// Fix reading INP3 Flag from BPQNODES - -// Build 7 - -// Add MAXHOPS and MAXRTT config options - -// Build 8 - -// Fix INP3 deletion of Application Nodes. -// Fix GETCALLSIGN for Pactor Sessions -// Add N Call* to display all SSID's of a call -// Fix flow control on Pactor sessions. - -// Build 9 - -// HDLC Support for XP -// Add AUTH routines - -// Build 10 - -// Fix handling commands split over more that one packet. - -// Build 11 - -// Attach cmd changes for winmor disconnecting state -// Option Interlock Winmor/Pactor ports - -// Build 12 - -// Add APPLS export for winmor -// Handle commands ending CR LF - -// Build 13 - -// Incorporate Rig Control in Kernel - -// Build 14 - -// Fix config reload for Rig COntrol - -// 410n March 2010 - -// Implement C P via PACTOR/WINMOR (for Airmail) - -// Build 2 - -// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR -// Fix resetting IDLE Timer on Pactor/WINMOR sessions -// Send L4 KEEPLI messages based on IDLETIME - -// 410o July 2010 - -// Read bpqcfg.txt instead of .bin -// Support 32 bit MMASK (Allowing 32 Ports) -// Support 32 bit _APPLMASK (Allowing 32 Applications) -// Allow more commands -// Allow longer command aliases -// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR -// Clear RIGControl RTS and DTR on close - -// 410o Build 2 August 2010 - -// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) -// Fix Kenwood Rig Control when more than one message received at once. -// Save minimzed state of Rigcontrol Window - -// 410o Build 3 August 2010 - -// Fix reporting of set errors in scan to a random session - -// 410o Build 4 August 2010 - -// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK -// Fix validation of TRANSDELAY - -// 410o Build 5 August 2010 - -// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) -// Add WINMOR and SCS Pactor mode control option to RigControl -// Extend INFOMSG to 2000 bytes -// Improve Scan freq change lock (check both SCS and WINMOR Ports) - -// 410o Build 6 September 2010 - -// Incorporate IPGateway in main code. -// Fix GetSessionInfo for Pactor/Winmor Ports -// Add Antenna Selection to RigControl -// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) - -// 410o Build 7 September 2010 - -// Move rigconrtol display to driver windows -// Move rigcontrol config to driver config. -// Allow driver and IPGateway config info in bpq32.cfg -// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll -// Add option to reread IP Gateway config. -// Fix Reinit after process with timer closes (error in TellSessions). - -// 410p Build 2 October 2010 - -// Move KAM and SCS drivers to bpq32.dll - -// 410p Build 3 October 2010 - -// Support more than one axip port. - -// 410p Build 4 October 2010 - -// Dynamically load psapi.dll (for 98/ME) - -// 410p Build 5 October 2010 - -// Incorporate TelnetServer -// Fix AXIP ReRead Config -// Report AXIP accept() fails to syslog, not a popup. - -// 410p Build 6 October 2010 - -// Includes HAL support -// Changes to Pactor Drivers disconnect code -// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param -// Config now checks for duplicate port definitions -// Add Node Map reporting -// Fix WINMOR deferred disconnect. -// Report Pactor PORTCALL to WL2K instead of RMS Applcall - -// 410p Build 7 October 2010 - -// Add In/Out flag to Map reporting, and report centre, not dial -// Write Telnet log to BPQ Directory -// Add Port to AXIP resolver display -// Send Reports to update.g8bpq.net:81 -// Add support for FT100 to Rigcontrol -// Add timeout to Rigcontrol PTT -// Add Save Registry Command - -// 410p Build 8 November 2010 - -// Add NOKEEPALIVES Port Param -// Renumbered for release - -// 410p Build 9 November 2010 - -// Get Bandwith for map report from WL2K Report Command -// Fix freq display for FT100 (was KHz, not MHz) -// Don't try to change SCS mode whilst initialising -// Allow reporting of Lat/Lon as well as Locator -// Fix Telnet Log Name -// Fix starting with Minimized windows when Minimizetotray isn't set -// Extra Program Error trapping in SessionControl -// Fix reporting same freq with different bandwidths at different times. -// Code changes to support SCS Robust Packet Mode. -// Add FT2000 to Rigcontrol -// Only Send CTEXT to connects to Node (not to connects to an Application Call) - -// Released as Build 10 - -// 410p Build 11 January 2011 - -// Fix MH Update for SCS Outgoing Calls -// Add Direct CMS Access to TelnetServer -// Restructure DISCONNECT processing to run in Timer owning process - -// 410p Build 12 January 2011 - -// Add option for Hardware PTT to use a different com port from the scan port -// Add CAT PTT for Yaesu 897 (and maybe others) -// Fix RMS Packet ports busy after restart -// Fix CMS Telnet with MAXSESSIONS > 10 - -// 410p Build 13 January 2011 - -// Fix loss of buffers in TelnetServer -// Add CMS logging. -// Add non - Promiscuous mode option for BPQETHER - -// 410p Build 14 January 2011 - -// Add support for BPQTermTCP -// Allow more that one FBBPORT -// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs -// Add session length to CMS Telnet logging. -// Return Secure Session Flag from GetConnectionInfo -// Show Uptime as dd/hh/mm - -// 4.10.16.17 March 2011 - -// Add "Close all programs" command -// Add BPQ Program Directory registry key -// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) -// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries -// Add support for SCS Tracker HF Modes -// Fix WL2K Reporting -// Report Version to WL2K -// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) - - -// Above released as 5.0.0.1 - -// 5.2.0.1 - -// Add caching of CMS Server IP addresses -// Initialise TNC State on Pactor Dialogs -// Add Shortened (6 digit) AUTH mode. -// Update MH with all frames (not just I/UI) -// Add IPV6 Support for TelnetServer and AXIP -// Fix TNC OK Test for Tracker -// Fix crash in CMS mode if terminal disconnects while tcp commect in progress -// Add WL2K reporting for Robust Packet -// Add option to suppress WL2K reporting for specific frequencies -// Fix Timeband processing for Rig Control -// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access -// New Driver for V4 TNC - -// 5.2.1.3 October 2011 - -// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) -// Improved program error logging -// WL2K reporting changed to new format agreed with Lee Inman - -// 5.2.3.1 January 2012 - -// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. -// Fix reporting of Tracker freqs to WL2K. -// Fix Tracker monitoring setup (sending M UISC) -// Fix possible call/application routing error on RP -// Changes for P4Dragon -// Include APRS Digi/IGate -// Tracker monitoring now includes DIGIS -// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers -// Include driver for UZ7HO soundcard modem. -// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal -// No longer supports separate config files, or BPQTELNETSERVER.exe -// Improved flow control for Telnet CMS Sessions -// Fix handling Config file without a newline after last line -// Add non - Promiscuous mode option for BPQETHER -// Change Console Window to a Dialog Box. -// Fix possible corruption and loss of buffers in Tracker drivers -// Add Beacon After Session option to Tracker and UZ7HO Drivers -// Rewrite RigControl and add "Reread Config Command" -// Support User Mode VCOM Driver for VKISS ports - -// 5.2.4.1 January 2012 - -// Remove CR from Telnet User and Password Prompts -// Add Rigcontrol to UZ7HO driver -// Fix corruption of Free Buffer Count by Rigcontol -// Fix WINMOR and V4 PTT -// Add MultiPSK Driver -// Add SendBeacon export for BPQAPRS -// Add SendChatReport function -// Fix check on length of Port Config ID String with trailing spaces -// Fix interlock when Port Number <> Port Slot -// Add NETROMCALL for L3 Activity -// Add support for APRS Application -// Fix Telnet with FBBPORT and no TCPPORT -// Add Reread APRS Config -// Fix switching to Pactor after scanning in normal packet mode (PTC) - -// 5.2.5.1 February 2012 - -// Stop reading Password file. -// Add extra MPSK commands -// Fix MPSK Transparency -// Make LOCATOR command compulsory -// Add MobileBeaconInterval APRS param -// Send Course and Speed when APRS is using GPS -// Fix Robust Packet reporting in PTC driver -// Fix corruption of some MIC-E APRS packets - -// 5.2.6.1 February 2012 - -// Convert to MDI presentation of BPQ32.dll windows -// Send APRS Status packets -// Send QUIT not EXIT in PTC Init -// Implement new WL2K reporting format and include traffic reporting info in CMS signon -// New WL2KREPORT format -// Prevent loops when APPL alias refers to itself -// Add RigControl for Flex radios and ICOM IC-M710 Marine radio - -// 5.2.7.1 - -// Fix opening more thn one console window on Win98 -// Change method of configuring multiple timelots on WL2K reporting -// Add option to update WK2K Sysop Database -// Add Web server -// Add UIONLY port option - -// 5.2.7.2 - -// Fix handling TelnetServer packets over 500 bytes in normal mode - -// 5.2.7.3 - -// Fix Igate handling packets from UIView - -// 5.2.7.4 - -// Prototype Baycom driver. - -// 5.2.7.5 - -// Set WK2K group ref to MARS (3) if using a MARS service code - -// 5.2.7.7 - -// Check for programs calling CloseBPQ32 when holding semaphore -// Try/Except round Status Timer Processing - -// 5.2.7.8 - -// More Try/Except round Timer Processing - -// 5.2.7.9 - -// Enable RX in Baycom, and remove test loopback in tx - -// 5.2.7.10 - -// Try/Except round ProcessHTTPMessage - -// 5.2.7.11 - -// BAYCOM tweaks - -// 5.2.7.13 - -// Release semaphore after program error in Timer Processing -// Check fro valid dest in REFRESHROUTE - - -// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( - -// Version 5.2.9.1 Sept 2012 - -// Fix using KISS ports with COMn > 16 -// Add "KISS over UDP" driver for PI as a TNC concentrator - -// Version 6.0.1.1 - -// Convert to C for linux portability -// Try to speed up kiss polling - -// Version 6.0.2.1 - -// Fix operation on Win98 -// Fix callsign error with AGWtoBPQ -// Fix PTT problem with WINMOR -// Fix Reread telnet config -// Add Secure CMS signon -// Fix error in cashing addresses of CMS servers -// Fix Port Number when using Send Raw. -// Fix PE in KISS driver if invalid subchannel received -// Fix Orignal address of beacons -// Speed up Telnet port monitoring. -// Add TNC Emulators -// Add CountFramesQueuedOnStream API -// Limit number of frames that can be queued on a session. -// Add XDIGI feature -// Add Winmor Robust Mode switching for compatibility with new Winmor TNC -// Move most APRS code from BPQAPRS to here -// Stop corruption caused by overlong KISS frames - -// Version 6.0.3.1 - -// Add starting/killing WINMOR TNC on remote host -// Fix Program Error when APRS Item or Object name is same as call of reporting station -// Dont digi a frame that we have already digi'ed -// Add ChangeSessionIdleTime API -// Add WK2KSYSOP Command -// Add IDLETIME Command -// Fix Errors in RELAYAPPL processing -// Fix PE cauaed by invalid Rigcontrol Line - -// Version 6.0.4.1 - -// Add frequency dependent autoconnect appls for SCS Pactor -// Fix DED Monitoring of I and UI with no data -// Include AGWPE Emulator (from AGWtoBPQ) -// accept DEL (Hex 7F) as backspace in Telnet -// Fix re-running resolver on re-read AXIP config -// Speed up processing, mainly for Telnet Sessions -// Fix APRS init on restart of bpq32.exe -// Change to 2 stop bits -// Fix scrolling of WINMOR trace window -// Fix Crash when ueing DED TNC Emulator -// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions -// Allow HOST applications even when CMS option is disabled -// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) - -// Version 6.0.5.1 January 2014 - -// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) -// Add "Clear" option to MH command -// Add "Connect to RMS Relay" Option -// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control -// Fix routing of first call in Robust Packet -// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) -// Add RTS>CAT PTT option for Sound Card rigs -// Add Clear Nodes Option (NODE DEL ALL) -// SCS Pactor can set differeant APPLCALLS when scanning. -// Fix possible Scan hangup after a manual requency change with SCS Pactor -// Accept Scan entry of W0 to disable WINMOR on that frequency -// Fix corruption of NETROMCALL by SIMPLE config command -// Enforce Pactor Levels -// Add Telnet outward connect -// Add Relay/Trimode Emulation -// Fix V4 Driver -// Add PTT Mux -// Add Locked ARP Entries (via bpq32.cfg) -// Fix IDLETIME node command -// Fix STAY param on connect -// Add STAY option to Attach and Application Commands -// Fix crash on copying a large AXIP MH Window -// Fix possible crash when bpq32.exe dies -// Fix DIGIPORT for UI frames - -// Version 6.0.6.1 April 2014 - -// FLDigi Interface -// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. -// Validate INP3 messages to try to prevent crash -// Fix possible crash if an overlarge KISS frame is received -// Fix error in AXR command -// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line -// Add CBELL to TNC21 emulator -// Add sent objects and third party messages to APRS Dup List -// Incorporate UIUtil -// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 -// Improvements to FLDIGI interlocking -// Fix TNC State Display for Tracker -// Cache CMS Addresses on LinBPQ -// Fix count error on DED Driver when handling 256 byte packets -// Add basic SNMP interface for MRTG -// Fix memory loss from getaddrinfo -// Process "BUSY" response from Tracker -// Handle serial port writes that don't accept all the data -// Trap Error 10038 and try to reopen socket -// Fix crash if overlong command line received - -// Version 6.0.7.1 Aptil 2014 -// Fix RigContol with no frequencies for Kenwood and Yaesu -// Add busy check to FLDIGI connects - -// Version 6.0.8.1 August 2014 - -// Use HKEY_CURRENT_USER on all OS versions -// Fix crash when APRS symbol is a space. -// Fixes for FT847 CAT -// Fix display of 3rd byte of FRMR -// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver -// Fix possible memory corruption in WINMOR driver -// Fix FT2000 Modes -// Use new WL2K reporting system (Web API Based) -// APRS Server now cycles through hosts if DNS returns more than one -// BPQ32 can now start and stop FLDIGI -// Fix loss of AXIP Resolver when running more than one AXIP port - -// Version 6.0.9.1 November 2014 - -// Fix setting NOKEEPALIVE flag on route created from incoming L3 message -// Ignore NODES from locked route with quality 0 -// Fix seting source port in AXIP -// Fix Dual Stack (IPV4/V6) on Linux. -// Fix RELAYSOCK if IPv6 is enabled. -// Add support for FT1000 -// Fix hang when APRS Messaging packet received on RF -// Attempt to normalize Node qualies when stations use widely differing Route qualities -// Add NODES VIA command to display nodes reachable via a specified neighbour -// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) -// Fix buffering large messages in Telnet Host API -// Fix occasional crash in terminal part line processing -// Add "NoFallback" command to Telnet server to disable "fallback to Relay" -// Improved support for APPLCALL scanning with Pactor -// MAXBUFFS config statement is no longer needed. -// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails -// Implement LISTEN and CQ commands -// FLDIGI driver can now start FLDIGI on a remote system. -// Add IGNOREUNLOCKEDROUTES parameter -// Fix error if too many Telnet server connections - -// Version 6.0.10.1 Feb 2015 - -// Fix crash if corrupt HTML request received. -// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. -// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins -// Add INP3ONLY flag. -// Fix program error if enter UNPROTO without a destination path -// Show client IP address on HTTP sessions in Telnet Server -// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set -// Add FT990 RigControl support, fix FT1000MP support. -// Support ARMV5 processors -// Changes to support LinBPQ APRS Client -// Add IC7410 to supported Soundcard rigs -// Add CAT PTT to NMEA type (for ICOM Marine Radios_ -// Fix ACKMODE -// Add KISS over TCP -// Support ACKMode on VKISS -// Improved reporting of configuration file format errors -// Experimental driver to support ARQ sessions using UI frames - -// Version 6.0.11.1 September 2015 - -// Fixes for IPGateway configuration and Virtual Circuit Mode -// Separate Portmapper from IPGateway -// Add PING Command -// Add ARDOP Driver -// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) -// Add "VeryOldMode" for KAM Version 5.02 -// Add KISS over TCP Slave Mode. -// Support Pactor and Packet on P4Dragon on one port -// Add "Remote Staton Quality" to Web ROUTES display -// Add Virtual Host option for IPGateway NET44 Encap -// Add NAT for local hosts to IPGateway -// Fix setting filter from RADIO command for IC7410 -// Add Memory Channel Scanning for ICOM Radios -// Try to reopen Rig Control port if it fails (could be unplugged USB) -// Fix restoring position of Monitor Window -// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) -// Support APRS beacons in RP mode on Dragon// -// Change Virtual MAC address on IPGateway to include last octet of IP Address -// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode -// Fix sending I frames before L2 session is up -// Fix Flow control on Telnet outbound sessions. -// Fix reporting of unterminatred comments in config -// Add option for RigControl to not change mode on FT100/FT990/FT1000 -// Add "Attach and Connect" for Telnet ports - -// Version 6.0.12.1 November 2015 - -// Fix logging of IP addresses for connects to FBBPORT -// Allow lower case user and passwords in Telnet "Attach and Connect" -// Fix possible hang in KISS over TCP Slave mode -// Fix duplicating LinBPQ process if running ARDOP fails -// Allow lower case command aliases and increase alias length to 48 -// Fix saving long IP frames pending ARP resolution -// Fix dropping last entry from a RIP44 message. -// Fix displaying Digis in MH list -// Add port name to Monitor config screen port list -// Fix APRS command display filter and add port filter -// Support port names in BPQTermTCP Monitor config -// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog -// Buffer Web Mgmt Edit Config output -// Add WebMail Support -// Fix not closing APRS Send WX file. -// Add RUN option to APRS Config to start APRS Client -// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 -// Close and reopen ARDOP connection if nothing received for 90 secs -// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) -// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode - -// 6.0.13.1 - -// Allow /ex to exit UNPROTO mode -// Support ARQBW commands. -// Support IC735 -// Fix sending ARDOP beacons after a busy holdoff -// Enable BPQDED driver to beacon via non-ax.25 ports. -// Fix channel number in UZ7HO monitoring -// Add SATGate mode to APRSIS Code. -// Fix crash caused by overlong user name in telnet logon -// Add option to log L4 connects -// Add AUTOADDQuiet mode to AXIP. -// Add EXCLUDE processing -// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux -// Convert calls in MAP entries to upper case. -// Support Linux COM Port names for APRS GPS -// Fix using NETROM serial protocol on ASYNC Port -// Fix setting MYLEVEL by scanner after manual level change. -// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic -// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command -// Add Multicast RX interface to FLDIGI Driver -// Fix processing application aliases to a connect command. -// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it -// Save backups of bpq32.cfg when editing with Web interface and report old and new length -// Add DD command to SCS Pactor, and use it for forced disconnect. -// Add ARDOP mode select to scan config -// ARDOP changes for ARDOP V 0.5+ -// Flip SSID bits on UZ7HO downlink connects - - -// Version 6.0.14.1 - -// Fix Socket leak in ARDOP and FLDIGI drivers. -// Add option to change CMS Server hostname -// ARDOP Changes for 0.8.0+ -// Discard Terminal Keepalive message (two nulls) in ARDOP command hander -// Allow parameters to be passed to ARDOP TNC when starting it -// Fix Web update of Beacon params -// Retry connects to KISS ports after failure -// Add support for ARDOP Serial Interface Native mode. -// Fix gating APRS-IS Messages to RF -// Fix Beacons when PORTNUM used -// Make sure old monitor flag is cleared for TermTCP sessions -// Add CI-V antenna control for IC746 -// Don't allow ARDOP beacons when connected -// Add support for ARDOP Serial over I2C -// Fix possble crash when using manual RADIO messages -// Save out of sequence L2 frames for possible reuse after retry -// Add KISS command to send KISS control frame to TNC -// Stop removing unused digis from packets sent to APRS-IS - -// Processing of ARDOP PING and PINGACK responses -// Handle changed encoding of WL2K update responses. -// Allow anonymous logon to telnet -// Don't use APPL= for RP Calls in Dragon Single mode. -// Add basic messaging page to APRS Web Server -// Add debug log option to SCSTracker and TrkMulti Driver -// Support REBOOT command on LinBPQ -// Allow LISTEN command on all ports that support ax.25 monitoring - -// Version 6.0.15.1 Feb 2018 - -// partial support for ax.25 V2.2 -// Add MHU and MHL commands and MH filter option -// Fix scan interlock with ARDOP -// Add Input source seiect for IC7300 -// Remove % transparency from web terminal signon message -// Fix L4 Connects In count on stats -// Fix crash caused by corrupt CMSInfo.txt -// Add Input peaks display to ARDOP status window -// Add options to show time in local and distances in KM on APRS Web pages -// Add VARA support -// Fix WINMOR Busy left set when port Suspended -// Add ARDOP-Packet Support -// Add Antenna Switching for TS 480 -// Fix possible crash in Web Terminal -// Support different Code Pages on Console sessions -// Use new Winlink API interface (api.winlink.org) -// Support USB/ACC switching on TS590SG -// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. -// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. -// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs -// Fix timing out ARDOP PENDING Lock -// Support mixed case WINLINK Passwords -// Add TUNE and POWER Rigcontol Commands for some radios -// ADD LOCALTIME and DISPKM options to APRS Digi/Igate - -// 6.0.16.1 March 2018 - -// Fix Setting data mode and filter for IC7300 radios -// Add VARA to WL2KREPORT -// Add trace to SCS Tracker status window -// Fix possible hang in IPGATEWAY -// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. -// Fix sending CTEXT on WINMOR sessions - -// 6.0.17.1 November 2018 - -// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA -// Add Abort Connection to WINMOR and VARA Interfaces -// Reinstate accidentally removed CMS Access logging -// Fix MH CLEAR -// Fix corruption of NODE table if NODES received from station with null alias -// Fix loss of buffer if session closed with something in PARTCMDBUFFER -// Fix Spurious GUARD ZONE CORRUPT message in IP Code. -// Remove "reread bpq32.cfg and reconfigure" menu options -// Add support for PTT using CM108 based soundcard interfaces -// Datestamp Telnet log files and delete old Telnet and CMSAcces logs - -// 6.0.18.1 January 2019 - -// Fix validation of NODES broadcasts -// Fix HIDENODES -// Check for failure to reread config on axip reconfigure -// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port -// Send Beacons from BCALL or PORTCALL if configured -// Fix possible corruption of last entry in MH display -// Ensure RTS/DTR is down when opening PTT Port -// Remove RECONFIG command -// Preparations for 64 bit version - -// 6.0.19 Sept 2019 -// Fix UZ7HO interlock -// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) -// Add option to save and restore MH lists and SAVEMH command -// Add Frequency (if known) to UZ7HO MH lists -// Add Gateway option to Telnet for PAT -// Try to fix SCS Tracker recovery -// Ensure RTS/DTR is down on CAT port if using that line for PTT -// Experimental APRS Messaging in Kernel -// Add Rigcontrol on remote PC's using WinmorControl -// ADD VARAFM and VARAFM96 WL2KREPORT modes -// Fix WL2K sysop update for new Winlink API -// Fix APRS when using PORTNUM higher than the number of ports -// Add Serial Port Type -// Add option to linbpq to log APRS-IS messages. -// Send WL2K Session Reports -// Drop Tunneled Packets from 44.192 - 44.255 -// Log incoming Telnet Connects -// Add IPV4: and IPV6: overrides on AXIP Resolver. -// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) -// Add RADIO FREQ command to display current frequency - -// 6.0.20 April 2020 - -// Trap and reject YAPP file transfer request. -// Fix possible overrun of TCP to Node Buffer -// Fix possible crash if APRS WX file doesn't have a terminating newline -// Change communication with BPQAPRS.exe to restore old message popup behaviour -// Preparation for 64 bit version -// Improve flow control on SCS Dragon -// Fragment messages from network links to L2 links with smaller paclen -// Change WL2K report rate to once every two hours -// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator -// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) -// Add links to Ports Web Manangement Page to open individual Driver windows -// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers -// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. -// Don't try to send WL2K Traffic report if Internet is down -// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server -// ADD AGWAPPL config command to set application number. AGWMASK is still supported -// Register Node Alias with UZ7HO Driver -// Register calls when UZ7HO TNC Restarts and at intervals afterwards -// Fix crash when no IOADDR or COMPORT in async port definition -// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 -// Only apply BBSFLAG=NOBBS to APPPLICATION 1 -// Add RIGREONFIG command -// fix APRS RECONFIG on LinBPQ -// Fix Web Terminal scroll to end problem on some browsers -// Add PTT_SETS_INPUT option for IC7600 -// Add TELRECONFIG command to reread users or whole config -// Enforce PACLEN on UZ7HO ports -// Fix PACLEN on Command Output. -// Retry axip resolver if it fails at startup -// Fix AGWAPI connect via digis -// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers -// Limit APRS OBJECT length to 80 chars -// UZ7HO disconnect incoming call if no free streams -// Improve response to REJ (no F) followed by RR (F). -// Try to prevent more than MAXFRAME frames outstanding when transmitting -// Allow more than one instance of APRS on Linux -// Stop APRS digi by originating station -// Send driver window trace to main monitor system -// Improve handling of IPOLL messages -// Fix setting end of address bit on dest call on connects to listening sessions -// Set default BBS and CHAT application number and number of streams on LinBPQ -// Support #include in bpq32.cfg processing - -// Version 6.0.21 14 December 2020 - -// Fix occasional missing newlines in some node command reponses -// More 64 bit fixes -// Add option to stop setting PDUPLEX param in SCSPACTOR -// Try to fix buffer loss -// Remove extra space from APRS position reports -// Suppress VARA IAMALIVE messages -// Add display and control of QtSoundModem modems -// Only send "No CMS connection available" message if fallbacktorelay is set. -// Add HAMLIB backend and emulator support to RIGCONTROL -// Ensure all beacons are sent even with very short beacon intervals -// Add VARA500 WL2K Reporting Mode -// Fix problem with prpcessing frame collector -// Temporarily disable L2 and L4 collectors till I can find problem -// Fix possible problem with interactive RADIO commands not giving a response, -// Incease maximum length of NODE command responses to handle maximum length INFO message, -// Allow WL2KREPORT in CONFIG section of UZ7HO port config. -// Fix program error in processing hamlib frame -// Save RestartAfterFailure option for VARA -// Check callsign has a winlink account before sending WL2KREPORT messages -// Add Bandwidth control to VARA scanning -// Renable L2 collector -// Fix TNCPORT reconnect on Linux -// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases -// Add option to suppress sending call to application in Telnet HOST API -// Add FT991A support to RigControl -// Use background.jpg for Edit Config page -// Send OK response to SCS Pactor commands starting with # -// Resend ICOM PTT OFF command after 30 seconds -// Add WXCall to APRS config -// Fixes for AEAPactor -// Allow PTTMUX to use real or com0com com ports -// Fix monitoring with AGW Emulator -// Derive approx position from packets on APRS ports with a valid 6 char location -// Fix corruption of APRS message lists if the station table fills up. -// Don't accept empty username or password on Relay sessions. -// Fix occasional empty Nodes broadcasts -// Add Digis to UZ7HO Port MH list -// Add PERMITTEDAPPLS port param -// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. -// Fix handling AX/IP (proto 93) frames -// Fix possible corruption sending APRS messages -// Allow Telnet connections to be made using Connect command as well as Attach then Connect -// Fix Cancel Sysop Signin -// Save axip resolver info and restore on restart -// Add Transparent mode to Telnet Server HOST API -// Fix Tracker driver if WL2KREPRRT is in main config section -// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. -// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon -// Fix crash in Web terminal when processing very long lines - -// Version 6.0.22.1 August 2021 - -// Fix bug in KAM TNCEMULATOR -// Add WinRPR Driver (DED over TCP) -// Fix handling of VARA config commands FM1200 and FM9600 -// Improve Web Termanal Line folding -// Add StartTNC to WinRPR driver -// Add support for VARA2750 Mode -// Add support for VARA connects via a VARA Digipeater -// Add digis to SCSTracker and WinRPR MHeard -// Separate RIGCONTROL config from PORT config and add RigControl window -// Fix crash when a Windows HID device doesn't have a product_string -// Changes to VARA TNC connection and restart process -// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. -// Fix saving part lines in adif log and Winlink Session reporting -// Add port specific CTEXT -// Add FRMR monitoring to UZ7HO driver -// Add audio input switching for IC7610 -// Include Rigcontrol Support for IC-F8101E -// Process any response to KISS command -// Fix NODE ADD command -// Add noUpdate flag to AXIP MAP -// Fix clearing NOFALLBACK flag in Telnet Server -// Allow connects to RMS Relay running on another host -// Allow use of Power setting in Rigcontol scan lines for Kenwood radios -// Prevent problems caused by using "CMS" as a Node Alias -// Include standard APRS Station pages in code -// Fix VALIDCALLS processing in HF drivers -// Send Netrom Link reports to Node Map -// Add REALTELNET mode to Telnet Outward Connect -// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST -// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection -// Fix long (> 60 seconds) scan intervals -// Improved debugging of stuck semaphores -// Fix potential securiby bug in BPQ Web server -// Send Chat Updates to chatupdate.g8bpq.net port 81 -// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY -// Add experimental Mode reporting -// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY -// Add UPNP Support - -// Version 6.0.23.1 June 2022 - -// Add option to control which applcalls are enabled in VARA -// Add support for rtl_udp to Rig Control -// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal -// Allow setting css styles for Web Terminal -// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows -// More flexible RigControl for split frequency operation, eg for QO100 -// Increase stack size for ProcessHTMLMessage (.11) -// Fix HTML Content-Type on images (.12) -// Add AIS and ADSB Support (.13) -// Compress web pages (.14) -// Change minidump routine and close after program error (.15) -// Add RMS Relay SYNC Mode (.17) -// Changes for compatibility with Winlink Hybrid -// Add Rigcontrol CMD feature to Yaesu code (21) -// More diagnostic code -// Trap potential buffer overrun in ax/tcp code -// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail -// Add FLRIG as backend for RigControl (.24) -// Fix bug in compressing some management web pages -// Fix bugs in AGW Emulator (.25) -// Add more PTT_Sets_Freq options for split frequency working (.26) -// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) -// Fix Telnet negotiation and backspace processing (.29) -// Fix VARA Mode change when scanning (.30) -// Add Web Mgmt Log Display (.33) -// Fix crash when connecting to RELAY when CMS=0 (.36) -// Send OK to user for manual freq changes with hamlib or flrig -// Fix Rigcontrol leaving port disabled when using an empty timeband -// Fix processing of backspace in Telnet character processing (.40) -// Increase max size of connect script -// Fix HAMLIB Slave Thread control -// Add processing of VARA mode responses and display of VARA Mode (41) -// Fix crash when VARA session aborted on LinBPQ (43) -// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) -// Include APRS Map web page -// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) -// Add Basic DRATS interface (46) -// Fix MYCALLS on VARA (49) -// Add FreeData driver (51) -// Add additonal Rigcontrol options for QO100 (51) -// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) -// Fix sending large compressed web messages (52) -// Fix freq display when using flrig or hamlib backends to rigcontrol -// Change VARA Driver to send ABORT when Session Time limit expires -// Add Chat Log to Web Logs display -// Fix possible buffer loss in RigControl -// Allow hosts on local lan to be treated as secure -// Improve validation of data sent to Winlink SessionAdd API call -// Add support for FreeDATA modem. -// Add GetLOC API Call -// Change Leaflet link in aprs map. -// Add Connect Log (64) -// Fix crash when Resolve CMS Servers returns ipv6 addresses -// Fix Reporting P4 sessions to Winlink (68) -// Add support for FreeBSD (68) -// Fix Rigcontrol PTCPORT (69) -// Set TNC Emulator sessions as secure (72) -// Fix not always detecting loss of FLRIG (73) -// Add ? and * wildcards to NODES command (74) -// Add Port RADIO config parameter (74) - -// Version 6.0.24.1 August 2023 - -// Apply NODES command wildcard to alias as well a call (2) -// Add STOPPORT/STARTPORT to VARA Driver (2) -// Add bandwidth setting to FLRIG interface. (2) -// Fix N VIA (3) -// Fix NODE ADD and NODE DEL (4) -// Improvements to FLRIG Rigcontrol backend (6, 7) -// Fix UZ7HO Window Title Update -// Reject L2 calls with a blank from call (8) -// Update WinRPR Window header with BPQ Port Description (8) -// Fix error in blank call code (9) -// Change web buttons to white on black when pressed (10) -// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) -// Add RADIO PTT command for testing PTT (11) -// Fix using APPLCALLs on SCSTracker RP call (12) -// Add Rigcntol Web Page (13) -// Fix scan bandwidth change with ARDOPOFDM (13) -// Fix setting Min Pactor Level in SCSPactor (13) -// Fix length of commands sent via CMD_TO_APPL flag (14) -// Add filter by quality option to N display (15) -// Fix VARA Mode reporting to WL2K (16) -// Add FLRIG POWER and TUNE commands (18) -// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) -// FLDIGI improvements (19) -// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) -// Fix processing C command if first port driver is SCSPACTROR (20) -// Fix crash in UZ7HO driver if bad raw frame received (21) -// Fix using FLARQ chat mode with FLDIGI ddriover (22) -// Fix to KISSHF driver (23) -// Fix for application buffer loss (24) -// Add Web Sockets auto-refresh option for Webmail index page (25) -// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) -// Add SmartID for bridged frames - Send ID only if packets sent recently (26) -// Add option to save and restore received APRS messages (27) -// Add mechanism to run a user program on certain events (27) -// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) -// Add Node Help command (28) -// Add APRS Igate RXOnly option (29) -// Fix RMC message handling with prefixes other than GP (29) -// Add GPSD support for APRS (30) -// Attempt to fix Tracker/WinRPR reconnect code (30) -// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) -// Fix interactive commands in tracker driver (33) -// Fix SESSIONTIMELIMIT processing -// Add STOPPORT/STARTPORT for UZ7HO driver -// Fix processing of extended QtSM 'g' frame (36) -// Allow setting just freq on Yaseu rigs (37) -// Enable KISSHF driver on Linux (40) -// Allow AISHOST and ADSBHOST to be a name as well as an address (41) -// Fix Interlock of incoming UZ7HO connections (41) -// Disable VARA Actions menu if not sysop (41) -// Fix Port CTEXT on UZ7HO B C or D channels (42) -// Fix repeated trigger of SessionTimeLimit (43) -// Fix posible memory corruption in UpateMH (44) -// Add PHG to APRS beacons (45) -// Dont send DM to stations in exclude list(45) -// Improvements to RMS Relay SYNC Mode (46) -// Check L4 connects against EXCLUDE list (47) -// Add vaidation of LOC in WL2K Session Reports (49) -// Change gpsd support for compatibility with Share Gps (50) -// Switch APRS Map to my Tiles (52) -// Fix using ; in UNPROTO Mode messages (52) -// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) -// Fix TNC Emulator Monitoring (53) -// Fix attach and connect on Telnet port bug introduced in .55 (56) -// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) -// Fix stack size in beginthread for MAC (58) -// Add NETROM over VARA (60) -// Add Disconnect Script (64) -// Add node commands to set UZ7HO modem mode and freq (64) -// Trap empty NODECALL or NETROMCALL(65) -// Trap NODES messages with empty From Call (65) -// Add RigControl for SDRConsole (66) -// Fix FLRig crash (66) -// Fix VARA disconnect handling (67) -// Support 64 ports (69) -// Fix Node commands for setting UZ7HO Modem (70) -// Fix processing SABM on an existing session (71) -// Extend KISS Node command to send more than one parameter byte (72) -// Add G7TAJ's code to record activity of HF ports for stats display (72) -// Add option to send KISS command to TNC on startup (73) -// Fix Bug in DED Emulator Monitor code (74) -// Add Filters to DED Monitor code (75) -// Detect loss of DED application (76) -// Fix connects to Application Alias with UZ7HO Driver (76) -// Fix Interlock of ports on same UZ7HO modem. (76) -// Add extended Ports command (77) -// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) -// Fix Web Terminal (80) -// Trap ENCRYPTION message from VARA (81) -// Fix processing of the Winlink API /account/exists response (82) -// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set - -// Version 6.0.25.? - -// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers -// Add Chat PACLEN config (5) -// Fix NC to Application Call (6) -// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) -// Get Beacon config from config file on Windows (9) -// fix processing DED TNC Emulator M command with space between M and params (10) -// Fix sending UI frames on SCSPACTOR (11) -// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) -// Add SDRAngel rig control (11) -// Add option to specify config and data directories on linbpq (12) -// Allow zero resptime (send RR immediately) (13) -// Make sure CMD bit is set on UI frames -// Add setting Modem Flags in QtSM AGW mode -// If FT847 om PTC Port send a "Cat On" command (17) -// Fix some 63 port bugs in RigCOntrol (17) -// Fix 63 port bug in Bridging (18) -// Add FTDX10 Rigcontrol (19) -// Fix 64 bit bug in displaying INP3 Messages (20) -// Improve restart of WinRPR TNC on remote host (21) -// Fix some Rigcontrol issues with empty timebands (22) -// Fix 64 bit bug in processing INP3 Messages (22) -// First pass at api (24) -// Send OK in response to Rigcontrol CMD (24) -// Disable CTS check in WriteComBlock (26) -// Improvments to reporting to M0LTE Map (26) -// IPGateway fix from github user isavitsky (27) -// Fix possible crash in SCSPactor PTCPORT code (29) -// Add NodeAPI call sendLinks and remove get from other calls (32) -// Improve validation of Web Beacon Config (33) -// Support SNMP via host ip stack as well as IPGateway (34) -// Switch APRS Map to OSM tile servers (36) -// Fix potential buffer overflow in Telnet login (36) -// Allow longer serial device names (37) -// Fix ICF8101 Mode setting (37) -// Kill link if we are getting repeated RR(F) after timeout -// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) -// Change default of SECURETELNET to 1 (41) -// Add optional ATTACH time limit for ARDOP (42) -// Fix buffer overflow risk in HTTP Terminal(42) -// Fix KISSHF Interlock (43) -// Support other than channel A on HFKISS (43) -// Support additional port info reporting for M0LTE Map (44) -// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) -// Add ARDOP UI Packets to MH (45) -// Add support for Qtsm Mgmt Interface (45) -// NodeAPI improvements (46) -// Add MQTT Interface (46) -// Fix buffer leak in ARDOP code(46) -// Fix possible crash if MQTT not in use (47) -// Add optional ATTACH time limit for VARA (48) -// API format fixes (48) -// AGWAPI Add protection against accidental connects from a non-agw application (50) -// Save MH and NODES every hour (51) -// Fix handling long unix device names (now max 250 bytes) (52) -// Fix error reporting in api update (53) -// Coding changes to remove some compiler warnings (53, 54) -// Add MQTT reporting of Mail Events (54) -// Fix beaconong on KISSHF ports (55) -// Fix MailAPI msgs endpoint -// Attempt to fix NC going to wrong application. (57) -// Improve ARDOP end of session code (58) -// Run M0LTE Map reporting in a separate thread (59/60) -// Add RHP support for WhatsPac (59) -// Add timestamps to LIS monitor (60) -// Fix problem with L4 frames being delivered out of sequence (60) -// Add Compression of Netrom connections (62) -// Improve handling of Locked Routes (62) -// Add L4 RESET (Paula G8PZT's extension to NETROM) -// Fix problem using SENDRAW from BPQMail (63) -// Fix compatibility with latest ardopcf (64) -// Fix bug in RHP socket timeout code (65) -// Fix L4 RTT (66) -// Fix RigConrol with Chanxx but no other settings (66) -// Add option to compress L2 frames (67) -// Sort Routes displays (67) -// Fix Ardop session premature close (70) -// Add timestamps to log entries in Web Driver windows (70) -// Generate stack backtrace if SIGSEGV or SIGABRT occur (Linux) (70) -// Remove some debug logging from L2 code (70) -// Fix compiling LinBPQ with nomqtt option (70) -// Improve handling of binary data in RHP interface (70) -// Fix sending KISS commands to multiport or multidropped TNCs (70) -// Add MHUV and MHLV commands (Verbose listing with timestamps in clock time) (70) - -#define CKernel - -#include "Versions.h" - -#define _CRT_SECURE_NO_DEPRECATE - -#pragma data_seg("_BPQDATA") - -#include "time.h" -#include "stdio.h" -#include - -#include "compatbits.h" -#include "AsmStrucs.h" - -#include "SHELLAPI.H" -#include "kernelresource.h" - -#include -#include -#include "BPQTermMDI.h" - -#include "GetVersion.h" - -#define DllImport __declspec( dllimport ) - -#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) -void _CheckGuardZone(char * File, int Line); - -#define CHECKLOADED 0 -#define SETAPPLFLAGS 1 -#define SENDBPQFRAME 2 -#define GETBPQFRAME 3 -#define GETSTREAMSTATUS 4 -#define CLEARSTREAMSTATUS 5 -#define BPQCONDIS 6 -#define GETBUFFERSTATUS 7 -#define GETCONNECTIONINFO 8 -#define BPQRETURN 9 // GETCALLS -//#define RAWTX 10 //IE KISS TYPE DATA -#define GETRAWFRAME 11 -#define UPDATESWITCH 12 -#define BPQALLOC 13 -//#define SENDNETFRAME 14 -#define GETTIME 15 - -extern short NUMBEROFPORTS; -extern long PORTENTRYLEN; -extern long LINKTABLELEN; -extern struct PORTCONTROL * PORTTABLE; -extern void * FREE_Q; -extern UINT APPL_Q; // Queue of frames for APRS Appl - -extern TRANSPORTENTRY * L4TABLE; -extern UCHAR NEXTID; -extern DWORD MAXCIRCUITS; -extern DWORD L4DEFAULTWINDOW; -extern DWORD L4T1; -extern APPLCALLS APPLCALLTABLE[]; -extern char * APPLS; - -extern struct WL2KInfo * WL2KReports; - -extern int NUMBEROFTNCPORTS; - - -void * VCOMExtInit(struct PORTCONTROL * PortEntry); -void * AXIPExtInit(struct PORTCONTROL * PortEntry); -void * SCSExtInit(struct PORTCONTROL * PortEntry); -void * AEAExtInit(struct PORTCONTROL * PortEntry); -void * KAMExtInit(struct PORTCONTROL * PortEntry); -void * HALExtInit(struct PORTCONTROL * PortEntry); -void * ETHERExtInit(struct PORTCONTROL * PortEntry); -void * AGWExtInit(struct PORTCONTROL * PortEntry); -void * WinmorExtInit(EXTPORTDATA * PortEntry); -void * TelnetExtInit(EXTPORTDATA * PortEntry); -//void * SoundModemExtInit(EXTPORTDATA * PortEntry); -void * TrackerExtInit(EXTPORTDATA * PortEntry); -void * TrackerMExtInit(EXTPORTDATA * PortEntry); -void * V4ExtInit(EXTPORTDATA * PortEntry); -void * UZ7HOExtInit(EXTPORTDATA * PortEntry); -void * MPSKExtInit(EXTPORTDATA * PortEntry); -void * FLDigiExtInit(EXTPORTDATA * PortEntry); -void * UIARQExtInit(EXTPORTDATA * PortEntry); -void * SerialExtInit(EXTPORTDATA * PortEntry); -void * ARDOPExtInit(EXTPORTDATA * PortEntry); -void * VARAExtInit(EXTPORTDATA * PortEntry); -void * KISSHFExtInit(EXTPORTDATA * PortEntry); -void * WinRPRExtInit(EXTPORTDATA * PortEntry); -void * HSMODEMExtInit(EXTPORTDATA * PortEntry); -void * FreeDataExtInit(EXTPORTDATA * PortEntry); -void * SIXPACKExtInit(EXTPORTDATA * PortEntry); - -extern char * ConfigBuffer; // Config Area -VOID REMOVENODE(dest_list * DEST); -DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); -DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); -VOID GetUIConfig(); -VOID ADIFWriteFreqList(); -void SaveAIS(); -void initAIS(); -void initADSB(); - -extern BOOL ADIFLogEnabled; - -int CloseOnError = 0; - -char UIClassName[]="UIMAINWINDOW"; // the main window class name - -HWND UIhWnd; - -extern char AUTOSAVE; -extern char AUTOSAVEMH; - -extern char MYNODECALL; // 10 chars,not null terminated - -extern QCOUNT; -extern BPQVECSTRUC BPQHOSTVECTOR[]; -#define BPQHOSTSTREAMS 64 -#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] - -extern char * CONFIGFILENAME; - -DllExport BPQVECSTRUC * BPQHOSTVECPTR; - -extern int DATABASESTART; - -extern struct ROUTE * NEIGHBOURS; -extern int ROUTE_LEN; -extern int MAXNEIGHBOURS; - -extern struct DEST_LIST * DESTS; // NODE LIST -extern int DEST_LIST_LEN; -extern int MAXDESTS; // MAX NODES IN SYSTEM - -extern struct _LINKTABLE * LINKS; -extern int LINK_TABLE_LEN; -extern int MAXLINKS; - -extern double LatFromLOC; -extern double LonFromLOC; - - -extern int BPQHOSTAPI(); -extern int INITIALISEPORTS(); -extern int TIMERINTERRUPT(); -extern int MONDECODE(); -extern int BPQMONOPTIONS(); -extern char PWTEXT[]; -extern char PWLen; - -extern int FINDFREEDESTINATION(); -extern int RAWTX(); -extern int RELBUFF(); -extern int SENDNETFRAME(); -extern char MYCALL[]; // 7 chars, ax.25 format - -extern HWND hIPResWnd; -extern BOOL IPMinimized; - -extern int NODESINPROGRESS; -extern VOID * CURRENTNODE; - - -BOOL Start(); - -VOID SaveWindowPos(int port); -VOID SaveAXIPWindowPos(int port); -VOID SetupRTFHddr(); -DllExport VOID APIENTRY CreateNewTrayIcon(); -int DoReceivedData(int Stream); -int DoStateChange(int Stream); -int DoMonData(int Stream); -struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); -CloseHostSessions(); -SaveHostSessions(); -VOID SaveBPQ32Windows(); -VOID CloseDriverWindow(int port); -VOID CheckWL2KReportTimer(); -VOID SetApplPorts(); -VOID WriteMiniDump(); -VOID FindLostBuffers(); -BOOL InitializeTNCEmulator(); -VOID TNCTimer(); -char * strlop(char * buf, char delim); - -DllExport int APIENTRY Get_APPLMASK(int Stream); -DllExport int APIENTRY GetStreamPID(int Stream); -DllExport int APIENTRY GetApplFlags(int Stream); -DllExport int APIENTRY GetApplNum(int Stream); -DllExport BOOL APIENTRY GetAllocationState(int Stream); -DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); -DllExport int APIENTRY RXCount(int Stream); -DllExport int APIENTRY TXCount(int Stream); -DllExport int APIENTRY MONCount(int Stream); -DllExport int APIENTRY GetCallsign(int stream, char * callsign); -DllExport VOID APIENTRY RelBuff(VOID * Msg); -void SaveMH(); -void DRATSPoll(); - -#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); -int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); - -VOID SetWindowTextSupport(); -int WritetoConsoleSupport(char * buff); -VOID PMClose(); -VOID MySetWindowText(HWND hWnd, char * Msg); -BOOL CreateMonitorWindow(char * MonSize); -VOID FormatTime3(char * Time, time_t cTime); - -char EXCEPTMSG[80] = ""; - -char SIGNONMSG[128] = ""; -char SESSIONHDDR[80] = ""; -int SESSHDDRLEN = 0; - -BOOL IncludesMail = FALSE; -BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index - - -char WL2KCall[10]; -char WL2KLoc[7]; - -extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON -extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON -extern char LOC[7]; // Maidenhead Locator for Reporting -extern char ReportDest[7]; - -extern UCHAR ConfigDirectory[260]; - -extern uint64_t timeLoadedMS; - -VOID __cdecl Debugprintf(const char * format, ...); -VOID __cdecl Consoleprintf(const char * format, ...); - -DllExport int APIENTRY CloseBPQ32(); -DllExport char * APIENTRY GetLOC(); -DllExport int APIENTRY SessionControl(int stream, int command, int param); - -int DoRefreshWebMailIndex(); - -BOOL APIENTRY Init_IP(); -BOOL APIENTRY Poll_IP(); - -BOOL APIENTRY Init_PM(); -BOOL APIENTRY Poll_PM(); - -BOOL APIENTRY Init_APRS(); -BOOL APIENTRY Poll_APRS(); -VOID HTTPTimer(); - -BOOL APIENTRY Rig_Init(); -BOOL APIENTRY Rig_Close(); -BOOL Rig_Poll(); - -VOID IPClose(); -VOID APRSClose(); -VOID CloseTNCEmulator(); - -VOID Poll_AGW(); -void RHPPoll(); -BOOL AGWAPIInit(); -int AGWAPITerminate(); - -int * Flag = (int *)&Flag; // for Dump Analysis -int MAJORVERSION=4; -int MINORVERSION=9; - -struct SEM Semaphore = {0, 0, 0, 0}; -struct SEM APISemaphore = {0, 0, 0, 0}; -int SemHeldByAPI = 0; -int LastSemGets = 0; -UINT Sem_eax = 0; -UINT Sem_ebx = 0; -UINT Sem_ecx = 0; -UINT Sem_edx = 0; -UINT Sem_esi = 0; -UINT Sem_edi = 0; - - -#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) -void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); -void FreeSemaphore(struct SEM * Semaphore); - -DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; -//DllExport long MONDECODEPTR = (long)&MONDECODE; - -extern UCHAR BPQDirectory[]; -extern UCHAR LogDirectory[]; -extern UCHAR BPQProgramDirectory[]; - -static char BPQWinMsg[] = "BPQWindowMessage"; - -static char ClassName[] = "BPQMAINWINDOW"; - -HKEY REGTREE = HKEY_CURRENT_USER; -char REGTREETEXT[100] = "HKEY_CURRENT_USER"; - -UINT BPQMsg=0; - -#define MAXLINELEN 120 -#define MAXSCREENLEN 50 - -#define BGCOLOUR RGB(236,233,216) - -HBRUSH bgBrush = NULL; - -//int LINELEN=120; -//int SCREENLEN=50; - -//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; - -//int lineno=0; -//int col=0; - -#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer -int ReportTimer = 0; - -HANDLE OpenConfigFile(char * file); - -VOID SetupBPQDirectory(); -VOID SendLocation(); - -//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); - -#define TRAY_ICON_ID 1 // ID number for the Notify Icon -#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window - -NOTIFYICONDATA niData; - -int SetupConsoleWindow(); - -BOOL StartMinimized=FALSE; -BOOL MinimizetoTray=TRUE; - -BOOL StatusMinimized = FALSE; -BOOL ConsoleMinimized = FALSE; - -HMENU trayMenu=0; - -HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; - -BOOL FrameMaximized = FALSE; - -BOOL IGateEnabled = TRUE; -extern int ISDelayTimer; // Time before trying to reopen APRS-IS link -extern int ISPort; - -UINT * WINMORTraceQ = NULL; -UINT * SetWindowTextQ = NULL; - -static RECT Rect = {100,100,400,400}; // Console Window Position -RECT FRect = {100,100,800,600}; // Frame -static RECT StatusRect = {100,100,850,500}; // Status Window - -DllExport int APIENTRY DumpSystem(); -DllExport int APIENTRY SaveNodes (); -DllExport int APIENTRY ClearNodes (); -DllExport int APIENTRY SetupTrayIcon(); - -#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) - -VOID * _Q_REM(VOID *Q, char * File, int Line); - -UINT ReleaseBuffer(UINT *BUFF); - - -VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); - -DllExport int APIENTRY DeallocateStream(int stream); - -int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); - -int FirstEntry = 1; -BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close -BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe - -BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. - // Used to Kill surplus BPQ32.exe processes - -DWORD Our_PID; // Our Process ID - local variable - -void * InitDone = 0; -int FirstInitDone = 0; -int PerlReinit = 0; -UINT_PTR TimerHandle = 0; -UINT_PTR SessHandle = 0; - -BOOL EventsEnabled = 0; - -unsigned int TimerInst = 0xffffffff; - -HANDLE hInstance = 0; - -int AttachedProcesses = 0; -int AttachingProcess = 0; -HINSTANCE hIPModule = 0; -HINSTANCE hRigModule = 0; - -BOOL ReconfigFlag = FALSE; -BOOL RigReconfigFlag = FALSE; -BOOL APRSReconfigFlag = FALSE; -BOOL CloseAllNeeded = FALSE; -BOOL NeedWebMailRefresh = FALSE; - -int AttachedPIDList[100] = {0}; - -HWND hWndArray[100] = {0}; -int PIDArray[100] = {0}; -char PopupText[30][100] = {""}; - -// Next 3 should be uninitialised so they are local to each process - -UCHAR MCOM; -UCHAR MTX; // Top bit indicates use local time -uint64_t MMASK; -UCHAR MUIONLY; - -UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list - -char pgm[256]; // Uninitialised so per process - -HANDLE Mutex; - -BOOL PartLine = FALSE; -int pindex = 0; -DWORD * WritetoConsoleQ; - - -LARGE_INTEGER lpFrequency = {0}; -LARGE_INTEGER lastRunTime; -LARGE_INTEGER currentTime; - -int ticksPerMillisec; -int interval; - - -VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); - - -TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; -TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; - - -BOOL ProcessConfig(); -VOID FreeConfig(); - -DllExport int APIENTRY WritetoConsole(char * buff); - -BOOLEAN CheckifBPQ32isLoaded(); -BOOLEAN StartBPQ32(); -DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); -BOOL LoadIPDriver(); -BOOL Send_IP(VOID * Block, DWORD len); -VOID CheckforLostProcesses(); -BOOL LoadRigDriver(); -VOID SaveConfig(); -VOID CreateRegBackup(); -VOID ResolveUpdateThread(); -VOID OpenReportingSockets(); -DllExport VOID APIENTRY CloseAllPrograms(); -DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); -int upnpClose(); - -BOOL IPActive = FALSE; -extern BOOL IPRequired; -BOOL PMActive = FALSE; -extern BOOL PMRequired; -BOOL RigRequired = TRUE; -BOOL RigActive = FALSE; -BOOL APRSActive = FALSE; -BOOL AGWActive = FALSE; -BOOL needAIS = FALSE; -int needADSB = 0; - -extern int AGWPort; - -Tell_Sessions(); - - -typedef int (WINAPI FAR *FARPROCX)(); - -FARPROCX CreateToolHelp32SnapShotPtr; -FARPROCX Process32Firstptr; -FARPROCX Process32Nextptr; - -void LoadToolHelperRoutines() -{ - HINSTANCE ExtDriver=0; - int err; - char msg[100]; - - ExtDriver=LoadLibrary("kernel32.dll"); - - if (ExtDriver == NULL) - { - err=GetLastError(); - sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); - OutputDebugString(msg); - return; - } - - CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); - Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); - Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); - - if (CreateToolHelp32SnapShotPtr == 0) - { - err=GetLastError(); - sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); - OutputDebugString(msg); - return; - } -} - -BOOL GetProcess(int ProcessID, char * Program) -{ - HANDLE hProcessSnap; - PROCESSENTRY32 pe32; - int p; - - if (CreateToolHelp32SnapShotPtr==0) - { - return (TRUE); // Routine not available - } - // Take a snapshot of all processes in the system. - hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); - if( hProcessSnap == INVALID_HANDLE_VALUE ) - { - OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); - return( FALSE ); - } - - // Set the size of the structure before using it. - pe32.dwSize = sizeof( PROCESSENTRY32 ); - - // Retrieve information about the first process, - // and exit if unsuccessful - if( !Process32Firstptr( hProcessSnap, &pe32 ) ) - { - OutputDebugString( "Process32First Failed\n" ); // Show cause of failure - CloseHandle( hProcessSnap ); // Must clean up the snapshot object! - return( FALSE ); - } - - // Now walk the snapshot of processes, and - // display information about each process in turn - do - { - if (ProcessID==pe32.th32ProcessID) - { - // if running on 98, program contains the full path - remove it - - for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) - { - if (pe32.szExeFile[p]=='\\') - { - break; - } - } - p++; - - sprintf(Program,"%s", &pe32.szExeFile[p]); - CloseHandle( hProcessSnap ); - return( TRUE ); - } - - } while( Process32Nextptr( hProcessSnap, &pe32 ) ); - - - sprintf(Program,"PID %d Not Found", ProcessID); - CloseHandle( hProcessSnap ); - return(FALSE); -} - -BOOL IsProcess(int ProcessID) -{ - // Check that Process exists - - HANDLE hProcessSnap; - PROCESSENTRY32 pe32; - - if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available - - hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); - - if( hProcessSnap == INVALID_HANDLE_VALUE ) - { - OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); - return(TRUE); // Don't know, so assume ok - } - - pe32.dwSize = sizeof( PROCESSENTRY32 ); - - if( !Process32Firstptr( hProcessSnap, &pe32 ) ) - { - OutputDebugString( "Process32First Failed\n" ); // Show cause of failure - CloseHandle( hProcessSnap ); // Must clean up the snapshot object! - return(TRUE); // Don't know, so assume ok - } - - do - { - if (ProcessID==pe32.th32ProcessID) - { - CloseHandle( hProcessSnap ); - return( TRUE ); - } - - } while( Process32Nextptr( hProcessSnap, &pe32 ) ); - - CloseHandle( hProcessSnap ); - return(FALSE); -} - -#include "DbgHelp.h" - -VOID MonitorThread(int x) -{ - // Thread to detect killed processes. Runs in process owning timer. - - // Obviously can't detect loss of timer owning thread! - - do - { - if (Semaphore.Gets == LastSemGets && Semaphore.Flag) - { - // It is stuck - try to release - - Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", - Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); - - // Write a minidump - - WriteMiniDump(); - - Semaphore.Flag = 0; - } - - LastSemGets = Semaphore.Gets; - - Sleep(30000); - CheckforLostProcesses(); - - } while (TRUE); -} - -VOID CheckforLostProcesses() -{ - UCHAR buff[100]; - char Log[80]; - int i, n, ProcessID; - - for (n=0; n < AttachedProcesses; n++) - { - ProcessID=AttachedPIDList[n]; - - if (!IsProcess(ProcessID)) - { - // Process has died - Treat as a detach - - sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); - OutputDebugString(Log); - - // Remove Tray Icon Entry - - for( i = 0; i < 100; ++i ) - { - if (PIDArray[i] == ProcessID) - { - hWndArray[i] = 0; - sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); - OutputDebugString(Log); - DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); - } - } - - // If process had the semaphore, release it - - if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) - { - OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); - Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, - Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); - - Semaphore.Flag = 0; - SemHeldByAPI = 0; - } - - for (i=1;i<65;i++) - { - if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) - { - DeallocateStream(i); - } - } - - if (TimerInst == ProcessID) - { - KillTimer(NULL,TimerHandle); - TimerHandle=0; - TimerInst=0xffffffff; -// Tell_Sessions(); - OutputDebugString("BPQ32 Process was running timer \n"); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - - } - - // Remove this entry from PID List - - for (i=n; i< AttachedProcesses; i++) - { - AttachedPIDList[i]=AttachedPIDList[i+1]; - } - AttachedProcesses--; - - sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); - OutputDebugString(buff); - } - } -} -VOID MonitorTimerThread(int x) -{ - // Thread to detect killed timer process. Runs in all other BPQ32 processes. - - do { - - Sleep(60000); - - if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) - { - // Timer owning Process has died - Force a new timer to be created - // New timer thread will detect lost process and tidy up - - Debugprintf("BPQ32 Process %d with Timer died", TimerInst); - - // If process was holding the semaphore, release it - - if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) - { - OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); - Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, - Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); - Semaphore.Flag = 0; - SemHeldByAPI = 0; - } - -// KillTimer(NULL,TimerHandle); -// TimerHandle=0; -// TimerInst=0xffffffff; -// Tell_Sessions(); - - CheckforLostProcesses(); // Normally only done in timer thread, which is now dead - - // Timer can only run in BPQ32.exe - - TimerInst=0xffffffff; // So we dont keep doing it - TimerHandle = 0; // So new process attaches - - if (Closing == FALSE && AttachingProcess == FALSE) - { - OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); - StartBPQ32(); - } - -// if (MinimizetoTray) -// Shell_NotifyIcon(NIM_DELETE,&niData); - } - - } while (TRUE); -} - -VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); - -VOID TimerProcX(); - -VOID CALLBACK TimerProc( - HWND hwnd, // handle of window for timer messages - UINT uMsg, // WM_TIMER message - UINT idEvent, // timer identifier - DWORD dwTime) // current system time -{ - KillTimer(NULL,TimerHandle); - TimerProcX(); - TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); -} -VOID TimerProcX() -{ - struct _EXCEPTION_POINTERS exinfo; - - // - // Get semaphore before proceeeding - // - - GetSemaphore(&Semaphore, 2); - - // Get time since last run - - QueryPerformanceCounter(¤tTime); - - interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; - lastRunTime.QuadPart = currentTime.QuadPart; - - //Debugprintf("%d", interval); - - // Process WINMORTraceQ - - while (WINMORTraceQ) - { - UINT * Buffer = Q_REM(&WINMORTraceQ); - struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; - int Len = Buffer[2]; - char * Msg = (char *)&Buffer[3]; - - WritetoTraceSupport(TNC, Msg, Len); - RelBuff(Buffer); - } - - if (SetWindowTextQ) - SetWindowTextSupport(); - - while (WritetoConsoleQ) - { - UINT * Buffer = Q_REM(&WritetoConsoleQ); - WritetoConsoleSupport((char *)&Buffer[2]); - RelBuff(Buffer); - } - - strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); - - __try - { - - if (trayMenu == NULL) - SetupTrayIcon(); - - // See if reconfigure requested - - if (CloseAllNeeded) - { - CloseAllNeeded = FALSE; - CloseAllPrograms(); - } - - if (ReconfigFlag) - { - // Only do it it timer owning process, or we could get in a real mess! - - if(TimerInst == GetCurrentProcessId()) - { - int i; - BPQVECSTRUC * HOSTVEC; - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - WSADATA WsaData; // receives data from WSAStartup - RECT cRect; - - ReconfigFlag = FALSE; - - SetupBPQDirectory(); - - WritetoConsole("Reconfiguring ...\n\n"); - OutputDebugString("BPQ32 Reconfiguring ...\n"); - - GetWindowRect(FrameWnd, &FRect); - - SaveWindowPos(70); // Rigcontrol - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports - } - } - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - IPClose(); - PMClose(); - APRSClose(); - Rig_Close(); - CloseTNCEmulator(); - if (AGWActive) - AGWAPITerminate(); - - WSACleanup(); - - WL2KReports = NULL; - - Sleep(2000); - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - - Start(); - - INITIALISEPORTS(); // Restart Ports - - SetApplPorts(); - - FreeConfig(); - - for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec - { - HOSTVEC=&BPQHOSTVECTOR[i-1]; - - HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized - - if (HOSTVEC->HOSTSESSION !=0) - { - // Had a connection - - HOSTVEC->HOSTSESSION=0; - HOSTVEC->HOSTFLAGS |=3; // Disconnected - - PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); - } - } - - // Free the APRS Appl Q - - APPL_Q = 0; - - OpenReportingSockets(); - - WritetoConsole("\n\nReconfiguration Complete\n"); - - if (IPRequired) IPActive = Init_IP(); - if (PMRequired) PMActive = Init_PM(); - - APRSActive = Init_APRS(); - - if (ISPort == 0) - IGateEnabled = 0; - - CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); - - GetClientRect(hConsWnd, &cRect); - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - { - ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - } - InvalidateRect(hConsWnd, NULL, TRUE); - - RigActive = Rig_Init(); - - if (NUMBEROFTNCPORTS) - { - FreeSemaphore(&Semaphore); - InitializeTNCEmulator(); - GetSemaphore(&Semaphore, 0); - } - - FreeSemaphore(&Semaphore); - AGWActive = AGWAPIInit(); - GetSemaphore(&Semaphore, 0); - - OutputDebugString("BPQ32 Reconfiguration Complete\n"); - } - } - - - if (RigReconfigFlag) - { - // Only do it it timer owning process, or we could get in a real mess! - - if(TimerInst == GetCurrentProcessId()) - { - RigReconfigFlag = FALSE; - CloseDriverWindow(70); - Rig_Close(); - Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close - RigActive = Rig_Init(); - - WritetoConsole("Rigcontrol Reconfiguration Complete\n"); - } - } - - if (APRSReconfigFlag) - { - // Only do it it timer owning process, or we could get in a real mess! - - if(TimerInst == GetCurrentProcessId()) - { - APRSReconfigFlag = FALSE; - APRSClose(); - APRSActive = Init_APRS(); - - WritetoConsole("APRS Reconfiguration Complete\n"); - } - } - - } - #include "StdExcept.c" - - if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) - FreeSemaphore(&Semaphore); - - } - - strcpy(EXCEPTMSG, "Timer Processing"); - - __try - { - if (IPActive) Poll_IP(); - if (PMActive) Poll_PM(); - if (RigActive) Rig_Poll(); - - if (NeedWebMailRefresh) - DoRefreshWebMailIndex(); - - CheckGuardZone(); - - if (APRSActive) - { - Poll_APRS(); - CheckGuardZone(); - } - - CheckWL2KReportTimer(); - - CheckGuardZone(); - - TIMERINTERRUPT(); - - CheckGuardZone(); - - FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore - - if (NUMBEROFTNCPORTS) - TNCTimer(); - - if (AGWActive) - Poll_AGW(); - - DRATSPoll(); - RHPPoll(); - - CheckGuardZone(); - - strcpy(EXCEPTMSG, "HTTP Timer Processing"); - - HTTPTimer(); - - CheckGuardZone(); - - strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); - - if (ReportTimer) - { - ReportTimer--; - - if (ReportTimer == 0) - { - ReportTimer = REPORTINTERVAL; - SendLocation(); - } - } - } - - #include "StdExcept.c" - - if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) - FreeSemaphore(&Semaphore); - - } - - CheckGuardZone(); - - return; -} - -HANDLE NPHandle; - -int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; -int (WINAPI FAR *EnumProcessesPtr)() = NULL; - -FirstInit() -{ - WSADATA WsaData; // receives data from WSAStartup - HINSTANCE ExtDriver=0; - RECT cRect; - - - // First Time Ports and Timer init - - // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain - - // Call wsastartup - most systems need winsock, and duplicate statups could be a problem - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - // Load Psapi.dll if possible - - ExtDriver=LoadLibrary("Psapi.dll"); - - SetupTrayIcon(); - - if (ExtDriver) - { - GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); - EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); - } - - timeLoadedMS = GetTickCount(); - - INITIALISEPORTS(); - - OpenReportingSockets(); - - WritetoConsole("\n"); - WritetoConsole("Port Initialisation Complete\n"); - - if (IPRequired) IPActive = Init_IP(); - if (PMRequired) PMActive = Init_PM(); - - APRSActive = Init_APRS(); - - if (APRSActive) - { - hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, - 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); - - CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, - 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "IGate State - Disconnected", - WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", - WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "GPS Off", - WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); - } - - if (ISPort == 0) - IGateEnabled = 0; - - CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); - - GetClientRect(hConsWnd, &cRect); - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - { - ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - } - InvalidateRect(hConsWnd, NULL, TRUE); - - RigActive = Rig_Init(); - - _beginthread(MonitorThread,0,0); - - TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); - TimerInst=GetCurrentProcessId(); - SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); - - // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser - - if (ADIFLogEnabled) - ADIFWriteFreqList(); - - OutputDebugString("BPQ32 Port Initialisation Complete\n"); - - if (needAIS) - initAIS(); - - if (needADSB) - initADSB(); - - return 0; -} - -Check_Timer() -{ - if (Closing) - return 0; - - if (Semaphore.Flag) - return 0; - - if (InitDone == (void *)-1) - { - GetSemaphore(&Semaphore, 3); - Sleep(15000); - FreeSemaphore(&Semaphore); - exit (0); - } - - if (FirstInitDone == 0) - { - GetSemaphore(&Semaphore, 3); - - if (_stricmp(pgm, "bpq32.exe") == 0) - { - FirstInit(); - FreeSemaphore(&Semaphore); - if (NUMBEROFTNCPORTS) - InitializeTNCEmulator(); - - AGWActive = AGWAPIInit(); - FirstInitDone=1; // Only init in BPQ32.exe - return 0; - } - else - { - FreeSemaphore(&Semaphore); - return 0; - } - } - - if (TimerHandle == 0 && FirstInitDone == 1) - { - WSADATA WsaData; // receives data from WSAStartup - HINSTANCE ExtDriver=0; - RECT cRect; - - // Only attach timer to bpq32.exe - - if (_stricmp(pgm, "bpq32.exe") != 0) - { - return 0; - } - - GetSemaphore(&Semaphore, 3); - OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); - - if (!ProcessConfig()) - { - ShowWindow(hConsWnd, SW_RESTORE); - SendMessage(hConsWnd, WM_PAINT, 0, 0); - SetForegroundWindow(hConsWnd); - - InitDone = (void *)-1; - FreeSemaphore(&Semaphore); - - MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); - - exit (0); - } - - GetVersionInfo("bpq32.dll"); - - SetupConsoleWindow(); - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - Consoleprintf("Reinitialising..."); - - SetupBPQDirectory(); - - Sleep(1000); // Allow time for sockets to close - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - // Load Psapi.dll if possible - - ExtDriver = LoadLibrary("Psapi.dll"); - - SetupTrayIcon(); - - if (ExtDriver) - { - GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); - EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); - } - - Start(); - - INITIALISEPORTS(); - - OpenReportingSockets(); - - NODESINPROGRESS = 0; - CURRENTNODE = 0; - - SetApplPorts(); - - WritetoConsole("\n\nPort Reinitialisation Complete\n"); - - BPQMsg = RegisterWindowMessage(BPQWinMsg); - - CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); - -// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", -// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); - - if (IPRequired) IPActive = Init_IP(); - if (PMRequired) PMActive = Init_PM(); - - RigActive = Rig_Init(); - APRSActive = Init_APRS(); - - if (ISPort == 0) - IGateEnabled = 0; - - CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); - - GetClientRect(hConsWnd, &cRect); - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - { - ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - } - InvalidateRect(hConsWnd, NULL, TRUE); - - FreeConfig(); - - _beginthread(MonitorThread,0,0); - - ReportTimer = 0; - - OpenReportingSockets(); - - FreeSemaphore(&Semaphore); - - if (NUMBEROFTNCPORTS) - InitializeTNCEmulator(); - - AGWActive = AGWAPIInit(); - - if (StartMinimized) - if (MinimizetoTray) - ShowWindow(FrameWnd, SW_HIDE); - else - ShowWindow(FrameWnd, SW_SHOWMINIMIZED); - else - ShowWindow(FrameWnd, SW_RESTORE); - - TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); - TimerInst=GetCurrentProcessId(); - SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); - - return (1); - } - - return (0); -} - -DllExport INT APIENTRY CheckTimer() -{ - return Check_Timer(); -} - -Tell_Sessions() -{ - // - // Post a message to all listening sessions, so they call the - // API, and cause a new timer to be allocated - // - HWND hWnd; - int i; - - for (i=1;i<65;i++) - { - if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) - { - hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; - PostMessage(hWnd, BPQMsg,i, 1); - PostMessage(hWnd, BPQMsg,i, 2); - } - } - return (0); -} - -BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) -{ - DWORD n; - char buf[350]; - - int i; - unsigned int ProcessID; - - OSVERSIONINFO osvi; - - memset(&osvi, 0, sizeof(OSVERSIONINFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - GetVersionEx(&osvi); - - - switch( ul_reason_being_called ) - { - case DLL_PROCESS_ATTACH: - - if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); - return 0; - } - - if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); - return 0; - } - - if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); - return 0; - } - - if (sizeof(LINKTABLE) != LINK_TABLE_LEN) - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); - return 0; - } - if (sizeof(struct ROUTE) != ROUTE_LEN) - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); - return 0; - } - - if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) - { - // Catastrophic - Refuse to load - - MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); - return 0; - } - - GetSemaphore(&Semaphore, 4); - - BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; - - LoadToolHelperRoutines(); - - Our_PID = GetCurrentProcessId(); - - QueryPerformanceFrequency(&lpFrequency); - - ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; - - lastRunTime.QuadPart = lpFrequency.QuadPart; - - GetProcess(Our_PID, pgm); - - if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) - { - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 1; - } - - if (_stricmp(pgm,"BPQ32.exe") == 0) - BPQ32_EXE = TRUE; - - if (_stricmp(pgm,"BPQMailChat.exe") == 0) - IncludesMail = TRUE; - - if (_stricmp(pgm,"BPQMail.exe") == 0) - IncludesMail = TRUE; - - if (_stricmp(pgm,"BPQChat.exe") == 0) - IncludesChat = TRUE; - - if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end - { - FirstEntry = 0; - if (BPQ32_EXE) - CloseLast = FALSE; - } - else - { - if (BPQ32_EXE && AttachingProcess == 0) - { - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); - return 0; - } - } - - if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) - { - MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 0; - } - - if (_stricmp(pgm,"BPQUIUtil.exe") == 0) - { - MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 0; - } - - if (_stricmp(pgm,"BPQMailChat.exe") == 0) - { - MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); - AttachedProcesses++; // We will get a detach - FreeSemaphore(&Semaphore); - return 0; - } - AuthorisedProgram = TRUE; - - if (InitDone == 0) - { -// #pragma warning(push) -// #pragma warning(disable : 4996) - -// if (_winver < 0x0600) -// #pragma warning(pop) -// { -// // Below Vista -// -// REGTREE = HKEY_LOCAL_MACHINE; -// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); -// } - - hInstance=hInst; - - Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); - - if (Mutex != NULL) - { - OutputDebugString("Another BPQ32.dll is loaded\n"); - i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); - FreeSemaphore(&Semaphore); - - if (i != IDOK) return (0); - - CloseHandle(Mutex); - } - - if (!BPQ32_EXE) - { - if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed - { - // Wasn't Loaded, so we have started it, and should let it init system - - goto SkipInit; - } - } - - GetVersionInfo("bpq32.dll"); - - sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", - TextVerstring, Datestring, VerCopyright); - - SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); - - SetupConsoleWindow(); - SetupBPQDirectory(); - - if (!ProcessConfig()) - { - StartMinimized = FALSE; - MinimizetoTray = FALSE; - ShowWindow(FrameWnd, SW_MAXIMIZE); - ShowWindow(hConsWnd, SW_MAXIMIZE); - ShowWindow(StatusWnd, SW_HIDE); - - SendMessage(hConsWnd, WM_PAINT, 0, 0); - SetForegroundWindow(hConsWnd); - - InitDone = (void *)-1; - FreeSemaphore(&Semaphore); - - MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); - - return (0); - } - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - - if (Start() !=0) - { - Sleep(3000); - FreeSemaphore(&Semaphore); - return (0); - } - else - { - SetApplPorts(); - - GetUIConfig(); - - InitDone = &InitDone; - BPQMsg = RegisterWindowMessage(BPQWinMsg); -// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); -// TimerInst=GetCurrentProcessId(); - -/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); - - if (Mutex != NULL) - { - OutputDebugString("Another BPQ32.dll is loaded\n"); - MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); - FreeSemaphore(&Semaphore); - return (0); - } - -*/ - Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); - -// CreatePipe(&H1,&H2,NULL,1000); - -// GetLastError(); - -// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", -// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); - -// GetLastError(); - -/* - // - // Read SYSOP password - // - - if (PWTEXT[0] == 0) - { - handle = OpenConfigFile("PASSWORD.BPQ"); - - if (handle == INVALID_HANDLE_VALUE) - { - WritetoConsole("Can't open PASSWORD.BPQ\n"); - PWLen=0; - PWTEXT[0]=0; - } - else - { - ReadFile(handle,PWTEXT,78,&n,NULL); - CloseHandle(handle); - } - } -*/ - for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null - PWLen=i; - - } - } - else - { - if (InitDone != &InitDone) - { - MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); - FreeSemaphore(&Semaphore); - return (0); - } - } - - // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread -SkipInit: - - _beginthread(MonitorTimerThread,0,0); - - FreeSemaphore(&Semaphore); - - AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); - - if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; - - GetProcess(GetCurrentProcessId(),pgm); - n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); - OutputDebugString(buf); - - // Set up local variables - - MCOM=1; - MTX=1; - MMASK=0xffffffffffffffff; - -// if (StartMinimized) -// if (MinimizetoTray) -// ShowWindow(FrameWnd, SW_HIDE); -// else -// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); -// else -// ShowWindow(FrameWnd, SW_RESTORE); - - return 1; - - case DLL_THREAD_ATTACH: - - return 1; - - case DLL_THREAD_DETACH: - - return 1; - - case DLL_PROCESS_DETACH: - - if (_stricmp(pgm,"BPQMailChat.exe") == 0) - IncludesMail = FALSE; - - if (_stricmp(pgm,"BPQChat.exe") == 0) - IncludesChat = FALSE; - - ProcessID=GetCurrentProcessId(); - - Debugprintf("BPQ32 Process %d Detaching", ProcessID); - - // Release any streams that the app has failed to release - - for (i=1;i<65;i++) - { - if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) - { - // If connected, disconnect - - SessionControl(i, 2, 0); - DeallocateStream(i); - } - } - - // Remove any Tray Icon Entries - - for( i = 0; i < 100; ++i ) - { - if (PIDArray[i] == ProcessID) - { - char Log[80]; - hWndArray[i] = 0; - sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); - OutputDebugString(Log); - DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); - } - } - - if (Mutex) CloseHandle(Mutex); - - // Remove our entry from PID List - - for (i=0; i< AttachedProcesses; i++) - if (AttachedPIDList[i] == ProcessID) - break; - - for (; i< AttachedProcesses; i++) - { - AttachedPIDList[i]=AttachedPIDList[i+1]; - } - - AttachedProcesses--; - - if (TimerInst == ProcessID) - { - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - - OutputDebugString("BPQ32 Process with Timer closing\n"); - - // Call Port Close Routines - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! - { - SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports - } - } - - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - - IPClose(); - PMClose(); - APRSClose(); - Rig_Close(); - CloseTNCEmulator(); - if (AGWActive) - AGWAPITerminate(); - - upnpClose(); - - WSACleanup(); - WSAGetLastError(); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - if (hConsWnd) DestroyWindow(hConsWnd); - - KillTimer(NULL,TimerHandle); - TimerHandle=0; - TimerInst=0xffffffff; - - if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes - { - OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); - StartBPQ32(); - } - } - else - { - // Not Timer Process - - if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left - { - Debugprintf("Only BPQ32.exe running - close it"); - CloseAllNeeded = TRUE; - } - } - - if (AttachedProcesses < 2) - { - if (AUTOSAVE) - SaveNodes(); - if (AUTOSAVEMH) - SaveMH(); - - if (needAIS) - SaveAIS(); - } - if (AttachedProcesses == 0) - { - Closing = TRUE; - KillTimer(NULL,TimerHandle); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - // Unload External Drivers - - { - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) - FreeLibrary(PORTVEC->DLLhandle); - - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - } - } - - GetProcess(GetCurrentProcessId(),pgm); - n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); - OutputDebugString(buf); - - return 1; - } - return 1; -} - -DllExport int APIENTRY CloseBPQ32() -{ - // Unload External Drivers - - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - int i; - int ProcessID = GetCurrentProcessId(); - - if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) - { - OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); - Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, - Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); - - Semaphore.Flag = 0; - SemHeldByAPI = 0; - } - - if (TimerInst == ProcessID) - { - OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); - } - } - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - KillTimer(NULL,TimerHandle); - TimerHandle=0; - TimerInst=0xffffffff; - - IPClose(); - PMClose(); - APRSClose(); - Rig_Close(); - if (AGWActive) - AGWAPITerminate(); - - upnpClose(); - - CloseTNCEmulator(); - WSACleanup(); - - if (hConsWnd) DestroyWindow(hConsWnd); - - Debugprintf("AttachedProcesses %d ", AttachedProcesses); - - if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes - { - OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); - StartBPQ32(); - } - } - - return 0; -} - -BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); - -VOID SetupBPQDirectory() -{ - HKEY hKey = 0; - HKEY hKeyIn = 0; - HKEY hKeyOut = 0; - int disp; - int retCode,Type,Vallen=MAX_PATH,i; - char msg[512]; - char ValfromReg[MAX_PATH] = ""; - char DLLName[256]="Not Known"; - char LogDir[256]; - char Time[64]; - -/* -•NT4 was/is '4' -•Win 95 is 4.00.950 -•Win 98 is 4.10.1998 -•Win 98 SE is 4.10.2222 -•Win ME is 4.90.3000 -•2000 is NT 5.0.2195 -•XP is actually 5.1 -•Vista is 6.0 -•Win7 is 6.1 - - i = _osver; / Build - i = _winmajor; - i = _winminor; -*/ -/* -#pragma warning(push) -#pragma warning(disable : 4996) - -if (_winver < 0x0600) -#pragma warning(pop) - { - // Below Vista - - REGTREE = HKEY_LOCAL_MACHINE; - strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); - ValfromReg[0] = 0; - } - else -*/ - { - if (_stricmp(pgm, "regsvr32.exe") == 0) - { - Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); - } - else - { - // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER - - retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_READ, - &hKeyIn); - - retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); - - // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy - - Vallen = MAX_PATH; - retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); - - if (retCode != ERROR_SUCCESS) - if (hKeyIn) - CopyReg(hKeyIn, hKeyOut); - - RegCloseKey(hKeyIn); - RegCloseKey(hKeyOut); - } - } - - GetModuleFileName(hInstance,DLLName,256); - - BPQDirectory[0]=0; - - retCode = RegOpenKeyEx (REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_QUERY_VALUE, - &hKey); - - if (retCode == ERROR_SUCCESS) - { - // Try "BPQ Directory" - - Vallen = MAX_PATH; - retCode = RegQueryValueEx(hKey,"BPQ Directory",0, - &Type,(UCHAR *)&ValfromReg,&Vallen); - - if (retCode == ERROR_SUCCESS) - { - if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') - ValfromReg[0]=0; - } - - if (ValfromReg[0] == 0) - { - // BPQ Directory absent or = "" - try "Config File Location" - - Vallen = MAX_PATH; - - retCode = RegQueryValueEx(hKey,"Config File Location",0, - &Type,(UCHAR *)&ValfromReg,&Vallen); - - if (retCode == ERROR_SUCCESS) - { - if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') - ValfromReg[0]=0; - } - } - - if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); - - // Get StartMinimized and MinimizetoTray flags - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); - - ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); - - // Also get "BPQ Program Directory" - - ValfromReg[0] = 0; - Vallen = MAX_PATH; - - retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); - - if (retCode == ERROR_SUCCESS) - ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); - - // And Log Directory - - ValfromReg[0] = 0; - Vallen = MAX_PATH; - - retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); - - if (retCode == ERROR_SUCCESS) - ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); - - RegCloseKey(hKey); - } - - strcpy(ConfigDirectory, BPQDirectory); - - if (LogDirectory[0] == 0) - strcpy(LogDirectory, BPQDirectory); - - if (BPQProgramDirectory[0] == 0) - strcpy(BPQProgramDirectory, BPQDirectory); - - sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); - WritetoConsole(msg); - OutputDebugString(msg); - FormatTime3(Time, time(NULL)); - sprintf(msg,"Loaded %s\n", Time); - WritetoConsole(msg); - OutputDebugString(msg); - -#pragma warning(push) -#pragma warning(disable : 4996) - -#if _MSC_VER >= 1400 - -#define _winmajor 6 -#define _winminor 0 - -#endif - - i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); - -#pragma warning(pop) - - WritetoConsole(msg); - OutputDebugString(msg); - - i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); - WritetoConsole(&msg[6]); - msg[i-1]=0; - OutputDebugString(msg); - - // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, - // so will write the wrong tree on ) - - if (_stricmp(pgm, "regsvr32.exe") == 0) - { - Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); - } - else - { - retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); - - sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); - retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); - - RegCloseKey(hKey); - } - - // Make sure Logs Directory exists - - sprintf(LogDir, "%s/Logs", LogDirectory); - - CreateDirectory(LogDir, NULL); - - return; -} - -HANDLE OpenConfigFile(char *fn) -{ - HANDLE handle; - UCHAR Value[MAX_PATH]; - FILETIME LastWriteTime; - SYSTEMTIME Time; - char Msg[256]; - - - // If no directory, use current - if (BPQDirectory[0] == 0) - { - strcpy(Value,fn); - } - else - { - strcpy(Value,BPQDirectory); - strcat(Value,"\\"); - strcat(Value,fn); - } - - handle = CreateFile(Value, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - - GetFileTime(handle, NULL, NULL, &LastWriteTime); - FileTimeToSystemTime(&LastWriteTime, &Time); - - sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, - Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); - - OutputDebugString(Msg); - - return(handle); -} - -#ifdef _WIN64 -int BPQHOSTAPI() -{ - return 0; -} -#endif - - -DllExport int APIENTRY GETBPQAPI() -{ - return (int)BPQHOSTAPI; -} - -//DllExport UINT APIENTRY GETMONDECODE() -//{ -// return (UINT)MONDECODE; -//} - - -DllExport INT APIENTRY BPQAPI(int Fn, char * params) -{ - -/* -; -; BPQ HOST MODE SUPPORT CODE -; -; 22/11/95 -; -; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED -; (OS2 VERSION HAD UPSET KANT VERISON -; -; -*/ - - -/* - - BPQHOSTPORT: -; -; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS -; -; COMMANDS SUPPORTED ARE -; -; AH = 0 Get node/switch version number and description. On return -; AH='B',AL='P',BH='Q',BL=' ' -; DH = major version number and DL = minor version number. -; -; -; AH = 1 Set application mask to value in DL (or even DX if 16 -; applications are ever to be supported). -; -; Set application flag(s) to value in CL (or CX). -; whether user gets connected/disconnected messages issued -; by the node etc. -; -; -; AH = 2 Send frame in ES:SI (length CX) -; -; -; AH = 3 Receive frame into buffer at ES:DI, length of frame returned -; in CX. BX returns the number of outstanding frames still to -; be received (ie. after this one) or zero if no more frames -; (ie. this is last one). -; -; -; -; AH = 4 Get stream status. Returns: -; -; CX = 0 if stream disconnected or CX = 1 if stream connected -; DX = 0 if no change of state since last read, or DX = 1 if -; the connected/disconnected state has changed since -; last read (ie. delta-stream status). -; -; -; -; AH = 6 Session control. -; -; CX = 0 Conneect - _APPLMASK in DL -; CX = 1 connect -; CX = 2 disconnect -; CX = 3 return user to node -; -; -; AH = 7 Get buffer counts for stream. Returns: -; -; AX = number of status change messages to be received -; BX = number of frames queued for receive -; CX = number of un-acked frames to be sent -; DX = number of buffers left in node -; SI = number of trace frames queued for receive -; -;AH = 8 Port control/information. Called with a stream number -; in AL returns: -; -; AL = Radio port on which channel is connected (or zero) -; AH = SESSION TYPE BITS -; BX = L2 paclen for the radio port -; CX = L2 maxframe for the radio port -; DX = L4 window size (if L4 circuit, or zero) -; ES:DI = CALLSIGN - -;AH = 9 Fetch node/application callsign & alias. AL = application -; number: -; -; 0 = node -; 1 = BBS -; 2 = HOST -; 3 = SYSOP etc. etc. -; -; Returns string with alias & callsign or application name in -; user's buffer pointed to by ES:SI length CX. For example: -; -; "WORCS:G8TIC" or "TICPMS:G8TIC-10". -; -; -; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of -; length CX, is transmitted as a HDLC frame on the radio -; port (not stream) in AL. -; -; -; AH = 11 Get Trace (RAW Data) Frame into ES:DI, -; Length to CX, Timestamp to AX -; -; -; AH = 12 Update Switch. At the moment only Beacon Text may be updated -; DX = Function -; 1=update BT. ES:SI, Len CX = Text -; 2=kick off nodes broadcast -; -; AH = 13 Allocate/deallocate stream -; If AL=0, return first free stream -; If AL>0, CL=1, Allocate stream. If aleady allocated, -; return CX nonzero, else allocate, and return CX=0 -; If AL>0, CL=2, Release stream -; -; -; AH = 14 Internal Interface for IP Router -; -; Send frame - to NETROM L3 if DL=0 -; to L2 Session if DL<>0 -; -; -; AH = 15 Get interval timer - - -*/ - - - switch(Fn) - { - - case CHECKLOADED: - - params[0]=MAJORVERSION; - params[1]=MINORVERSION; - params[2]=QCOUNT; - - return (1); - } - return 0; -} - -DllExport int APIENTRY InitSwitch() -{ - return (0); -} - -/*DllExport int APIENTRY SwitchTimer() -{ - GetSemaphore((&Semaphore); - - TIMERINTERRUPT(); - - FreeSemaphore(&Semaphore); - - return (0); -} -*/ -DllExport int APIENTRY GetFreeBuffs() -{ -// Returns number of free buffers -// (BPQHOST function 7 (part)). - return (QCOUNT); -} - -DllExport UCHAR * APIENTRY GetNodeCall() -{ - return (&MYNODECALL); -} - - -DllExport UCHAR * APIENTRY GetNodeAlias() -{ - return (&MYALIASTEXT[0]); -} - -DllExport UCHAR * APIENTRY GetBBSCall() -{ - return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); -} - - -DllExport UCHAR * APIENTRY GetBBSAlias() -{ - return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); -} - -DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) -{ - if (Appl < 1 || Appl > NumberofAppls ) return; - - strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); -} - -BOOL UpdateNodesForApp(int Appl); - -DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) -{ - char Call[10]=" "; - int i; - - if (Appl < 1 || Appl > NumberofAppls ) return FALSE; - - i=strlen(NewCall); - - if (i > 10) i=10; - - strncpy(Call,NewCall,i); - - strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); - - if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; - - UpdateNodesForApp(Appl); - - return TRUE; - -} - -DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) -{ - char Call[10]=" "; - int i; - - if (Appl < 1 || Appl > NumberofAppls ) return FALSE; - - i=strlen(NewCall); - - if (i > 10) i=10; - - strncpy(Call,NewCall,i); - - strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); - - if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; - - UpdateNodesForApp(Appl); - - return TRUE; - -} - - - -DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) -{ - if (Appl < 1 || Appl > NumberofAppls ) return FALSE; - - APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; - - UpdateNodesForApp(Appl); - - return TRUE; - -} - - -BOOL UpdateNodesForApp(int Appl) -{ - int App=Appl-1; - int DestLen = sizeof (struct DEST_LIST); - int n = MAXDESTS; - - struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; - APPLCALLS * APPL=&APPLCALLTABLE[App]; - - if (DEST == NULL) - { - // No dest at the moment. If we have valid call and Qual, create an entry - - if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; - - if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; - - - GetSemaphore(&Semaphore, 5); - - DEST = DESTS; - - while (n--) - { - if (DEST->DEST_CALL[0] == 0) // Spare - break; - } - - if (n == 0) - { - // no dests - - FreeSemaphore(&Semaphore); - return FALSE; - } - - NUMBEROFNODES++; - APPL->NODEPOINTER = DEST; - - memmove (DEST->DEST_CALL,APPL->APPLCALL,13); - - DEST->DEST_STATE=0x80; // SPECIAL ENTRY - - DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; - DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; - - FreeSemaphore(&Semaphore); - - return TRUE; - } - - // We have a destination. If Quality is zero, remove it, else update it - - if (APPLCALLTABLE[App].APPLQUAL == 0) - { - GetSemaphore(&Semaphore, 6); - - REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry - - APPL->NODEPOINTER=NULL; - - FreeSemaphore(&Semaphore); - return FALSE; - - } - - if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; - - GetSemaphore(&Semaphore, 7); - - memmove (DEST->DEST_CALL,APPL->APPLCALL,13); - - DEST->DEST_STATE=0x80; // SPECIAL ENTRY - - DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; - DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; - - FreeSemaphore(&Semaphore); - return TRUE; - -} - - -DllExport UCHAR * APIENTRY GetSignOnMsg() -{ - return (&SIGNONMSG[0]); -} - - -DllExport HKEY APIENTRY GetRegistryKey() -{ - return REGTREE; -} - -DllExport char * APIENTRY GetRegistryKeyText() -{ - return REGTREETEXT;; -} - -DllExport UCHAR * APIENTRY GetBPQDirectory() -{ - while (BPQDirectory[0] == 0) - { - Debugprintf("BPQ Directory not set up - waiting"); - Sleep(1000); - } - return (&BPQDirectory[0]); -} - -DllExport UCHAR * APIENTRY GetProgramDirectory() -{ - return (&BPQProgramDirectory[0]); -} - -DllExport UCHAR * APIENTRY GetLogDirectory() -{ - return (&LogDirectory[0]); -} - -// Version for Visual Basic - -DllExport char * APIENTRY CopyBPQDirectory(char * dir) -{ - return (strcpy(dir,BPQDirectory)); -} - -DllExport int APIENTRY GetMsgPerl(int stream, char * msg) -{ - int len,count; - - GetMsg(stream, msg, &len, &count ); - - return len; -} - -int Rig_Command(int Session, char * Command); - -BOOL Rig_CommandInt(int Session, char * Command) -{ - return Rig_Command(Session, Command); -} - -DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) -{ - BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; - return (0); -} - -#define L4USER 0 - -BPQVECSTRUC * PORTVEC ; - -VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) -{ - HINSTANCE ExtDriver=0; - char msg[128]; - int err=0; - HKEY hKey=0; - UCHAR Value[MAX_PATH]; - - // If no directory, use current - - if (BPQDirectory[0] == 0) - { - strcpy(Value,PORTVEC->PORT_DLL_NAME); - } - else - { - strcpy(Value,BPQDirectory); - strcat(Value,"\\"); - strcat(Value,PORTVEC->PORT_DLL_NAME); - } - - // Several Drivers are now built into bpq32.dll - - _strupr(Value); - - if (strstr(Value, "BPQVKISS")) - return VCOMExtInit; - - if (strstr(Value, "BPQAXIP")) - return AXIPExtInit; - - if (strstr(Value, "BPQETHER")) - return ETHERExtInit; - - if (strstr(Value, "BPQTOAGW")) - return AGWExtInit; - - if (strstr(Value, "AEAPACTOR")) - return AEAExtInit; - - if (strstr(Value, "HALDRIVER")) - return HALExtInit; - - if (strstr(Value, "KAMPACTOR")) - return KAMExtInit; - - if (strstr(Value, "SCSPACTOR")) - return SCSExtInit; - - if (strstr(Value, "WINMOR")) - return WinmorExtInit; - - if (strstr(Value, "V4")) - return V4ExtInit; - - if (strstr(Value, "TELNET")) - return TelnetExtInit; - -// if (strstr(Value, "SOUNDMODEM")) -// return SoundModemExtInit; - - if (strstr(Value, "SCSTRACKER")) - return TrackerExtInit; - - if (strstr(Value, "TRKMULTI")) - return TrackerMExtInit; - - if (strstr(Value, "UZ7HO")) - return UZ7HOExtInit; - - if (strstr(Value, "MULTIPSK")) - return MPSKExtInit; - - if (strstr(Value, "FLDIGI")) - return FLDigiExtInit; - - if (strstr(Value, "UIARQ")) - return UIARQExtInit; - -// if (strstr(Value, "BAYCOM")) -// return (UINT) BaycomExtInit; - - if (strstr(Value, "VARA")) - return VARAExtInit; - - if (strstr(Value, "ARDOP")) - return ARDOPExtInit; - - if (strstr(Value, "SERIAL")) - return SerialExtInit; - - if (strstr(Value, "KISSHF")) - return KISSHFExtInit; - - if (strstr(Value, "WINRPR")) - return WinRPRExtInit; - - if (strstr(Value, "HSMODEM")) - return HSMODEMExtInit; - - if (strstr(Value, "FREEDATA")) - return FreeDataExtInit; - - if (strstr(Value, "6PACK")) - return SIXPACKExtInit; - - ExtDriver = LoadLibrary(Value); - - if (ExtDriver == NULL) - { - err=GetLastError(); - - sprintf(msg,"Error loading Driver %s - Error code %d", - PORTVEC->PORT_DLL_NAME,err); - - MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); - - return(0); - } - - PORTVEC->DLLhandle=ExtDriver; - - return (GetProcAddress(ExtDriver,"_ExtInit@4")); - -} - -/* -_DATABASE LABEL BYTE - -FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! - DB MAJORVERSION,MINORVERSION -_NEIGHBOURS DD 0 - DW TYPE ROUTE -_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES - -_DESTS DD 0 ; NODE LIST - DW TYPE DEST_LIST -MAXDESTS DW 100 ; MAX NODES IN SYSTEM -*/ - - -DllExport int APIENTRY GetAttachedProcesses() -{ - return (AttachedProcesses); -} - -DllExport int * APIENTRY GetAttachedProcessList() -{ - return (&AttachedPIDList[0]); -} - -DllExport int * APIENTRY SaveNodesSupport() -{ - return (&DATABASESTART); -} - -// -// Internal BPQNODES support -// - -#define UCHAR unsigned char - -/* -ROUTE ADD G1HTL-1 2 200 0 0 0 -ROUTE ADD G4IRX-3 2 200 0 0 0 -NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 -NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 - -*/ - -struct DEST_LIST * Dests; -struct ROUTE * Routes; - -int MaxNodes; -int MaxRoutes; -int NodeLen; -int RouteLen; - -int count; -int cursor; - -int len,i; - -ULONG cnt; -char Normcall[10]; -char Portcall[10]; -char Alias[7]; - -char line[100]; - -HANDLE handle; - -int APIENTRY Restart() -{ - int i, Count = AttachedProcesses; - HANDLE hProc; - DWORD PID; - - for (i = 0; i < Count; i++) - { - PID = AttachedPIDList[i]; - - // Kill Timer Owner last - - if (TimerInst != PID) - { - hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); - - if (hProc) - { - TerminateProcess(hProc, 0); - CloseHandle(hProc); - } - } - } - - hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); - - if (hProc) - { - TerminateProcess(hProc, 0); - CloseHandle(hProc); - } - - - return 0; -} - -int APIENTRY Reboot() -{ - // Run shutdown -r -f - - STARTUPINFO SInfo; - PROCESS_INFORMATION PInfo; - char Cmd[] = "shutdown -r -f"; - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); -} -/* -int APIENTRY Reconfig() -{ - if (!ProcessConfig()) - { - return (0); - } - SaveNodes(); - WritetoConsole("Nodes Saved\n"); - ReconfigFlag=TRUE; - WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); - return 1; -} -*/ -// Code to support minimizing all BPQ Apps to a single Tray ICON - -// As we can't minimize the console window to the tray, I'll use an ordinary -// window instead. This also gives me somewhere to post the messages to - - -char AppName[] = "BPQ32"; -char Title[80] = "BPQ32.dll Console"; - -int NewLine(); - -char FrameClassName[] = TEXT("MdiFrame"); - -HWND ClientWnd; //This stores the MDI client area window handle - -LOGFONT LFTTYFONT ; - -HFONT hFont ; - -HMENU hPopMenu, hWndMenu; -HMENU hMainFrameMenu = NULL; -HMENU hBaseMenu = NULL; -HMENU hConsMenu = NULL; -HMENU hTermMenu = NULL; -HMENU hMonMenu = NULL; -HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; -HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; - - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); - -#define BPQMonitorAvail 1 -#define BPQDataAvail 2 -#define BPQStateChange 4 - -VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); -SOCKET OpenWL2KHTTPSock(); -SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); - -BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); -BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); - - -static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_INITDIALOG: - { - char _REPLYBUFFER[1000] = ""; - char Value[1000]; - - if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) - { -// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) - - GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); - SetDlgItemText(hDlg, NAME, Value); - - GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); - SetDlgItemText(hDlg, IDC_Locator, Value); - - GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); - SetDlgItemText(hDlg, ADDR1, Value); - - GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); - SetDlgItemText(hDlg, ADDR2, Value); - - GetJSONValue(_REPLYBUFFER, "\"City\":", Value); - SetDlgItemText(hDlg, CITY, Value); - - GetJSONValue(_REPLYBUFFER, "\"State\":", Value); - SetDlgItemText(hDlg, STATE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); - SetDlgItemText(hDlg, COUNTRY, Value); - - GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); - SetDlgItemText(hDlg, POSTCODE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); - SetDlgItemText(hDlg, EMAIL, Value); - - GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); - SetDlgItemText(hDlg, WEBSITE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); - SetDlgItemText(hDlg, PHONE, Value); - - GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); - SetDlgItemText(hDlg, ADDITIONALDATA, Value); - - } - - return (INT_PTR)TRUE; - } - case WM_COMMAND: - - switch(LOWORD(wParam)) - { - - case ID_SAVE: - { - char Name[100]; - char PasswordText[100]; - char LocatorText[100]; - char Addr1[100]; - char Addr2[100]; - char City[100]; - char State[100]; - char Country[100]; - char PostCode[100]; - char Email[100]; - char Website[100]; - char Phone[100]; - char Data[100]; - - SOCKET sock; - - int Len; - char Message[2048]; - char Reply[2048] = ""; - - - GetDlgItemText(hDlg, NAME, Name, 99); - GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); - GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); - GetDlgItemText(hDlg, ADDR1, Addr1, 99); - GetDlgItemText(hDlg, ADDR2, Addr2, 99); - GetDlgItemText(hDlg, CITY, City, 99); - GetDlgItemText(hDlg, STATE, State, 99); - GetDlgItemText(hDlg, COUNTRY, Country, 99); - GetDlgItemText(hDlg, POSTCODE, PostCode, 99); - GetDlgItemText(hDlg, EMAIL, Email, 99); - GetDlgItemText(hDlg, WEBSITE, Website, 99); - GetDlgItemText(hDlg, PHONE, Phone, 99); - GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); - - -//{"Callsign":"String","GridSquare":"String","SysopName":"String", -//"StreetAddress1":"String","StreetAddress2":"String","City":"String", -//"State":"String","Country":"String","PostalCode":"String","Email":"String", -//"Phones":"String","Website":"String","Comments":"String"} - - Len = sprintf(Message, - "\"Callsign\":\"%s\"," - "\"Password\":\"%s\"," - "\"GridSquare\":\"%s\"," - "\"SysopName\":\"%s\"," - "\"StreetAddress1\":\"%s\"," - "\"StreetAddress2\":\"%s\"," - "\"City\":\"%s\"," - "\"State\":\"%s\"," - "\"Country\":\"%s\"," - "\"PostalCode\":\"%s\"," - "\"Email\":\"%s\"," - "\"Phones\":\"%s\"," - "\"Website\":\"%s\"," - "\"Comments\":\"%s\"", - - WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); - - Debugprintf("Sending %s", Message); - - sock = OpenWL2KHTTPSock(); - - if (sock) - { - char * ptr; - - SendHTTPRequest(sock, - "/sysop/add", Message, Len, Reply); - - ptr = strstr(Reply, "\"ErrorCode\":"); - - if (ptr) - { - ptr = strstr(ptr, "Message"); - if (ptr) - { - ptr += 10; - strlop(ptr, '"'); - MessageBox(NULL ,ptr, "Error", MB_OK); - } - } - else - MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); - - } - closesocket(sock); - } - - case ID_CANCEL: - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - break; - } - } - return (INT_PTR)FALSE; -} - - - -LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -VOID WINAPI OnTabbedDialogInit(HWND hDlg); - -LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - POINT pos; - BOOL ret; - - CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area - //HWND m_hwndSystemInformation = 0; - - if (message == BPQMsg) - { - if (lParam & BPQDataAvail) - DoReceivedData(wParam); - - if (lParam & BPQMonitorAvail) - DoMonData(wParam); - - if (lParam & BPQStateChange) - DoStateChange(wParam); - - return (0); - } - - switch (message) - { - case MY_TRAY_ICON_MESSAGE: - - switch(lParam) - { - case WM_RBUTTONUP: - case WM_LBUTTONUP: - - GetCursorPos(&pos); - - // SetForegroundWindow(FrameWnd); - - TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); - return 0; - } - - break; - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - case WM_SIZING: - case WM_SIZE: - - SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); - break; - - case WM_NCCREATE: - - ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); - return TRUE; - - case WM_CREATE: - - // On creation of main frame, create the MDI client area - - MDIClientCreateStruct.hWindowMenu = NULL; - MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; - - ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area - NULL, // no caption required - WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, - 0, // No need to give any x/y or height/width since this client - // will just be used to get client windows created, effectively - // in the main window we will be seeing the mainframe window client area itself. - 0, - 0, - 0, - hWnd, - NULL, - hInstance, - (void *) &MDIClientCreateStruct); - - - return 0; - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) - { - handle=hWndArray[wmId-TRAYBASEID]; - - if (handle == FrameWnd) - ShowWindow(handle, SW_NORMAL); - - if (handle == FrameWnd && FrameMaximized == TRUE) - PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - else - PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); - - SetForegroundWindow(handle); - return 0; - } - - switch(wmId) - { - struct ConsoleInfo * Cinfo = NULL; - - case ID_NEWWINDOW: - Cinfo = CreateChildWindow(0, FALSE); - if (Cinfo) - SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); - break; - - case ID_WINDOWS_CASCADE: - SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); - return 0; - - case ID_WINDOWS_TILE: - SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); - return 0; - - case BPQCLOSEALL: - CloseAllPrograms(); - // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); - - return 0; - - case BPQUICONFIG: - { - int err, i=0; - char Title[80]; - WNDCLASS wc; - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = UIWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL; - wc.lpszClassName = UIClassName; - - RegisterClass(&wc); - - UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); - - if (!UIhWnd) - { - err=GetLastError(); - return FALSE; - } - - wsprintf(Title,"BPQ32 Beacon Configuration"); - MySetWindowText(UIhWnd, Title); - ShowWindow(UIhWnd, SW_NORMAL); - - OnTabbedDialogInit(UIhWnd); // Set up pages - - // UpdateWindow(UIhWnd); - return 0; - } - - - case IDD_WL2KSYSOP: - - if (WL2KCall[0] == 0) - { - MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); - break; - } - - DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); - break; - - - // Handle MDI Window commands - - default: - { - if(wmId >= IDM_FIRSTCHILD) - { - DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); - } - else - { - HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); - - if(hChild) - SendMessage(hChild, WM_COMMAND, wParam, lParam); - } - } - } - - break; - - case WM_INITMENUPOPUP: - { - HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); - - if(hChild) - SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MAXIMIZE: - - FrameMaximized = TRUE; - break; - - case SC_RESTORE: - - FrameMaximized = FALSE; - break; - - case SC_MINIMIZE: - - if (MinimizetoTray) - { - ShowWindow(hWnd, SW_HIDE); - return TRUE; - } - } - - return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); - - case WM_CLOSE: - - PostQuitMessage(0); - - if (MinimizetoTray) - DeleteTrayMenuItem(hWnd); - - break; - - default: - return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); - - } - return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); -} - -int OffsetH, OffsetW; - -int SetupConsoleWindow() -{ - WNDCLASS wc; - int i; - int retCode, Type, Vallen; - HKEY hKey=0; - char Size[80]; - WNDCLASSEX wndclassMainFrame; - RECT CRect; - - retCode = RegOpenKeyEx (REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_QUERY_VALUE, - &hKey); - - if (retCode == ERROR_SUCCESS) - { - Vallen=80; - - retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); - - if (FRect.top < - 500 || FRect.left < - 500) - { - FRect.left = 0; - FRect.top = 0; - FRect.right = 600; - FRect.bottom = 400; - } - - - Vallen=80; - retCode = RegQueryValueEx(hKey,"WindowSize",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); - - if (Rect.top < - 500 || Rect.left < - 500) - { - Rect.left = 0; - Rect.top = 0; - Rect.right = 600; - Rect.bottom = 400; - } - - Vallen=80; - - retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, - (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); - - if (retCode == ERROR_SUCCESS) - sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, - &StatusRect.top, &StatusRect.bottom, &StatusMinimized); - - if (StatusRect.top < - 500 || StatusRect.left < - 500) - { - StatusRect.left = 0; - StatusRect.top = 0; - StatusRect.right = 850; - StatusRect.bottom = 500; - } - - - // Get StartMinimized and MinimizetoTray flags - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); - - Vallen = 4; - retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); - } - - wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); - wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wndclassMainFrame.lpfnWndProc = FrameWndProc; - wndclassMainFrame.cbClsExtra = 0; - wndclassMainFrame.cbWndExtra = 0; - wndclassMainFrame.hInstance = hInstance; - wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); - wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); - wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); - wndclassMainFrame.lpszMenuName = NULL; - wndclassMainFrame.lpszClassName = FrameClassName; - wndclassMainFrame.hIconSm = NULL; - - if(!RegisterClassEx(&wndclassMainFrame)) - { - return 0; - } - - pindex = 0; - PartLine = FALSE; - - bgBrush = CreateSolidBrush(BGCOLOUR); - -// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); - - hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); - hConsMenu = GetSubMenu(hBaseMenu, 1); - hWndMenu = GetSubMenu(hBaseMenu, 0); - - hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); - hTermActMenu = GetSubMenu(hTermMenu, 1); - hTermCfgMenu = GetSubMenu(hTermMenu, 2); - hTermEdtMenu = GetSubMenu(hTermMenu, 3); - hTermHlpMenu = GetSubMenu(hTermMenu, 4); - - hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); - hMonCfgMenu = GetSubMenu(hMonMenu, 1); - hMonEdtMenu = GetSubMenu(hMonMenu, 2); - hMonHlpMenu = GetSubMenu(hMonMenu, 3); - - hMainFrameMenu = CreateMenu(); - AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); - - //Create the main MDI frame window - - ClientWnd = NULL; - - FrameWnd = CreateWindow(FrameClassName, - "BPQ32 Console", - WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, - FRect.left, - FRect.top, - FRect.right - FRect.left, - FRect.bottom - FRect.top, - NULL, // handle to parent window - hMainFrameMenu, // handle to menu - hInstance, // handle to the instance of module - NULL); // Long pointer to a value to be passed to the window through the - // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message - - - // Get Client Params - - if (FrameWnd == 0) - { - Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); - return 0; - } - - ShowWindow(FrameWnd, SW_RESTORE); - - - GetWindowRect(FrameWnd, &FRect); - OffsetH = FRect.bottom - FRect.top; - OffsetW = FRect.right - FRect.left; - GetClientRect(FrameWnd, &CRect); - OffsetH -= CRect.bottom; - OffsetW -= CRect.right; - OffsetH -= 4; - - // Create Console Window - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wc.lpfnWndProc = (WNDPROC)WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wc.lpszMenuName = 0; - wc.lpszClassName = ClassName; - - i=RegisterClass(&wc); - - sprintf (Title, "BPQ32.dll Console Version %s", VersionString); - - hConsWnd = CreateMDIWindow(ClassName, "Console", 0, - 0,0,0,0, ClientWnd, hInstance, 1234); - - i = GetLastError(); - - if (!hConsWnd) { - return (FALSE); - } - - wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - wc.lpfnWndProc = (WNDPROC)StatusWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wc.lpszMenuName = 0; - wc.lpszClassName = "Status"; - - i=RegisterClass(&wc); - - if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame - { - int Error = OffsetH - StatusRect.top; - StatusRect.top += Error; - StatusRect.bottom += Error; - } - - StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, - StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, - StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); - - SetTimer(StatusWnd, 1, 1000, NULL); - - hPopMenu = GetSubMenu(hBaseMenu, 1) ; - - if (MinimizetoTray) - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); - - if (StartMinimized) - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); - - DrawMenuBar(hConsWnd); - - // setup default font information - - LFTTYFONT.lfHeight = 12; - LFTTYFONT.lfWidth = 8 ; - LFTTYFONT.lfEscapement = 0 ; - LFTTYFONT.lfOrientation = 0 ; - LFTTYFONT.lfWeight = 0 ; - LFTTYFONT.lfItalic = 0 ; - LFTTYFONT.lfUnderline = 0 ; - LFTTYFONT.lfStrikeOut = 0 ; - LFTTYFONT.lfCharSet = 0; - LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; - LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; - LFTTYFONT.lfQuality = DEFAULT_QUALITY ; - LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; - lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; - - hFont = CreateFontIndirect(&LFTTYFONT) ; - - SetWindowText(hConsWnd,Title); - - if (Rect.right < 100 || Rect.bottom < 100) - { - GetWindowRect(hConsWnd, &Rect); - } - - if (Rect.top < OffsetH) // Make sure not off top of MDI frame - { - int Error = OffsetH - Rect.top; - Rect.top += Error; - Rect.bottom += Error; - } - - - MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); - - MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, - StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); - - hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", - WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | - LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, - Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, - hConsWnd, NULL, hInstance, NULL); - -// SendMessage(hWndCons, WM_SETFONT, hFont, 0); - - SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); - - if (ConsoleMinimized) - ShowWindow(hConsWnd, SW_SHOWMINIMIZED); - else - ShowWindow(hConsWnd, SW_RESTORE); - - if (StatusMinimized) - ShowWindow(StatusWnd, SW_SHOWMINIMIZED); - else - ShowWindow(StatusWnd, SW_RESTORE); - - ShowWindow(FrameWnd, SW_RESTORE); - - - LoadLibrary("riched20.dll"); - - if (StartMinimized) - if (MinimizetoTray) - ShowWindow(FrameWnd, SW_HIDE); - else - ShowWindow(FrameWnd, SW_SHOWMINIMIZED); - else - ShowWindow(FrameWnd, SW_RESTORE); - - CreateMonitorWindow(Size); - - return 0; -} - -DllExport int APIENTRY SetupTrayIcon() -{ - if (MinimizetoTray == 0) - return 0; - - trayMenu = CreatePopupMenu(); - - for( i = 0; i < 100; ++i ) - { - if (strcmp(PopupText[i],"BPQ32 Console") == 0) - { - hWndArray[i] = FrameWnd; - goto doneit; - } - } - - for( i = 0; i < 100; ++i ) - { - if (hWndArray[i] == 0) - { - hWndArray[i] = FrameWnd; - strcpy(PopupText[i],"BPQ32 Console"); - break; - } - } -doneit: - - for( i = 0; i < 100; ++i ) - { - if (hWndArray[i] != 0) - AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); - } - - // Set up Tray ICON - - ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); - - niData.cbSize = sizeof(NOTIFYICONDATA); - - // the ID number can be any UINT you choose and will - // be used to identify your icon in later calls to - // Shell_NotifyIcon - - niData.uID = TRAY_ICON_ID; - - // state which structure members are valid - // here you can also choose the style of tooltip - // window if any - specifying a balloon window: - // NIF_INFO is a little more complicated - - strcpy(niData.szTip,"BPQ32 Windows"); - - niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; - - // load the icon note: you should destroy the icon - // after the call to Shell_NotifyIcon - - niData.hIcon = - - //LoadIcon(NULL, IDI_APPLICATION); - - (HICON)LoadImage( hInstance, - MAKEINTRESOURCE(BPQICON), - IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON), - LR_DEFAULTCOLOR); - - - // set the window you want to receive event messages - - niData.hWnd = FrameWnd; - - // set the message to send - // note: the message value should be in the - // range of WM_APP through 0xBFFF - - niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; - - // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon - - if (Shell_NotifyIcon(NIM_ADD,&niData)) - Debugprintf("BPQ32 Create Tray Icon Ok"); -// else -// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); - - return 0; -} - -VOID SaveConfig() -{ - HKEY hKey=0; - int retCode, disp; - - retCode = RegCreateKeyEx(REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, // Reserved - 0, // Class - 0, // Options - KEY_ALL_ACCESS, - NULL, // Security Attrs - &hKey, - &disp); - - if (retCode == ERROR_SUCCESS) - { - retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); - retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); - } -} - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - POINT pos; - HWND handle; - RECT cRect; - - switch (message) - { - case WM_MDIACTIVATE: - - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); - } - else - { - // Deactivate - - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); - } - - DrawMenuBar(FrameWnd); - - return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); - - case MY_TRAY_ICON_MESSAGE: - - switch(lParam) - { - case WM_RBUTTONUP: - case WM_LBUTTONUP: - - GetCursorPos(&pos); - - SetForegroundWindow(hWnd); - - TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); - return 0; - } - - break; - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - if (wmId == IDC_ENIGATE) - { - int retCode, disp; - HKEY hKey=0; - - IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); - - if (IGateEnabled) - ISDelayTimer = 60; - - retCode = RegCreateKeyEx(REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, // Reserved - 0, // Class - 0, // Options - KEY_ALL_ACCESS, - NULL, // Security Attrs - &hKey, - &disp); - - if (retCode == ERROR_SUCCESS) - { - retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); - RegCloseKey(hKey); - } - - return 0; - } - - if (wmId == BPQSAVENODES) - { - SaveNodes(); - WritetoConsole("Nodes Saved\n"); - return 0; - } - if (wmId == BPQCLEARRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - - ClearNodes(); - WritetoConsole("Nodes file Cleared\n"); - ReconfigFlag=TRUE; - WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - if (wmId == BPQRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - SaveNodes(); - WritetoConsole("Nodes Saved\n"); - ReconfigFlag=TRUE; - WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - - if (wmId == SCANRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - - RigReconfigFlag = TRUE; - WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - - if (wmId == APRSRECONFIG) - { - if (!ProcessConfig()) - { - MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); - return (0); - } - - APRSReconfigFlag=TRUE; - WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); - return 0; - } - if (wmId == BPQDUMP) - { - DumpSystem(); - return 0; - } - - if (wmId == BPQCLOSEALL) - { - CloseAllPrograms(); - return 0; - } - - if (wmId == BPQUICONFIG) - { - int err, i=0; - char Title[80]; - WNDCLASS wc; - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = UIWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = DLGWINDOWEXTRA; - wc.hInstance = hInstance; - wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = bgBrush; - - wc.lpszMenuName = NULL; - wc.lpszClassName = UIClassName; - - RegisterClass(&wc); - - UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); - - if (!UIhWnd) - { - err=GetLastError(); - return FALSE; - } - - wsprintf(Title,"BPQ32 Beacon Utility Version"); - MySetWindowText(UIhWnd, Title); - return 0; - } - - if (wmId == BPQSAVEREG) - { - CreateRegBackup(); - return 0; - } - - if (wmId == BPQMINTOTRAY) - { - MinimizetoTray = !MinimizetoTray; - - if (MinimizetoTray) - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); - - SaveConfig(); - return 0; - } - - if (wmId == BPQSTARTMIN) - { - StartMinimized = !StartMinimized; - - if (StartMinimized) - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); - else - CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); - - SaveConfig(); - return 0; - } - - if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) - { - handle=hWndArray[wmId-TRAYBASEID]; - - if (handle == FrameWnd && FrameMaximized == TRUE) - PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - else - PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); - - SetForegroundWindow(handle); - return 0; - } - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MINIMIZE: - - ConsoleMinimized = TRUE; - break; - - case SC_RESTORE: - - ConsoleMinimized = FALSE; - SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); - - break; - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - - case WM_SIZE: - - GetClientRect(hWnd, &cRect); - - MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); - - if (APRSActive) - MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); - else - MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); - -// InvalidateRect(hWnd, NULL, TRUE); - break; - -/* - case WM_PAINT: - - hdc = BeginPaint (hWnd, &ps); - - hOldFont = SelectObject( hdc, hFont) ; - - for (i=0; i 300) - len = 300; - - memcpy(&buffptr[2], buff, len + 1); - - C_Q_ADD(&WritetoConsoleQ, buffptr); - - return 0; -} - -int WritetoConsoleSupport(char * buff) -{ - - int len=strlen(buff); - char Temp[2000]= ""; - char * ptr; - - if (PartLine) - { - SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); - SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); - PartLine = FALSE; - } - - if ((strlen(Temp) + strlen(buff)) > 1990) - Temp[0] = 0; // Should never have anything this long - - strcat(Temp, buff); - - ptr = strchr(Temp, '\n'); - - if (ptr) - *ptr = 0; - else - PartLine = TRUE; - - pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); - return 0; - } - -DllExport VOID APIENTRY BPQOutputDebugString(char * String) -{ - OutputDebugString(String); - return; - } - -HANDLE handle; -char fn[]="BPQDUMP"; -ULONG cnt; -char * stack; -//char screen[1920]; -//COORD ReadCoord; - -#define DATABYTES 400000 - -extern UCHAR DATAAREA[]; - -DllExport int APIENTRY DumpSystem() -{ - char fn[200]; - char Msg[250]; - - sprintf(fn,"%s\\BPQDUMP",BPQDirectory); - - handle = CreateFile(fn, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - -#ifndef _WIN64 - - _asm { - - mov stack,esp - } - - WriteFile(handle,stack,128,&cnt,NULL); -#endif - -// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); - - WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); - - CloseHandle(handle); - - sprintf(Msg, "Dump to %s Completed\n", fn); - WritetoConsole(Msg); - - FindLostBuffers(); - - return (0); -} - -BOOLEAN CheckifBPQ32isLoaded() -{ - HANDLE Mutex; - - // See if BPQ32 is running - if we create it in the NTVDM address space by - // loading bpq32.dll it will not work. - - Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); - - if (Mutex == NULL) - { - if (AttachingProcess == 0) // Already starting BPQ32 - { - OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); - StartBPQ32(); - } - return FALSE; - } - - CloseHandle(Mutex); - - return TRUE; -} - -BOOLEAN StartBPQ32() -{ - UCHAR Value[100]; - - char bpq[]="BPQ32.exe"; - char *fn=(char *)&bpq; - HKEY hKey=0; - int ret,Type,Vallen=99; - - char Errbuff[100]; - char buff[20]; - - STARTUPINFO StartupInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION - - AttachingProcess = 1; - -// Get address of BPQ Directory - - Value[0]=0; - - ret = RegOpenKeyEx (REGTREE, - "SOFTWARE\\G8BPQ\\BPQ32", - 0, - KEY_QUERY_VALUE, - &hKey); - - if (ret == ERROR_SUCCESS) - { - ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); - - if (ret == ERROR_SUCCESS) - { - if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') - Value[0]=0; - } - - - if (Value[0] == 0) - { - - // BPQ Directory absent or = "" - "try Config File Location" - - ret = RegQueryValueEx(hKey,"BPQ Directory",0, - &Type,(UCHAR *)&Value,&Vallen); - - if (ret == ERROR_SUCCESS) - { - if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') - Value[0]=0; - } - - } - RegCloseKey(hKey); - } - - if (Value[0] == 0) - { - strcpy(Value,fn); - } - else - { - strcat(Value,"\\"); - strcat(Value,fn); - } - - StartupInfo.cb=sizeof(StartupInfo); - StartupInfo.lpReserved=NULL; - StartupInfo.lpDesktop=NULL; - StartupInfo.lpTitle=NULL; - StartupInfo.dwFlags=0; - StartupInfo.cbReserved2=0; - StartupInfo.lpReserved2=NULL; - - if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, - CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, - NULL,NULL,&StartupInfo,&ProcessInformation)) - { - ret=GetLastError(); - - _itoa(ret,buff,10); - - strcpy(Errbuff, "BPQ32 Load "); - strcat(Errbuff,Value); - strcat(Errbuff," failed "); - strcat(Errbuff,buff); - OutputDebugString(Errbuff); - AttachingProcess = 0; - return FALSE; - } - - return TRUE; -} - - -DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() -{ - return &IPHOSTVECTOR; -} - -DllExport UINT APIENTRY GETSENDNETFRAMEADDR() -{ - return (UINT)&SENDNETFRAME; -} - -DllExport VOID APIENTRY RelBuff(VOID * Msg) -{ - UINT * pointer, * BUFF = Msg; - - if (Semaphore.Flag == 0) - Debugprintf("ReleaseBuffer called without semaphore"); - - pointer = FREE_Q; - - *BUFF =(UINT)pointer; - - FREE_Q = BUFF; - - QCOUNT++; - - return; -} - -extern int MINBUFFCOUNT; - -DllExport VOID * APIENTRY GetBuff() -{ - UINT * Temp = Q_REM(&FREE_Q); - - if (Semaphore.Flag == 0) - Debugprintf("GetBuff called without semaphore"); - - if (Temp) - { - QCOUNT--; - - if (QCOUNT < MINBUFFCOUNT) - MINBUFFCOUNT = QCOUNT; - } - - return Temp; -} - - -VOID __cdecl Debugprintf(const char * format, ...) -{ - char Mess[10000]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(Mess, format, arglist); - strcat(Mess, "\r\n"); - OutputDebugString(Mess); - - return; -} - -unsigned short int compute_crc(unsigned char *buf, int txlen); - -extern SOCKADDR_IN reportdest; - -extern SOCKET ReportSocket; - -extern SOCKADDR_IN Chatreportdest; - -DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) -{ - unsigned short int crc = compute_crc(buff, txlen); - - crc ^= 0xffff; - - buff[txlen++] = (crc&0xff); - buff[txlen++] = (crc>>8); - - sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); -} - -VOID CreateRegBackup() -{ - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - char RegFileName[MAX_PATH]; - char Msg[80]; - HANDLE handle; - int len, written; - char RegLine[300]; - -// SHELLEXECUTEINFO sei; -// STARTUPINFO SInfo; -// PROCESS_INFORMATION PInfo; - - sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); - - // Keep 4 Generations - - strcpy(Backup2, RegFileName); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, RegFileName); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, RegFileName); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, RegFileName); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); //Move .bak to .bak.1 - - strcpy(Backup2, RegFileName); - strcat(Backup2, ".bak"); - - CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak - - handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (handle == INVALID_HANDLE_VALUE) - { - sprintf(Msg, "Failed to open Registry Save File\n"); - WritetoConsole(Msg); - return; - } - - len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); - WriteFile(handle, RegLine, len, &written, NULL); - - if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) - WritetoConsole("Registry Save complete\n"); - else - WritetoConsole("Registry Save failed\n"); - - CloseHandle(handle); - return ; -/* - - if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista - { - sprintf(cmd, - "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); - - ZeroMemory(&SInfo, sizeof(SInfo)); - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) - { - sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); - WritetoConsole(Msg); - return; - } - } - else - { - - sprintf(cmd, - "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); - - ZeroMemory(&sei, sizeof(sei)); - - sei.cbSize = sizeof(SHELLEXECUTEINFOW); - sei.hwnd = hWnd; - sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; - sei.lpVerb = "runas"; - sei.lpFile = "regedit.exe"; - sei.lpParameters = cmd; - sei.nShow = SW_SHOWNORMAL; - - if (!ShellExecuteEx(&sei)) - { - sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); - WritetoConsole(Msg); - return; - } - } - - sprintf(Msg, "Registry Save Initiated\n", fn); - WritetoConsole(Msg); - - return ; -*/ -} - -BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) -{ - struct TNCINFO * TNC = (struct TNCINFO *)lParam; - UINT ProcessId; - - GetWindowThreadProcessId(hwnd, &ProcessId); - - for (i=0; i< AttachedProcesses; i++) - { - if (AttachedPIDList[i] == ProcessId) - { - Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); - PostMessage(hwnd, WM_CLOSE, 1, 1); - // AttachedPIDList[i] = 0; // So we don't do it again - break; - } - } - - return (TRUE); -} -DllExport BOOL APIENTRY RestoreFrameWindow() -{ - return ShowWindow(FrameWnd, SW_RESTORE); -} - -DllExport VOID APIENTRY CreateNewTrayIcon() -{ - Shell_NotifyIcon(NIM_DELETE,&niData); - trayMenu = NULL; -} - -DllExport VOID APIENTRY CloseAllPrograms() -{ -// HANDLE hProc; - - // Close all attached BPQ32 programs - - Closing = TRUE; - - ShowWindow(FrameWnd, SW_RESTORE); - - GetWindowRect(FrameWnd, &FRect); - - SaveBPQ32Windows(); - CloseHostSessions(); - - if (AttachedProcesses == 1) - CloseBPQ32(); - - Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], - AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); - - if (MinimizetoTray) - Shell_NotifyIcon(NIM_DELETE,&niData); - - EnumWindows(EnumForCloseProc, (LPARAM)NULL); -} - -#define MAX_KEY_LENGTH 255 -#define MAX_VALUE_NAME 16383 -#define MAX_VALUE_DATA 65536 - -BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) -{ - TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name - DWORD cbName; // size of name string - TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name - DWORD cchClassName = MAX_PATH; // size of class string - DWORD cSubKeys=0; // number of subkeys - DWORD cbMaxSubKey; // longest subkey size - DWORD cchMaxClass; // longest class string - DWORD cValues; // number of values for key - DWORD cchMaxValue; // longest value name - DWORD cbMaxValueData; // longest value data - DWORD cbSecurityDescriptor; // size of security descriptor - FILETIME ftLastWriteTime; // last write time - - DWORD i, retCode; - - TCHAR achValue[MAX_VALUE_NAME]; - DWORD cchValue = MAX_VALUE_NAME; - - // Get the class name and the value count. - retCode = RegQueryInfoKey( - hKeyIn, // key handle - achClass, // buffer for class name - &cchClassName, // size of class string - NULL, // reserved - &cSubKeys, // number of subkeys - &cbMaxSubKey, // longest subkey size - &cchMaxClass, // longest class string - &cValues, // number of values for this key - &cchMaxValue, // longest value name - &cbMaxValueData, // longest value data - &cbSecurityDescriptor, // security descriptor - &ftLastWriteTime); // last write time - - // Enumerate the subkeys, until RegEnumKeyEx fails. - - if (cSubKeys) - { - Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); - - for (i=0; i 76) - { - len += sprintf(&RegLine[len], "\\\r\n", RegLine); - strcat(RegLine, "\\\r\n"); - WriteFile(hFile, RegLine, len, &written, NULL); - strcpy(RegLine, " "); - len = 2; - } - - len += sprintf(&RegLine[len], "%02x,", Value[k]); - } - RegLine[--len] = 0x0d; - RegLine[++len] = 0x0a; - len++; - - break; - - case REG_DWORD: //( 4 ) // 32-bit number -// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) - - memcpy(&Intval, Value, 4); - len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); - break; - - case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number - break; - case REG_LINK: //( 6 ) // Symbolic Link (unicode) - break; - case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings - - len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); - for (k = 1; k < ValLen; k++) - { - if (len > 76) - { - len += sprintf(&RegLine[len], "\\\r\n"); - WriteFile(hFile, RegLine, len, &written, NULL); - strcpy(RegLine, " "); - len = 2; - } - - len += sprintf(&RegLine[len], "%02x,", Value[k]); - if (len > 76) - { - len += sprintf(&RegLine[len], "\\\r\n"); - WriteFile(hFile, RegLine, len, &written, NULL); - strcpy(RegLine, " "); - } - len += sprintf(&RegLine[len], "00,"); - } - - RegLine[--len] = 0x0d; - RegLine[++len] = 0x0a; - len++; - break; - - case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map - break; - case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description - break; - case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) - break; - case REG_QWORD: //( 11 ) // 64-bit number -// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) - break; - - } - - WriteFile(hFile, RegLine, len, &written, NULL); - } - } - } - - WriteFile(hFile, "\r\n", 2, &written, NULL); - - // Enumerate the subkeys, until RegEnumKeyEx fails. - - if (cSubKeys) - { - for (i=0; i> 1; - } - - Flags=GetApplFlags(i); - - if (OneBits > 1) - sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", - i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, - BPQHOSTVECTOR[i-1].PgmName); - else - sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", - i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, - BPQHOSTVECTOR[i-1].PgmName); - - } - } - - #include "StdExcept.c" - - if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) - FreeSemaphore(&Semaphore); - - } - - if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change - return 0; - - memcpy(Screen, NewScreen, 33 * 108); - InvalidateRect(StatusWnd,NULL,FALSE); - - return(0); -} - -LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - HFONT hOldFont ; - HGLOBAL hMem; - MINMAXINFO * mmi; - int i; - - switch (message) - { - case WM_TIMER: - - if (Semaphore.Flag == 0) - DoStatus(); - break; - - case WM_MDIACTIVATE: - - // Set the system info menu when getting activated - - if (lParam == (LPARAM) hWnd) - { - // Activate - - RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); - AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); - } - else - { - SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); - } - - DrawMenuBar(FrameWnd); - - return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_GETMINMAXINFO: - - mmi = (MINMAXINFO *)lParam; - mmi->ptMaxSize.x = 850; - mmi->ptMaxSize.y = 500; - mmi->ptMaxTrackSize.x = 850; - mmi->ptMaxTrackSize.y = 500; - - - case WM_COMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - //Parse the menu selections: - - switch (wmId) - { - -/* - case BPQSTREAMS: - - CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); - CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); - - StreamDisplay = TRUE; - - break; - - case BPQIPSTATUS: - - CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); - CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); - - StreamDisplay = FALSE; - memset(Screen, ' ', 4000); - - - break; - -*/ - - case BPQCOPY: - - // - // Copy buffer to clipboard - // - hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); - - if (hMem != 0) - { - if (OpenClipboard(hWnd)) - { -// CopyScreentoBuffer(GlobalLock(hMem)); - GlobalUnlock(hMem); - EmptyClipboard(); - SetClipboardData(CF_TEXT,hMem); - CloseClipboard(); - } - else - { - GlobalFree(hMem); - } - - } - - break; - - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - - case WM_SYSCOMMAND: - - wmId = LOWORD(wParam); // Remember, these are... - wmEvent = HIWORD(wParam); // ...different for Win32! - - switch (wmId) - { - case SC_MAXIMIZE: - - break; - - case SC_MINIMIZE: - - StatusMinimized = TRUE; - break; - - case SC_RESTORE: - - StatusMinimized = FALSE; - SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); - break; - } - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - case WM_PAINT: - - hdc = BeginPaint (hWnd, &ps); - - hOldFont = SelectObject( hdc, hFont) ; - - for (i=0; i<33; i++) - { - TextOut(hdc,0,i*14,&Screen[i*108],108); - } - - SelectObject( hdc, hOldFont ) ; - EndPaint (hWnd, &ps); - - break; - - case WM_DESTROY: - -// PostQuitMessage(0); - - break; - - - default: - - return DefMDIChildProc(hWnd, message, wParam, lParam); - - } - return (0); -} - -VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) -{ - HKEY hKey=0; - char Size[80]; - char Key[80]; - int retCode, disp; - RECT Rect; - - if (IsWindow(hWnd) == FALSE) - return; - - ShowWindow(hWnd, SW_RESTORE); - - if (GetWindowRect(hWnd, &Rect) == FALSE) - return; - - // Make relative to Frame - - Rect.top -= FRect.top ; - Rect.left -= FRect.left; - Rect.bottom -= FRect.top; - Rect.right -= FRect.left; - - sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); - - retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, - KEY_ALL_ACCESS, NULL, &hKey, &disp); - - if (retCode == ERROR_SUCCESS) - { - sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); - retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); - RegCloseKey(hKey); - } -} - -extern int GPSPort; -extern char LAT[]; // in standard APRS Format -extern char LON[]; // in standard APRS Format - -VOID SaveBPQ32Windows() -{ - HKEY hKey=0; - char Size[80]; - int retCode, disp; - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - int i; - - retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); - - if (retCode == ERROR_SUCCESS) - { - sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); - retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); - - // Save GPS Position - - if (GPSPort) - { - sprintf(Size, "%s, %s", LAT, LON); - retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); - } - - RegCloseKey(hKey); - } - - SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); - SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); - - for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); - } - } - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - SaveWindowPos(70); // Rigcontrol - - - if (hIPResWnd) - SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); - - SaveHostSessions(); -} - -DllExport BOOL APIENTRY CheckIfOwner() -{ - // - // Returns TRUE if current process is root process - // that loaded the DLL - // - - if (TimerInst == GetCurrentProcessId()) - - return (TRUE); - else - return (FALSE); -} - -VOID GetParam(char * input, char * key, char * value) -{ - char * ptr = strstr(input, key); - char Param[2048]; - char * ptr1, * ptr2; - char c; - - - if (ptr) - { - ptr2 = strchr(ptr, '&'); - if (ptr2) *ptr2 = 0; - strcpy(Param, ptr + strlen(key)); - if (ptr2) *ptr2 = '&'; // Restore string - - // Undo any % transparency - - ptr1 = Param; - ptr2 = Param; - - c = *(ptr1++); - - while (c) - { - if (c == '%') - { - int n; - int m = *(ptr1++) - '0'; - if (m > 9) m = m - 7; - n = *(ptr1++) - '0'; - if (n > 9) n = n - 7; - - *(ptr2++) = m * 16 + n; - } - else if (c == '+') - *(ptr2++) = ' '; - else - *(ptr2++) = c; - - c = *(ptr1++); - } - - *(ptr2++) = 0; - - strcpy(value, Param); - } -} - -int GetListeningPortsPID(int Port) -{ - MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; - PMIB_TCPROW_OWNER_PID Row; - int dwSize = 0; - DWORD n; - - // Get PID of process for this TCP Port - - // Get Length of table - - GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); - - TcpTable = malloc(dwSize); - - if (TcpTable == NULL) - return 0; - - GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); - - for (n = 0; n < TcpTable->dwNumEntries; n++) - { - Row = &TcpTable->table[n]; - - if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) - { - return Row->dwOwningPid; - break; - } - } - return 0; // Not found -} - -DllExport char * APIENTRY GetLOC() -{ - return LOC; -} - -DllExport void APIENTRY GetLatLon(double * lat, double * lon) -{ - *lat = LatFromLOC; - *lon = LonFromLOC; - return; -} - - -// UZ7HO Dll PTT interface - -// 1 ext_PTT_info -// 2 ext_PTT_settings -// 3 ext_PTT_OFF -// 4 ext_PTT_ON -// 5 ext_PTT_close -// 6 ext_PTT_open - -extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); - -VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); -VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); - -int WINAPI ext_PTT_info() -{ - return 0; -} - -int WINAPI ext_PTT_settings() -{ - return 0; -} - -int WINAPI ext_PTT_OFF(int Port) -{ - if (DLLRIG) - Rig_PTTEx(DLLRIG, 0, 0); - - return 0; -} - -int WINAPI ext_PTT_ON(int Port) -{ - if (DLLRIG) - Rig_PTTEx(DLLRIG, 1, 0); - - return 0; -} -int WINAPI ext_PTT_close() -{ - if (DLLRIG) - Rig_PTTEx(DLLRIG, 0, 0); - - return 0; -} - -DllExport INT WINAPI ext_PTT_open() -{ - return 1; -} - -char * stristr (char *ch1, char *ch2) -{ - char *chN1, *chN2; - char *chNdx; - char *chRet = NULL; - - chN1 = _strdup(ch1); - chN2 = _strdup(ch2); - - if (chN1 && chN2) - { - chNdx = chN1; - while (*chNdx) - { - *chNdx = (char) tolower(*chNdx); - chNdx ++; - } - chNdx = chN2; - - while (*chNdx) - { - *chNdx = (char) tolower(*chNdx); - chNdx ++; - } - - chNdx = strstr(chN1, chN2); - - if (chNdx) - chRet = ch1 + (chNdx - chN1); - } - - free (chN1); - free (chN2); - return chRet; -} - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ +// +// 409l Oct 2001 Fix l3timeout for KISS +// +// 409m Oct 2001 Fix Crossband Digi +// +// 409n May 2002 Change error handling on load ext DLL + +// 409p March 2005 Allow Multidigit COM Ports (kiss.c) + +// 409r August 2005 Treat NULL string in Registry as use current directory +// Allow shutdown to close BPQ Applications + +// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 + +// 409t January 2006 +// +// Add API for Perl "GetPerlMsg" +// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine +// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" +// Add New API "GetBPQDirectory" - Returns location of config file +// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command +// Rename BPQNODES to BPQNODES.dat +// New API "GetAttachedProcesses" - returns number of processes connected. +// Warn if user trys to close Console Window. +// Add Debug entries to record Process Attach/Detach +// Fix recovery following closure of first process + +// 409t Beta 2 February 2006 +// +// Add API Entry "GetPortNumber" +// +// 409u February 2006 +// +// Fix crash if allocate/deallocate called with stream=0 +// Add API to ch +// Display config file path +// Fix saving of Locked Node flag +// Added SAVENODES SYSOP command +// +// 409u 2 March 2006 +// +// Fix SetupBPQDirectory +// Add CopyBPQDirectory (for Basic Programs) +// +// 409u 3 March 2006 +// +// Release streams on DLL unload + +// 409v October 2006 +// +// Support Minimize to Tray for all BPQ progams +// Implement L4 application callsigns + +// 410 November 2006 +// +// Modified to compile with C++ 2005 Express Edition +// Make MCOM MTX MMASK local variables +// +// 410a January 2007 +// +// Add program name to Attach-Detach messages +// Attempt to detect processes which have died +// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt +// Add BCALL - origin call for Beacons +// Fix KISS ACKMODE ACK processing +// + +// 410b November 2007 +// +// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary + +// 410c December 2007 + +// Fix problem with NT introduced in V410a +// Display location of DLL on Console + +// 410d January 2008 + +// Fix crash in DLL Init caused by long path to program +// Invoke Appl2 alias on C command (if enabled) +// Allow C command to be disabled +// Remove debug trap in GETRAWFRAME +// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem +// Move Port statup code out of DLLInit (mainly for perl) +// Changes to allow Load/Unload of bpq32.dll by appl +// CloseBPQ32 API added +// Ext Driver Close routes called +// Changes to release Mutex + +// 410e May 2008 + +// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) +// Fix VCOM Driver (RX Len was 1 byte too long) +// Fix possible crash on L4CODE if L4DACK received out of sequence +// Add basic IP decoding + +// 410f October 2008 + +// Add IP Gateway +// Add Multiport DIGI capability +// Add GetPortDescription API +// Fix potential hangs if RNR lost +// Fix problem if External driver failes to load +// Put pushad/popad round _INITIALISEPORTS (main.asm) +// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) +// Ensure Route Qual is updated if Port Qual changed +// Add Reload Option, plus menu items for DUMP and SAVENODES + +// 410g December 2008 + +// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) +// Fix changed init of BPQDirectory (accidentally changed) +// Fix Checks for lost processes (accidentally deleted) +// Support HDLC Cards on W2K and above +// Delete Tray List entries for crashed processes +// Add Option to NODES command to sort by Callsign +// Add options to save or clear BPQNODES before Reconfig. +// Fix Reconfig in Win98 +// Monitor buffering tweaks +// Fix Init for large (>64k) tables +// Fix Nodes count in Stats + +// 410h January 2009 + +// Add Start Minimized Option +// Changes to KISS for WIn98 Virtual COM +// Open \\.\com instead of //./COM +// Extra Dignostics + +// 410i Febuary 2009 + +// Revert KISS Changes +// Save Window positions + +// 410j June 2009 + +// Fix tidying of window List when program crashed +// Add Max Nodes to Stats +// Don't update APPLnALIAS with received NODES info +// Fix MH display in other timezones +// Fix Possible crash when processing NETROM type Zero frames (eg NRR) +// Basic INP3 Stuff +// Add extra diagnostics to Lost Process detection +// Process Netrom Record Route frames. + +// 410k June 2009 + +// Fix calculation of %retries in extended ROUTES display +// Fix corruption of ROUTES table + +// 410l October 2009 + +// Add GetVersionString API call. +// Add GetPortTableEntry API call +// Keep links to neighbouring nodes open + +// Build 2 + +// Fix PE in NOROUTETODEST (missing POP EBX) + +// 410m November 2009 + +// Changes for PACTOR and WINMOR to support the ATTACH command +// Enable INP3 if configured on a route. +// Fix count of nodes in Stats Display +// Overwrite the worst quality unused route if a call is received from a node not in your +// table when the table is full + +// Build 5 + +// Rig Control Interface +// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) + +// Build 6 + +// Fix reading INP3 Flag from BPQNODES + +// Build 7 + +// Add MAXHOPS and MAXRTT config options + +// Build 8 + +// Fix INP3 deletion of Application Nodes. +// Fix GETCALLSIGN for Pactor Sessions +// Add N Call* to display all SSID's of a call +// Fix flow control on Pactor sessions. + +// Build 9 + +// HDLC Support for XP +// Add AUTH routines + +// Build 10 + +// Fix handling commands split over more that one packet. + +// Build 11 + +// Attach cmd changes for winmor disconnecting state +// Option Interlock Winmor/Pactor ports + +// Build 12 + +// Add APPLS export for winmor +// Handle commands ending CR LF + +// Build 13 + +// Incorporate Rig Control in Kernel + +// Build 14 + +// Fix config reload for Rig COntrol + +// 410n March 2010 + +// Implement C P via PACTOR/WINMOR (for Airmail) + +// Build 2 + +// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR +// Fix resetting IDLE Timer on Pactor/WINMOR sessions +// Send L4 KEEPLI messages based on IDLETIME + +// 410o July 2010 + +// Read bpqcfg.txt instead of .bin +// Support 32 bit MMASK (Allowing 32 Ports) +// Support 32 bit _APPLMASK (Allowing 32 Applications) +// Allow more commands +// Allow longer command aliases +// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR +// Clear RIGControl RTS and DTR on close + +// 410o Build 2 August 2010 + +// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) +// Fix Kenwood Rig Control when more than one message received at once. +// Save minimzed state of Rigcontrol Window + +// 410o Build 3 August 2010 + +// Fix reporting of set errors in scan to a random session + +// 410o Build 4 August 2010 + +// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK +// Fix validation of TRANSDELAY + +// 410o Build 5 August 2010 + +// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) +// Add WINMOR and SCS Pactor mode control option to RigControl +// Extend INFOMSG to 2000 bytes +// Improve Scan freq change lock (check both SCS and WINMOR Ports) + +// 410o Build 6 September 2010 + +// Incorporate IPGateway in main code. +// Fix GetSessionInfo for Pactor/Winmor Ports +// Add Antenna Selection to RigControl +// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) + +// 410o Build 7 September 2010 + +// Move rigconrtol display to driver windows +// Move rigcontrol config to driver config. +// Allow driver and IPGateway config info in bpq32.cfg +// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll +// Add option to reread IP Gateway config. +// Fix Reinit after process with timer closes (error in TellSessions). + +// 410p Build 2 October 2010 + +// Move KAM and SCS drivers to bpq32.dll + +// 410p Build 3 October 2010 + +// Support more than one axip port. + +// 410p Build 4 October 2010 + +// Dynamically load psapi.dll (for 98/ME) + +// 410p Build 5 October 2010 + +// Incorporate TelnetServer +// Fix AXIP ReRead Config +// Report AXIP accept() fails to syslog, not a popup. + +// 410p Build 6 October 2010 + +// Includes HAL support +// Changes to Pactor Drivers disconnect code +// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param +// Config now checks for duplicate port definitions +// Add Node Map reporting +// Fix WINMOR deferred disconnect. +// Report Pactor PORTCALL to WL2K instead of RMS Applcall + +// 410p Build 7 October 2010 + +// Add In/Out flag to Map reporting, and report centre, not dial +// Write Telnet log to BPQ Directory +// Add Port to AXIP resolver display +// Send Reports to update.g8bpq.net:81 +// Add support for FT100 to Rigcontrol +// Add timeout to Rigcontrol PTT +// Add Save Registry Command + +// 410p Build 8 November 2010 + +// Add NOKEEPALIVES Port Param +// Renumbered for release + +// 410p Build 9 November 2010 + +// Get Bandwith for map report from WL2K Report Command +// Fix freq display for FT100 (was KHz, not MHz) +// Don't try to change SCS mode whilst initialising +// Allow reporting of Lat/Lon as well as Locator +// Fix Telnet Log Name +// Fix starting with Minimized windows when Minimizetotray isn't set +// Extra Program Error trapping in SessionControl +// Fix reporting same freq with different bandwidths at different times. +// Code changes to support SCS Robust Packet Mode. +// Add FT2000 to Rigcontrol +// Only Send CTEXT to connects to Node (not to connects to an Application Call) + +// Released as Build 10 + +// 410p Build 11 January 2011 + +// Fix MH Update for SCS Outgoing Calls +// Add Direct CMS Access to TelnetServer +// Restructure DISCONNECT processing to run in Timer owning process + +// 410p Build 12 January 2011 + +// Add option for Hardware PTT to use a different com port from the scan port +// Add CAT PTT for Yaesu 897 (and maybe others) +// Fix RMS Packet ports busy after restart +// Fix CMS Telnet with MAXSESSIONS > 10 + +// 410p Build 13 January 2011 + +// Fix loss of buffers in TelnetServer +// Add CMS logging. +// Add non - Promiscuous mode option for BPQETHER + +// 410p Build 14 January 2011 + +// Add support for BPQTermTCP +// Allow more that one FBBPORT +// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs +// Add session length to CMS Telnet logging. +// Return Secure Session Flag from GetConnectionInfo +// Show Uptime as dd/hh/mm + +// 4.10.16.17 March 2011 + +// Add "Close all programs" command +// Add BPQ Program Directory registry key +// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) +// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries +// Add support for SCS Tracker HF Modes +// Fix WL2K Reporting +// Report Version to WL2K +// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) + + +// Above released as 5.0.0.1 + +// 5.2.0.1 + +// Add caching of CMS Server IP addresses +// Initialise TNC State on Pactor Dialogs +// Add Shortened (6 digit) AUTH mode. +// Update MH with all frames (not just I/UI) +// Add IPV6 Support for TelnetServer and AXIP +// Fix TNC OK Test for Tracker +// Fix crash in CMS mode if terminal disconnects while tcp commect in progress +// Add WL2K reporting for Robust Packet +// Add option to suppress WL2K reporting for specific frequencies +// Fix Timeband processing for Rig Control +// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access +// New Driver for V4 TNC + +// 5.2.1.3 October 2011 + +// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) +// Improved program error logging +// WL2K reporting changed to new format agreed with Lee Inman + +// 5.2.3.1 January 2012 + +// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. +// Fix reporting of Tracker freqs to WL2K. +// Fix Tracker monitoring setup (sending M UISC) +// Fix possible call/application routing error on RP +// Changes for P4Dragon +// Include APRS Digi/IGate +// Tracker monitoring now includes DIGIS +// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers +// Include driver for UZ7HO soundcard modem. +// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal +// No longer supports separate config files, or BPQTELNETSERVER.exe +// Improved flow control for Telnet CMS Sessions +// Fix handling Config file without a newline after last line +// Add non - Promiscuous mode option for BPQETHER +// Change Console Window to a Dialog Box. +// Fix possible corruption and loss of buffers in Tracker drivers +// Add Beacon After Session option to Tracker and UZ7HO Drivers +// Rewrite RigControl and add "Reread Config Command" +// Support User Mode VCOM Driver for VKISS ports + +// 5.2.4.1 January 2012 + +// Remove CR from Telnet User and Password Prompts +// Add Rigcontrol to UZ7HO driver +// Fix corruption of Free Buffer Count by Rigcontol +// Fix WINMOR and V4 PTT +// Add MultiPSK Driver +// Add SendBeacon export for BPQAPRS +// Add SendChatReport function +// Fix check on length of Port Config ID String with trailing spaces +// Fix interlock when Port Number <> Port Slot +// Add NETROMCALL for L3 Activity +// Add support for APRS Application +// Fix Telnet with FBBPORT and no TCPPORT +// Add Reread APRS Config +// Fix switching to Pactor after scanning in normal packet mode (PTC) + +// 5.2.5.1 February 2012 + +// Stop reading Password file. +// Add extra MPSK commands +// Fix MPSK Transparency +// Make LOCATOR command compulsory +// Add MobileBeaconInterval APRS param +// Send Course and Speed when APRS is using GPS +// Fix Robust Packet reporting in PTC driver +// Fix corruption of some MIC-E APRS packets + +// 5.2.6.1 February 2012 + +// Convert to MDI presentation of BPQ32.dll windows +// Send APRS Status packets +// Send QUIT not EXIT in PTC Init +// Implement new WL2K reporting format and include traffic reporting info in CMS signon +// New WL2KREPORT format +// Prevent loops when APPL alias refers to itself +// Add RigControl for Flex radios and ICOM IC-M710 Marine radio + +// 5.2.7.1 + +// Fix opening more thn one console window on Win98 +// Change method of configuring multiple timelots on WL2K reporting +// Add option to update WK2K Sysop Database +// Add Web server +// Add UIONLY port option + +// 5.2.7.2 + +// Fix handling TelnetServer packets over 500 bytes in normal mode + +// 5.2.7.3 + +// Fix Igate handling packets from UIView + +// 5.2.7.4 + +// Prototype Baycom driver. + +// 5.2.7.5 + +// Set WK2K group ref to MARS (3) if using a MARS service code + +// 5.2.7.7 + +// Check for programs calling CloseBPQ32 when holding semaphore +// Try/Except round Status Timer Processing + +// 5.2.7.8 + +// More Try/Except round Timer Processing + +// 5.2.7.9 + +// Enable RX in Baycom, and remove test loopback in tx + +// 5.2.7.10 + +// Try/Except round ProcessHTTPMessage + +// 5.2.7.11 + +// BAYCOM tweaks + +// 5.2.7.13 + +// Release semaphore after program error in Timer Processing +// Check fro valid dest in REFRESHROUTE + + +// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( + +// Version 5.2.9.1 Sept 2012 + +// Fix using KISS ports with COMn > 16 +// Add "KISS over UDP" driver for PI as a TNC concentrator + +// Version 6.0.1.1 + +// Convert to C for linux portability +// Try to speed up kiss polling + +// Version 6.0.2.1 + +// Fix operation on Win98 +// Fix callsign error with AGWtoBPQ +// Fix PTT problem with WINMOR +// Fix Reread telnet config +// Add Secure CMS signon +// Fix error in cashing addresses of CMS servers +// Fix Port Number when using Send Raw. +// Fix PE in KISS driver if invalid subchannel received +// Fix Orignal address of beacons +// Speed up Telnet port monitoring. +// Add TNC Emulators +// Add CountFramesQueuedOnStream API +// Limit number of frames that can be queued on a session. +// Add XDIGI feature +// Add Winmor Robust Mode switching for compatibility with new Winmor TNC +// Move most APRS code from BPQAPRS to here +// Stop corruption caused by overlong KISS frames + +// Version 6.0.3.1 + +// Add starting/killing WINMOR TNC on remote host +// Fix Program Error when APRS Item or Object name is same as call of reporting station +// Dont digi a frame that we have already digi'ed +// Add ChangeSessionIdleTime API +// Add WK2KSYSOP Command +// Add IDLETIME Command +// Fix Errors in RELAYAPPL processing +// Fix PE cauaed by invalid Rigcontrol Line + +// Version 6.0.4.1 + +// Add frequency dependent autoconnect appls for SCS Pactor +// Fix DED Monitoring of I and UI with no data +// Include AGWPE Emulator (from AGWtoBPQ) +// accept DEL (Hex 7F) as backspace in Telnet +// Fix re-running resolver on re-read AXIP config +// Speed up processing, mainly for Telnet Sessions +// Fix APRS init on restart of bpq32.exe +// Change to 2 stop bits +// Fix scrolling of WINMOR trace window +// Fix Crash when ueing DED TNC Emulator +// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions +// Allow HOST applications even when CMS option is disabled +// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) + +// Version 6.0.5.1 January 2014 + +// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) +// Add "Clear" option to MH command +// Add "Connect to RMS Relay" Option +// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control +// Fix routing of first call in Robust Packet +// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) +// Add RTS>CAT PTT option for Sound Card rigs +// Add Clear Nodes Option (NODE DEL ALL) +// SCS Pactor can set differeant APPLCALLS when scanning. +// Fix possible Scan hangup after a manual requency change with SCS Pactor +// Accept Scan entry of W0 to disable WINMOR on that frequency +// Fix corruption of NETROMCALL by SIMPLE config command +// Enforce Pactor Levels +// Add Telnet outward connect +// Add Relay/Trimode Emulation +// Fix V4 Driver +// Add PTT Mux +// Add Locked ARP Entries (via bpq32.cfg) +// Fix IDLETIME node command +// Fix STAY param on connect +// Add STAY option to Attach and Application Commands +// Fix crash on copying a large AXIP MH Window +// Fix possible crash when bpq32.exe dies +// Fix DIGIPORT for UI frames + +// Version 6.0.6.1 April 2014 + +// FLDigi Interface +// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. +// Validate INP3 messages to try to prevent crash +// Fix possible crash if an overlarge KISS frame is received +// Fix error in AXR command +// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line +// Add CBELL to TNC21 emulator +// Add sent objects and third party messages to APRS Dup List +// Incorporate UIUtil +// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 +// Improvements to FLDIGI interlocking +// Fix TNC State Display for Tracker +// Cache CMS Addresses on LinBPQ +// Fix count error on DED Driver when handling 256 byte packets +// Add basic SNMP interface for MRTG +// Fix memory loss from getaddrinfo +// Process "BUSY" response from Tracker +// Handle serial port writes that don't accept all the data +// Trap Error 10038 and try to reopen socket +// Fix crash if overlong command line received + +// Version 6.0.7.1 Aptil 2014 +// Fix RigContol with no frequencies for Kenwood and Yaesu +// Add busy check to FLDIGI connects + +// Version 6.0.8.1 August 2014 + +// Use HKEY_CURRENT_USER on all OS versions +// Fix crash when APRS symbol is a space. +// Fixes for FT847 CAT +// Fix display of 3rd byte of FRMR +// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver +// Fix possible memory corruption in WINMOR driver +// Fix FT2000 Modes +// Use new WL2K reporting system (Web API Based) +// APRS Server now cycles through hosts if DNS returns more than one +// BPQ32 can now start and stop FLDIGI +// Fix loss of AXIP Resolver when running more than one AXIP port + +// Version 6.0.9.1 November 2014 + +// Fix setting NOKEEPALIVE flag on route created from incoming L3 message +// Ignore NODES from locked route with quality 0 +// Fix seting source port in AXIP +// Fix Dual Stack (IPV4/V6) on Linux. +// Fix RELAYSOCK if IPv6 is enabled. +// Add support for FT1000 +// Fix hang when APRS Messaging packet received on RF +// Attempt to normalize Node qualies when stations use widely differing Route qualities +// Add NODES VIA command to display nodes reachable via a specified neighbour +// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) +// Fix buffering large messages in Telnet Host API +// Fix occasional crash in terminal part line processing +// Add "NoFallback" command to Telnet server to disable "fallback to Relay" +// Improved support for APPLCALL scanning with Pactor +// MAXBUFFS config statement is no longer needed. +// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails +// Implement LISTEN and CQ commands +// FLDIGI driver can now start FLDIGI on a remote system. +// Add IGNOREUNLOCKEDROUTES parameter +// Fix error if too many Telnet server connections + +// Version 6.0.10.1 Feb 2015 + +// Fix crash if corrupt HTML request received. +// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. +// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins +// Add INP3ONLY flag. +// Fix program error if enter UNPROTO without a destination path +// Show client IP address on HTTP sessions in Telnet Server +// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set +// Add FT990 RigControl support, fix FT1000MP support. +// Support ARMV5 processors +// Changes to support LinBPQ APRS Client +// Add IC7410 to supported Soundcard rigs +// Add CAT PTT to NMEA type (for ICOM Marine Radios_ +// Fix ACKMODE +// Add KISS over TCP +// Support ACKMode on VKISS +// Improved reporting of configuration file format errors +// Experimental driver to support ARQ sessions using UI frames + +// Version 6.0.11.1 September 2015 + +// Fixes for IPGateway configuration and Virtual Circuit Mode +// Separate Portmapper from IPGateway +// Add PING Command +// Add ARDOP Driver +// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) +// Add "VeryOldMode" for KAM Version 5.02 +// Add KISS over TCP Slave Mode. +// Support Pactor and Packet on P4Dragon on one port +// Add "Remote Staton Quality" to Web ROUTES display +// Add Virtual Host option for IPGateway NET44 Encap +// Add NAT for local hosts to IPGateway +// Fix setting filter from RADIO command for IC7410 +// Add Memory Channel Scanning for ICOM Radios +// Try to reopen Rig Control port if it fails (could be unplugged USB) +// Fix restoring position of Monitor Window +// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) +// Support APRS beacons in RP mode on Dragon// +// Change Virtual MAC address on IPGateway to include last octet of IP Address +// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode +// Fix sending I frames before L2 session is up +// Fix Flow control on Telnet outbound sessions. +// Fix reporting of unterminatred comments in config +// Add option for RigControl to not change mode on FT100/FT990/FT1000 +// Add "Attach and Connect" for Telnet ports + +// Version 6.0.12.1 November 2015 + +// Fix logging of IP addresses for connects to FBBPORT +// Allow lower case user and passwords in Telnet "Attach and Connect" +// Fix possible hang in KISS over TCP Slave mode +// Fix duplicating LinBPQ process if running ARDOP fails +// Allow lower case command aliases and increase alias length to 48 +// Fix saving long IP frames pending ARP resolution +// Fix dropping last entry from a RIP44 message. +// Fix displaying Digis in MH list +// Add port name to Monitor config screen port list +// Fix APRS command display filter and add port filter +// Support port names in BPQTermTCP Monitor config +// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog +// Buffer Web Mgmt Edit Config output +// Add WebMail Support +// Fix not closing APRS Send WX file. +// Add RUN option to APRS Config to start APRS Client +// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 +// Close and reopen ARDOP connection if nothing received for 90 secs +// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) +// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode + +// 6.0.13.1 + +// Allow /ex to exit UNPROTO mode +// Support ARQBW commands. +// Support IC735 +// Fix sending ARDOP beacons after a busy holdoff +// Enable BPQDED driver to beacon via non-ax.25 ports. +// Fix channel number in UZ7HO monitoring +// Add SATGate mode to APRSIS Code. +// Fix crash caused by overlong user name in telnet logon +// Add option to log L4 connects +// Add AUTOADDQuiet mode to AXIP. +// Add EXCLUDE processing +// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux +// Convert calls in MAP entries to upper case. +// Support Linux COM Port names for APRS GPS +// Fix using NETROM serial protocol on ASYNC Port +// Fix setting MYLEVEL by scanner after manual level change. +// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic +// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command +// Add Multicast RX interface to FLDIGI Driver +// Fix processing application aliases to a connect command. +// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it +// Save backups of bpq32.cfg when editing with Web interface and report old and new length +// Add DD command to SCS Pactor, and use it for forced disconnect. +// Add ARDOP mode select to scan config +// ARDOP changes for ARDOP V 0.5+ +// Flip SSID bits on UZ7HO downlink connects + + +// Version 6.0.14.1 + +// Fix Socket leak in ARDOP and FLDIGI drivers. +// Add option to change CMS Server hostname +// ARDOP Changes for 0.8.0+ +// Discard Terminal Keepalive message (two nulls) in ARDOP command hander +// Allow parameters to be passed to ARDOP TNC when starting it +// Fix Web update of Beacon params +// Retry connects to KISS ports after failure +// Add support for ARDOP Serial Interface Native mode. +// Fix gating APRS-IS Messages to RF +// Fix Beacons when PORTNUM used +// Make sure old monitor flag is cleared for TermTCP sessions +// Add CI-V antenna control for IC746 +// Don't allow ARDOP beacons when connected +// Add support for ARDOP Serial over I2C +// Fix possble crash when using manual RADIO messages +// Save out of sequence L2 frames for possible reuse after retry +// Add KISS command to send KISS control frame to TNC +// Stop removing unused digis from packets sent to APRS-IS + +// Processing of ARDOP PING and PINGACK responses +// Handle changed encoding of WL2K update responses. +// Allow anonymous logon to telnet +// Don't use APPL= for RP Calls in Dragon Single mode. +// Add basic messaging page to APRS Web Server +// Add debug log option to SCSTracker and TrkMulti Driver +// Support REBOOT command on LinBPQ +// Allow LISTEN command on all ports that support ax.25 monitoring + +// Version 6.0.15.1 Feb 2018 + +// partial support for ax.25 V2.2 +// Add MHU and MHL commands and MH filter option +// Fix scan interlock with ARDOP +// Add Input source seiect for IC7300 +// Remove % transparency from web terminal signon message +// Fix L4 Connects In count on stats +// Fix crash caused by corrupt CMSInfo.txt +// Add Input peaks display to ARDOP status window +// Add options to show time in local and distances in KM on APRS Web pages +// Add VARA support +// Fix WINMOR Busy left set when port Suspended +// Add ARDOP-Packet Support +// Add Antenna Switching for TS 480 +// Fix possible crash in Web Terminal +// Support different Code Pages on Console sessions +// Use new Winlink API interface (api.winlink.org) +// Support USB/ACC switching on TS590SG +// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. +// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. +// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs +// Fix timing out ARDOP PENDING Lock +// Support mixed case WINLINK Passwords +// Add TUNE and POWER Rigcontol Commands for some radios +// ADD LOCALTIME and DISPKM options to APRS Digi/Igate + +// 6.0.16.1 March 2018 + +// Fix Setting data mode and filter for IC7300 radios +// Add VARA to WL2KREPORT +// Add trace to SCS Tracker status window +// Fix possible hang in IPGATEWAY +// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. +// Fix sending CTEXT on WINMOR sessions + +// 6.0.17.1 November 2018 + +// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA +// Add Abort Connection to WINMOR and VARA Interfaces +// Reinstate accidentally removed CMS Access logging +// Fix MH CLEAR +// Fix corruption of NODE table if NODES received from station with null alias +// Fix loss of buffer if session closed with something in PARTCMDBUFFER +// Fix Spurious GUARD ZONE CORRUPT message in IP Code. +// Remove "reread bpq32.cfg and reconfigure" menu options +// Add support for PTT using CM108 based soundcard interfaces +// Datestamp Telnet log files and delete old Telnet and CMSAcces logs + +// 6.0.18.1 January 2019 + +// Fix validation of NODES broadcasts +// Fix HIDENODES +// Check for failure to reread config on axip reconfigure +// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port +// Send Beacons from BCALL or PORTCALL if configured +// Fix possible corruption of last entry in MH display +// Ensure RTS/DTR is down when opening PTT Port +// Remove RECONFIG command +// Preparations for 64 bit version + +// 6.0.19 Sept 2019 +// Fix UZ7HO interlock +// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) +// Add option to save and restore MH lists and SAVEMH command +// Add Frequency (if known) to UZ7HO MH lists +// Add Gateway option to Telnet for PAT +// Try to fix SCS Tracker recovery +// Ensure RTS/DTR is down on CAT port if using that line for PTT +// Experimental APRS Messaging in Kernel +// Add Rigcontrol on remote PC's using WinmorControl +// ADD VARAFM and VARAFM96 WL2KREPORT modes +// Fix WL2K sysop update for new Winlink API +// Fix APRS when using PORTNUM higher than the number of ports +// Add Serial Port Type +// Add option to linbpq to log APRS-IS messages. +// Send WL2K Session Reports +// Drop Tunneled Packets from 44.192 - 44.255 +// Log incoming Telnet Connects +// Add IPV4: and IPV6: overrides on AXIP Resolver. +// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) +// Add RADIO FREQ command to display current frequency + +// 6.0.20 April 2020 + +// Trap and reject YAPP file transfer request. +// Fix possible overrun of TCP to Node Buffer +// Fix possible crash if APRS WX file doesn't have a terminating newline +// Change communication with BPQAPRS.exe to restore old message popup behaviour +// Preparation for 64 bit version +// Improve flow control on SCS Dragon +// Fragment messages from network links to L2 links with smaller paclen +// Change WL2K report rate to once every two hours +// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator +// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) +// Add links to Ports Web Manangement Page to open individual Driver windows +// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers +// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. +// Don't try to send WL2K Traffic report if Internet is down +// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server +// ADD AGWAPPL config command to set application number. AGWMASK is still supported +// Register Node Alias with UZ7HO Driver +// Register calls when UZ7HO TNC Restarts and at intervals afterwards +// Fix crash when no IOADDR or COMPORT in async port definition +// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 +// Only apply BBSFLAG=NOBBS to APPPLICATION 1 +// Add RIGREONFIG command +// fix APRS RECONFIG on LinBPQ +// Fix Web Terminal scroll to end problem on some browsers +// Add PTT_SETS_INPUT option for IC7600 +// Add TELRECONFIG command to reread users or whole config +// Enforce PACLEN on UZ7HO ports +// Fix PACLEN on Command Output. +// Retry axip resolver if it fails at startup +// Fix AGWAPI connect via digis +// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers +// Limit APRS OBJECT length to 80 chars +// UZ7HO disconnect incoming call if no free streams +// Improve response to REJ (no F) followed by RR (F). +// Try to prevent more than MAXFRAME frames outstanding when transmitting +// Allow more than one instance of APRS on Linux +// Stop APRS digi by originating station +// Send driver window trace to main monitor system +// Improve handling of IPOLL messages +// Fix setting end of address bit on dest call on connects to listening sessions +// Set default BBS and CHAT application number and number of streams on LinBPQ +// Support #include in bpq32.cfg processing + +// Version 6.0.21 14 December 2020 + +// Fix occasional missing newlines in some node command reponses +// More 64 bit fixes +// Add option to stop setting PDUPLEX param in SCSPACTOR +// Try to fix buffer loss +// Remove extra space from APRS position reports +// Suppress VARA IAMALIVE messages +// Add display and control of QtSoundModem modems +// Only send "No CMS connection available" message if fallbacktorelay is set. +// Add HAMLIB backend and emulator support to RIGCONTROL +// Ensure all beacons are sent even with very short beacon intervals +// Add VARA500 WL2K Reporting Mode +// Fix problem with prpcessing frame collector +// Temporarily disable L2 and L4 collectors till I can find problem +// Fix possible problem with interactive RADIO commands not giving a response, +// Incease maximum length of NODE command responses to handle maximum length INFO message, +// Allow WL2KREPORT in CONFIG section of UZ7HO port config. +// Fix program error in processing hamlib frame +// Save RestartAfterFailure option for VARA +// Check callsign has a winlink account before sending WL2KREPORT messages +// Add Bandwidth control to VARA scanning +// Renable L2 collector +// Fix TNCPORT reconnect on Linux +// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases +// Add option to suppress sending call to application in Telnet HOST API +// Add FT991A support to RigControl +// Use background.jpg for Edit Config page +// Send OK response to SCS Pactor commands starting with # +// Resend ICOM PTT OFF command after 30 seconds +// Add WXCall to APRS config +// Fixes for AEAPactor +// Allow PTTMUX to use real or com0com com ports +// Fix monitoring with AGW Emulator +// Derive approx position from packets on APRS ports with a valid 6 char location +// Fix corruption of APRS message lists if the station table fills up. +// Don't accept empty username or password on Relay sessions. +// Fix occasional empty Nodes broadcasts +// Add Digis to UZ7HO Port MH list +// Add PERMITTEDAPPLS port param +// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. +// Fix handling AX/IP (proto 93) frames +// Fix possible corruption sending APRS messages +// Allow Telnet connections to be made using Connect command as well as Attach then Connect +// Fix Cancel Sysop Signin +// Save axip resolver info and restore on restart +// Add Transparent mode to Telnet Server HOST API +// Fix Tracker driver if WL2KREPRRT is in main config section +// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. +// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon +// Fix crash in Web terminal when processing very long lines + +// Version 6.0.22.1 August 2021 + +// Fix bug in KAM TNCEMULATOR +// Add WinRPR Driver (DED over TCP) +// Fix handling of VARA config commands FM1200 and FM9600 +// Improve Web Termanal Line folding +// Add StartTNC to WinRPR driver +// Add support for VARA2750 Mode +// Add support for VARA connects via a VARA Digipeater +// Add digis to SCSTracker and WinRPR MHeard +// Separate RIGCONTROL config from PORT config and add RigControl window +// Fix crash when a Windows HID device doesn't have a product_string +// Changes to VARA TNC connection and restart process +// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. +// Fix saving part lines in adif log and Winlink Session reporting +// Add port specific CTEXT +// Add FRMR monitoring to UZ7HO driver +// Add audio input switching for IC7610 +// Include Rigcontrol Support for IC-F8101E +// Process any response to KISS command +// Fix NODE ADD command +// Add noUpdate flag to AXIP MAP +// Fix clearing NOFALLBACK flag in Telnet Server +// Allow connects to RMS Relay running on another host +// Allow use of Power setting in Rigcontol scan lines for Kenwood radios +// Prevent problems caused by using "CMS" as a Node Alias +// Include standard APRS Station pages in code +// Fix VALIDCALLS processing in HF drivers +// Send Netrom Link reports to Node Map +// Add REALTELNET mode to Telnet Outward Connect +// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST +// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection +// Fix long (> 60 seconds) scan intervals +// Improved debugging of stuck semaphores +// Fix potential securiby bug in BPQ Web server +// Send Chat Updates to chatupdate.g8bpq.net port 81 +// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY +// Add experimental Mode reporting +// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY +// Add UPNP Support + +// Version 6.0.23.1 June 2022 + +// Add option to control which applcalls are enabled in VARA +// Add support for rtl_udp to Rig Control +// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal +// Allow setting css styles for Web Terminal +// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows +// More flexible RigControl for split frequency operation, eg for QO100 +// Increase stack size for ProcessHTMLMessage (.11) +// Fix HTML Content-Type on images (.12) +// Add AIS and ADSB Support (.13) +// Compress web pages (.14) +// Change minidump routine and close after program error (.15) +// Add RMS Relay SYNC Mode (.17) +// Changes for compatibility with Winlink Hybrid +// Add Rigcontrol CMD feature to Yaesu code (21) +// More diagnostic code +// Trap potential buffer overrun in ax/tcp code +// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail +// Add FLRIG as backend for RigControl (.24) +// Fix bug in compressing some management web pages +// Fix bugs in AGW Emulator (.25) +// Add more PTT_Sets_Freq options for split frequency working (.26) +// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) +// Fix Telnet negotiation and backspace processing (.29) +// Fix VARA Mode change when scanning (.30) +// Add Web Mgmt Log Display (.33) +// Fix crash when connecting to RELAY when CMS=0 (.36) +// Send OK to user for manual freq changes with hamlib or flrig +// Fix Rigcontrol leaving port disabled when using an empty timeband +// Fix processing of backspace in Telnet character processing (.40) +// Increase max size of connect script +// Fix HAMLIB Slave Thread control +// Add processing of VARA mode responses and display of VARA Mode (41) +// Fix crash when VARA session aborted on LinBPQ (43) +// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) +// Include APRS Map web page +// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) +// Add Basic DRATS interface (46) +// Fix MYCALLS on VARA (49) +// Add FreeData driver (51) +// Add additonal Rigcontrol options for QO100 (51) +// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) +// Fix sending large compressed web messages (52) +// Fix freq display when using flrig or hamlib backends to rigcontrol +// Change VARA Driver to send ABORT when Session Time limit expires +// Add Chat Log to Web Logs display +// Fix possible buffer loss in RigControl +// Allow hosts on local lan to be treated as secure +// Improve validation of data sent to Winlink SessionAdd API call +// Add support for FreeDATA modem. +// Add GetLOC API Call +// Change Leaflet link in aprs map. +// Add Connect Log (64) +// Fix crash when Resolve CMS Servers returns ipv6 addresses +// Fix Reporting P4 sessions to Winlink (68) +// Add support for FreeBSD (68) +// Fix Rigcontrol PTCPORT (69) +// Set TNC Emulator sessions as secure (72) +// Fix not always detecting loss of FLRIG (73) +// Add ? and * wildcards to NODES command (74) +// Add Port RADIO config parameter (74) + +// Version 6.0.24.1 August 2023 + +// Apply NODES command wildcard to alias as well a call (2) +// Add STOPPORT/STARTPORT to VARA Driver (2) +// Add bandwidth setting to FLRIG interface. (2) +// Fix N VIA (3) +// Fix NODE ADD and NODE DEL (4) +// Improvements to FLRIG Rigcontrol backend (6, 7) +// Fix UZ7HO Window Title Update +// Reject L2 calls with a blank from call (8) +// Update WinRPR Window header with BPQ Port Description (8) +// Fix error in blank call code (9) +// Change web buttons to white on black when pressed (10) +// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) +// Add RADIO PTT command for testing PTT (11) +// Fix using APPLCALLs on SCSTracker RP call (12) +// Add Rigcntol Web Page (13) +// Fix scan bandwidth change with ARDOPOFDM (13) +// Fix setting Min Pactor Level in SCSPactor (13) +// Fix length of commands sent via CMD_TO_APPL flag (14) +// Add filter by quality option to N display (15) +// Fix VARA Mode reporting to WL2K (16) +// Add FLRIG POWER and TUNE commands (18) +// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) +// FLDIGI improvements (19) +// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) +// Fix processing C command if first port driver is SCSPACTROR (20) +// Fix crash in UZ7HO driver if bad raw frame received (21) +// Fix using FLARQ chat mode with FLDIGI ddriover (22) +// Fix to KISSHF driver (23) +// Fix for application buffer loss (24) +// Add Web Sockets auto-refresh option for Webmail index page (25) +// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) +// Add SmartID for bridged frames - Send ID only if packets sent recently (26) +// Add option to save and restore received APRS messages (27) +// Add mechanism to run a user program on certain events (27) +// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) +// Add Node Help command (28) +// Add APRS Igate RXOnly option (29) +// Fix RMC message handling with prefixes other than GP (29) +// Add GPSD support for APRS (30) +// Attempt to fix Tracker/WinRPR reconnect code (30) +// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) +// Fix interactive commands in tracker driver (33) +// Fix SESSIONTIMELIMIT processing +// Add STOPPORT/STARTPORT for UZ7HO driver +// Fix processing of extended QtSM 'g' frame (36) +// Allow setting just freq on Yaseu rigs (37) +// Enable KISSHF driver on Linux (40) +// Allow AISHOST and ADSBHOST to be a name as well as an address (41) +// Fix Interlock of incoming UZ7HO connections (41) +// Disable VARA Actions menu if not sysop (41) +// Fix Port CTEXT on UZ7HO B C or D channels (42) +// Fix repeated trigger of SessionTimeLimit (43) +// Fix posible memory corruption in UpateMH (44) +// Add PHG to APRS beacons (45) +// Dont send DM to stations in exclude list(45) +// Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) +// Switch APRS Map to my Tiles (52) +// Fix using ; in UNPROTO Mode messages (52) +// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) +// Fix TNC Emulator Monitoring (53) +// Fix attach and connect on Telnet port bug introduced in .55 (56) +// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) +// Fix stack size in beginthread for MAC (58) +// Add NETROM over VARA (60) +// Add Disconnect Script (64) +// Add node commands to set UZ7HO modem mode and freq (64) +// Trap empty NODECALL or NETROMCALL(65) +// Trap NODES messages with empty From Call (65) +// Add RigControl for SDRConsole (66) +// Fix FLRig crash (66) +// Fix VARA disconnect handling (67) +// Support 64 ports (69) +// Fix Node commands for setting UZ7HO Modem (70) +// Fix processing SABM on an existing session (71) +// Extend KISS Node command to send more than one parameter byte (72) +// Add G7TAJ's code to record activity of HF ports for stats display (72) +// Add option to send KISS command to TNC on startup (73) +// Fix Bug in DED Emulator Monitor code (74) +// Add Filters to DED Monitor code (75) +// Detect loss of DED application (76) +// Fix connects to Application Alias with UZ7HO Driver (76) +// Fix Interlock of ports on same UZ7HO modem. (76) +// Add extended Ports command (77) +// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) +// Fix Web Terminal (80) +// Trap ENCRYPTION message from VARA (81) +// Fix processing of the Winlink API /account/exists response (82) +// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set + +// Version 6.0.25.? + +// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers +// Add Chat PACLEN config (5) +// Fix NC to Application Call (6) +// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) +// Get Beacon config from config file on Windows (9) +// fix processing DED TNC Emulator M command with space between M and params (10) +// Fix sending UI frames on SCSPACTOR (11) +// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) +// Add SDRAngel rig control (11) +// Add option to specify config and data directories on linbpq (12) +// Allow zero resptime (send RR immediately) (13) +// Make sure CMD bit is set on UI frames +// Add setting Modem Flags in QtSM AGW mode +// If FT847 om PTC Port send a "Cat On" command (17) +// Fix some 63 port bugs in RigCOntrol (17) +// Fix 63 port bug in Bridging (18) +// Add FTDX10 Rigcontrol (19) +// Fix 64 bit bug in displaying INP3 Messages (20) +// Improve restart of WinRPR TNC on remote host (21) +// Fix some Rigcontrol issues with empty timebands (22) +// Fix 64 bit bug in processing INP3 Messages (22) +// First pass at api (24) +// Send OK in response to Rigcontrol CMD (24) +// Disable CTS check in WriteComBlock (26) +// Improvments to reporting to M0LTE Map (26) +// IPGateway fix from github user isavitsky (27) +// Fix possible crash in SCSPactor PTCPORT code (29) +// Add NodeAPI call sendLinks and remove get from other calls (32) +// Improve validation of Web Beacon Config (33) +// Support SNMP via host ip stack as well as IPGateway (34) +// Switch APRS Map to OSM tile servers (36) +// Fix potential buffer overflow in Telnet login (36) +// Allow longer serial device names (37) +// Fix ICF8101 Mode setting (37) +// Kill link if we are getting repeated RR(F) after timeout +// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) +// Change default of SECURETELNET to 1 (41) +// Add optional ATTACH time limit for ARDOP (42) +// Fix buffer overflow risk in HTTP Terminal(42) +// Fix KISSHF Interlock (43) +// Support other than channel A on HFKISS (43) +// Support additional port info reporting for M0LTE Map (44) +// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) +// Add ARDOP UI Packets to MH (45) +// Add support for Qtsm Mgmt Interface (45) +// NodeAPI improvements (46) +// Add MQTT Interface (46) +// Fix buffer leak in ARDOP code(46) +// Fix possible crash if MQTT not in use (47) +// Add optional ATTACH time limit for VARA (48) +// API format fixes (48) +// AGWAPI Add protection against accidental connects from a non-agw application (50) +// Save MH and NODES every hour (51) +// Fix handling long unix device names (now max 250 bytes) (52) +// Fix error reporting in api update (53) +// Coding changes to remove some compiler warnings (53, 54) +// Add MQTT reporting of Mail Events (54) +// Fix beaconong on KISSHF ports (55) +// Fix MailAPI msgs endpoint +// Attempt to fix NC going to wrong application. (57) +// Improve ARDOP end of session code (58) +// Run M0LTE Map reporting in a separate thread (59/60) +// Add RHP support for WhatsPac (59) +// Add timestamps to LIS monitor (60) +// Fix problem with L4 frames being delivered out of sequence (60) +// Add Compression of Netrom connections (62) +// Improve handling of Locked Routes (62) +// Add L4 RESET (Paula G8PZT's extension to NETROM) +// Fix problem using SENDRAW from BPQMail (63) +// Fix compatibility with latest ardopcf (64) +// Fix bug in RHP socket timeout code (65) +// Fix L4 RTT (66) +// Fix RigConrol with Chanxx but no other settings (66) +// Add option to compress L2 frames (67) +// Sort Routes displays (67) +// Fix Ardop session premature close (70) +// Add timestamps to log entries in Web Driver windows (70) +// Generate stack backtrace if SIGSEGV or SIGABRT occur (Linux) (70) +// Remove some debug logging from L2 code (70) +// Fix compiling LinBPQ with nomqtt option (70) +// Improve handling of binary data in RHP interface (70) +// Fix sending KISS commands to multiport or multidropped TNCs (70) +// Add MHUV and MHLV commands (Verbose listing with timestamps in clock time) (70) + +#define CKernel + +#include "Versions.h" + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" +#include "AsmStrucs.h" + +#include "SHELLAPI.H" +#include "kernelresource.h" + +#include +#include +#include "BPQTermMDI.h" + +#include "GetVersion.h" + +#define DllImport __declspec( dllimport ) + +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) +void _CheckGuardZone(char * File, int Line); + +#define CHECKLOADED 0 +#define SETAPPLFLAGS 1 +#define SENDBPQFRAME 2 +#define GETBPQFRAME 3 +#define GETSTREAMSTATUS 4 +#define CLEARSTREAMSTATUS 5 +#define BPQCONDIS 6 +#define GETBUFFERSTATUS 7 +#define GETCONNECTIONINFO 8 +#define BPQRETURN 9 // GETCALLS +//#define RAWTX 10 //IE KISS TYPE DATA +#define GETRAWFRAME 11 +#define UPDATESWITCH 12 +#define BPQALLOC 13 +//#define SENDNETFRAME 14 +#define GETTIME 15 + +extern short NUMBEROFPORTS; +extern long PORTENTRYLEN; +extern long LINKTABLELEN; +extern struct PORTCONTROL * PORTTABLE; +extern void * FREE_Q; +extern UINT APPL_Q; // Queue of frames for APRS Appl + +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR NEXTID; +extern DWORD MAXCIRCUITS; +extern DWORD L4DEFAULTWINDOW; +extern DWORD L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; + +extern struct WL2KInfo * WL2KReports; + +extern int NUMBEROFTNCPORTS; + + +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * TelnetExtInit(EXTPORTDATA * PortEntry); +//void * SoundModemExtInit(EXTPORTDATA * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * UIARQExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * SIXPACKExtInit(EXTPORTDATA * PortEntry); + +extern char * ConfigBuffer; // Config Area +VOID REMOVENODE(dest_list * DEST); +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); +VOID GetUIConfig(); +VOID ADIFWriteFreqList(); +void SaveAIS(); +void initAIS(); +void initADSB(); + +extern BOOL ADIFLogEnabled; + +int CloseOnError = 0; + +char UIClassName[]="UIMAINWINDOW"; // the main window class name + +HWND UIhWnd; + +extern char AUTOSAVE; +extern char AUTOSAVEMH; + +extern char MYNODECALL; // 10 chars,not null terminated + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +extern char * CONFIGFILENAME; + +DllExport BPQVECSTRUC * BPQHOSTVECPTR; + +extern int DATABASESTART; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + +extern double LatFromLOC; +extern double LonFromLOC; + + +extern int BPQHOSTAPI(); +extern int INITIALISEPORTS(); +extern int TIMERINTERRUPT(); +extern int MONDECODE(); +extern int BPQMONOPTIONS(); +extern char PWTEXT[]; +extern char PWLen; + +extern int FINDFREEDESTINATION(); +extern int RAWTX(); +extern int RELBUFF(); +extern int SENDNETFRAME(); +extern char MYCALL[]; // 7 chars, ax.25 format + +extern HWND hIPResWnd; +extern BOOL IPMinimized; + +extern int NODESINPROGRESS; +extern VOID * CURRENTNODE; + + +BOOL Start(); + +VOID SaveWindowPos(int port); +VOID SaveAXIPWindowPos(int port); +VOID SetupRTFHddr(); +DllExport VOID APIENTRY CreateNewTrayIcon(); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int DoMonData(int Stream); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +CloseHostSessions(); +SaveHostSessions(); +VOID SaveBPQ32Windows(); +VOID CloseDriverWindow(int port); +VOID CheckWL2KReportTimer(); +VOID SetApplPorts(); +VOID WriteMiniDump(); +VOID FindLostBuffers(); +BOOL InitializeTNCEmulator(); +VOID TNCTimer(); +char * strlop(char * buf, char delim); + +DllExport int APIENTRY Get_APPLMASK(int Stream); +DllExport int APIENTRY GetStreamPID(int Stream); +DllExport int APIENTRY GetApplFlags(int Stream); +DllExport int APIENTRY GetApplNum(int Stream); +DllExport BOOL APIENTRY GetAllocationState(int Stream); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY RXCount(int Stream); +DllExport int APIENTRY TXCount(int Stream); +DllExport int APIENTRY MONCount(int Stream); +DllExport int APIENTRY GetCallsign(int stream, char * callsign); +DllExport VOID APIENTRY RelBuff(VOID * Msg); +void SaveMH(); +void DRATSPoll(); + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +VOID SetWindowTextSupport(); +int WritetoConsoleSupport(char * buff); +VOID PMClose(); +VOID MySetWindowText(HWND hWnd, char * Msg); +BOOL CreateMonitorWindow(char * MonSize); +VOID FormatTime3(char * Time, time_t cTime); + +char EXCEPTMSG[80] = ""; + +char SIGNONMSG[128] = ""; +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index + + +char WL2KCall[10]; +char WL2KLoc[7]; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + +extern UCHAR ConfigDirectory[260]; + +extern uint64_t timeLoadedMS; + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +DllExport int APIENTRY CloseBPQ32(); +DllExport char * APIENTRY GetLOC(); +DllExport int APIENTRY SessionControl(int stream, int command, int param); + +int DoRefreshWebMailIndex(); + +BOOL APIENTRY Init_IP(); +BOOL APIENTRY Poll_IP(); + +BOOL APIENTRY Init_PM(); +BOOL APIENTRY Poll_PM(); + +BOOL APIENTRY Init_APRS(); +BOOL APIENTRY Poll_APRS(); +VOID HTTPTimer(); + +BOOL APIENTRY Rig_Init(); +BOOL APIENTRY Rig_Close(); +BOOL Rig_Poll(); + +VOID IPClose(); +VOID APRSClose(); +VOID CloseTNCEmulator(); + +VOID Poll_AGW(); +void RHPPoll(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +int * Flag = (int *)&Flag; // for Dump Analysis +int MAJORVERSION=4; +int MINORVERSION=9; + +struct SEM Semaphore = {0, 0, 0, 0}; +struct SEM APISemaphore = {0, 0, 0, 0}; +int SemHeldByAPI = 0; +int LastSemGets = 0; +UINT Sem_eax = 0; +UINT Sem_ebx = 0; +UINT Sem_ecx = 0; +UINT Sem_edx = 0; +UINT Sem_esi = 0; +UINT Sem_edi = 0; + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; +//DllExport long MONDECODEPTR = (long)&MONDECODE; + +extern UCHAR BPQDirectory[]; +extern UCHAR LogDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +static char BPQWinMsg[] = "BPQWindowMessage"; + +static char ClassName[] = "BPQMAINWINDOW"; + +HKEY REGTREE = HKEY_CURRENT_USER; +char REGTREETEXT[100] = "HKEY_CURRENT_USER"; + +UINT BPQMsg=0; + +#define MAXLINELEN 120 +#define MAXSCREENLEN 50 + +#define BGCOLOUR RGB(236,233,216) + +HBRUSH bgBrush = NULL; + +//int LINELEN=120; +//int SCREENLEN=50; + +//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; + +//int lineno=0; +//int col=0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +HANDLE OpenConfigFile(char * file); + +VOID SetupBPQDirectory(); +VOID SendLocation(); + +//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); + +#define TRAY_ICON_ID 1 // ID number for the Notify Icon +#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window + +NOTIFYICONDATA niData; + +int SetupConsoleWindow(); + +BOOL StartMinimized=FALSE; +BOOL MinimizetoTray=TRUE; + +BOOL StatusMinimized = FALSE; +BOOL ConsoleMinimized = FALSE; + +HMENU trayMenu=0; + +HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; + +BOOL FrameMaximized = FALSE; + +BOOL IGateEnabled = TRUE; +extern int ISDelayTimer; // Time before trying to reopen APRS-IS link +extern int ISPort; + +UINT * WINMORTraceQ = NULL; +UINT * SetWindowTextQ = NULL; + +static RECT Rect = {100,100,400,400}; // Console Window Position +RECT FRect = {100,100,800,600}; // Frame +static RECT StatusRect = {100,100,850,500}; // Status Window + +DllExport int APIENTRY DumpSystem(); +DllExport int APIENTRY SaveNodes (); +DllExport int APIENTRY ClearNodes (); +DllExport int APIENTRY SetupTrayIcon(); + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) + +VOID * _Q_REM(VOID *Q, char * File, int Line); + +UINT ReleaseBuffer(UINT *BUFF); + + +VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +DllExport int APIENTRY DeallocateStream(int stream); + +int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); + +int FirstEntry = 1; +BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close +BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe + +BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. + // Used to Kill surplus BPQ32.exe processes + +DWORD Our_PID; // Our Process ID - local variable + +void * InitDone = 0; +int FirstInitDone = 0; +int PerlReinit = 0; +UINT_PTR TimerHandle = 0; +UINT_PTR SessHandle = 0; + +BOOL EventsEnabled = 0; + +unsigned int TimerInst = 0xffffffff; + +HANDLE hInstance = 0; + +int AttachedProcesses = 0; +int AttachingProcess = 0; +HINSTANCE hIPModule = 0; +HINSTANCE hRigModule = 0; + +BOOL ReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL CloseAllNeeded = FALSE; +BOOL NeedWebMailRefresh = FALSE; + +int AttachedPIDList[100] = {0}; + +HWND hWndArray[100] = {0}; +int PIDArray[100] = {0}; +char PopupText[30][100] = {""}; + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MTX; // Top bit indicates use local time +uint64_t MMASK; +UCHAR MUIONLY; + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +char pgm[256]; // Uninitialised so per process + +HANDLE Mutex; + +BOOL PartLine = FALSE; +int pindex = 0; +DWORD * WritetoConsoleQ; + + +LARGE_INTEGER lpFrequency = {0}; +LARGE_INTEGER lastRunTime; +LARGE_INTEGER currentTime; + +int ticksPerMillisec; +int interval; + + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + + +TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; +TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; + + +BOOL ProcessConfig(); +VOID FreeConfig(); + +DllExport int APIENTRY WritetoConsole(char * buff); + +BOOLEAN CheckifBPQ32isLoaded(); +BOOLEAN StartBPQ32(); +DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); +BOOL LoadIPDriver(); +BOOL Send_IP(VOID * Block, DWORD len); +VOID CheckforLostProcesses(); +BOOL LoadRigDriver(); +VOID SaveConfig(); +VOID CreateRegBackup(); +VOID ResolveUpdateThread(); +VOID OpenReportingSockets(); +DllExport VOID APIENTRY CloseAllPrograms(); +DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); +int upnpClose(); + +BOOL IPActive = FALSE; +extern BOOL IPRequired; +BOOL PMActive = FALSE; +extern BOOL PMRequired; +BOOL RigRequired = TRUE; +BOOL RigActive = FALSE; +BOOL APRSActive = FALSE; +BOOL AGWActive = FALSE; +BOOL needAIS = FALSE; +int needADSB = 0; + +extern int AGWPort; + +Tell_Sessions(); + + +typedef int (WINAPI FAR *FARPROCX)(); + +FARPROCX CreateToolHelp32SnapShotPtr; +FARPROCX Process32Firstptr; +FARPROCX Process32Nextptr; + +void LoadToolHelperRoutines() +{ + HINSTANCE ExtDriver=0; + int err; + char msg[100]; + + ExtDriver=LoadLibrary("kernel32.dll"); + + if (ExtDriver == NULL) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); + OutputDebugString(msg); + return; + } + + CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); + Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); + Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); + + if (CreateToolHelp32SnapShotPtr == 0) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); + OutputDebugString(msg); + return; + } +} + +BOOL GetProcess(int ProcessID, char * Program) +{ + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + int p; + + if (CreateToolHelp32SnapShotPtr==0) + { + return (TRUE); // Routine not available + } + // Take a snapshot of all processes in the system. + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + if (ProcessID==pe32.th32ProcessID) + { + // if running on 98, program contains the full path - remove it + + for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) + { + if (pe32.szExeFile[p]=='\\') + { + break; + } + } + p++; + + sprintf(Program,"%s", &pe32.szExeFile[p]); + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + + sprintf(Program,"PID %d Not Found", ProcessID); + CloseHandle( hProcessSnap ); + return(FALSE); +} + +BOOL IsProcess(int ProcessID) +{ + // Check that Process exists + + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available + + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return(TRUE); // Don't know, so assume ok + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return(TRUE); // Don't know, so assume ok + } + + do + { + if (ProcessID==pe32.th32ProcessID) + { + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return(FALSE); +} + +#include "DbgHelp.h" + +VOID MonitorThread(int x) +{ + // Thread to detect killed processes. Runs in process owning timer. + + // Obviously can't detect loss of timer owning thread! + + do + { + if (Semaphore.Gets == LastSemGets && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + // Write a minidump + + WriteMiniDump(); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); + CheckforLostProcesses(); + + } while (TRUE); +} + +VOID CheckforLostProcesses() +{ + UCHAR buff[100]; + char Log[80]; + int i, n, ProcessID; + + for (n=0; n < AttachedProcesses; n++) + { + ProcessID=AttachedPIDList[n]; + + if (!IsProcess(ProcessID)) + { + // Process has died - Treat as a detach + + sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); + OutputDebugString(Log); + + // Remove Tray Icon Entry + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + // If process had the semaphore, release it + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) + { + DeallocateStream(i); + } + } + + if (TimerInst == ProcessID) + { + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; +// Tell_Sessions(); + OutputDebugString("BPQ32 Process was running timer \n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + + } + + // Remove this entry from PID List + + for (i=n; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + AttachedProcesses--; + + sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); + OutputDebugString(buff); + } + } +} +VOID MonitorTimerThread(int x) +{ + // Thread to detect killed timer process. Runs in all other BPQ32 processes. + + do { + + Sleep(60000); + + if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) + { + // Timer owning Process has died - Force a new timer to be created + // New timer thread will detect lost process and tidy up + + Debugprintf("BPQ32 Process %d with Timer died", TimerInst); + + // If process was holding the semaphore, release it + + if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + +// KillTimer(NULL,TimerHandle); +// TimerHandle=0; +// TimerInst=0xffffffff; +// Tell_Sessions(); + + CheckforLostProcesses(); // Normally only done in timer thread, which is now dead + + // Timer can only run in BPQ32.exe + + TimerInst=0xffffffff; // So we dont keep doing it + TimerHandle = 0; // So new process attaches + + if (Closing == FALSE && AttachingProcess == FALSE) + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + +// if (MinimizetoTray) +// Shell_NotifyIcon(NIM_DELETE,&niData); + } + + } while (TRUE); +} + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); + +VOID TimerProcX(); + +VOID CALLBACK TimerProc( + HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT idEvent, // timer identifier + DWORD dwTime) // current system time +{ + KillTimer(NULL,TimerHandle); + TimerProcX(); + TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); +} +VOID TimerProcX() +{ + struct _EXCEPTION_POINTERS exinfo; + + // + // Get semaphore before proceeeding + // + + GetSemaphore(&Semaphore, 2); + + // Get time since last run + + QueryPerformanceCounter(¤tTime); + + interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; + lastRunTime.QuadPart = currentTime.QuadPart; + + //Debugprintf("%d", interval); + + // Process WINMORTraceQ + + while (WINMORTraceQ) + { + UINT * Buffer = Q_REM(&WINMORTraceQ); + struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; + int Len = Buffer[2]; + char * Msg = (char *)&Buffer[3]; + + WritetoTraceSupport(TNC, Msg, Len); + RelBuff(Buffer); + } + + if (SetWindowTextQ) + SetWindowTextSupport(); + + while (WritetoConsoleQ) + { + UINT * Buffer = Q_REM(&WritetoConsoleQ); + WritetoConsoleSupport((char *)&Buffer[2]); + RelBuff(Buffer); + } + + strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); + + __try + { + + if (trayMenu == NULL) + SetupTrayIcon(); + + // See if reconfigure requested + + if (CloseAllNeeded) + { + CloseAllNeeded = FALSE; + CloseAllPrograms(); + } + + if (ReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + WSADATA WsaData; // receives data from WSAStartup + RECT cRect; + + ReconfigFlag = FALSE; + + SetupBPQDirectory(); + + WritetoConsole("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + GetWindowRect(FrameWnd, &FRect); + + SaveWindowPos(70); // Rigcontrol + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + WSACleanup(); + + WL2KReports = NULL; + + Sleep(2000); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); // Restart Ports + + SetApplPorts(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + + PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + // Free the APRS Appl Q + + APPL_Q = 0; + + OpenReportingSockets(); + + WritetoConsole("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 0); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 0); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + } + + + if (RigReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + RigReconfigFlag = FALSE; + CloseDriverWindow(70); + Rig_Close(); + Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close + RigActive = Rig_Init(); + + WritetoConsole("Rigcontrol Reconfiguration Complete\n"); + } + } + + if (APRSReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + WritetoConsole("APRS Reconfiguration Complete\n"); + } + } + + } + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + strcpy(EXCEPTMSG, "Timer Processing"); + + __try + { + if (IPActive) Poll_IP(); + if (PMActive) Poll_PM(); + if (RigActive) Rig_Poll(); + + if (NeedWebMailRefresh) + DoRefreshWebMailIndex(); + + CheckGuardZone(); + + if (APRSActive) + { + Poll_APRS(); + CheckGuardZone(); + } + + CheckWL2KReportTimer(); + + CheckGuardZone(); + + TIMERINTERRUPT(); + + CheckGuardZone(); + + FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "HTTP Timer Processing"); + + HTTPTimer(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + CheckGuardZone(); + + return; +} + +HANDLE NPHandle; + +int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; +int (WINAPI FAR *EnumProcessesPtr)() = NULL; + +FirstInit() +{ + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + + // First Time Ports and Timer init + + // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain + + // Call wsastartup - most systems need winsock, and duplicate statups could be a problem + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver=LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + timeLoadedMS = GetTickCount(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + WritetoConsole("\n"); + WritetoConsole("Port Initialisation Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (APRSActive) + { + hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, + 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); + + CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, + 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGate State - Disconnected", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "GPS Off", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); + } + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + _beginthread(MonitorThread,0,0); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser + + if (ADIFLogEnabled) + ADIFWriteFreqList(); + + OutputDebugString("BPQ32 Port Initialisation Complete\n"); + + if (needAIS) + initAIS(); + + if (needADSB) + initADSB(); + + return 0; +} + +Check_Timer() +{ + if (Closing) + return 0; + + if (Semaphore.Flag) + return 0; + + if (InitDone == (void *)-1) + { + GetSemaphore(&Semaphore, 3); + Sleep(15000); + FreeSemaphore(&Semaphore); + exit (0); + } + + if (FirstInitDone == 0) + { + GetSemaphore(&Semaphore, 3); + + if (_stricmp(pgm, "bpq32.exe") == 0) + { + FirstInit(); + FreeSemaphore(&Semaphore); + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + FirstInitDone=1; // Only init in BPQ32.exe + return 0; + } + else + { + FreeSemaphore(&Semaphore); + return 0; + } + } + + if (TimerHandle == 0 && FirstInitDone == 1) + { + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + // Only attach timer to bpq32.exe + + if (_stricmp(pgm, "bpq32.exe") != 0) + { + return 0; + } + + GetSemaphore(&Semaphore, 3); + OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); + + if (!ProcessConfig()) + { + ShowWindow(hConsWnd, SW_RESTORE); + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); + + exit (0); + } + + GetVersionInfo("bpq32.dll"); + + SetupConsoleWindow(); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + Consoleprintf("Reinitialising..."); + + SetupBPQDirectory(); + + Sleep(1000); // Allow time for sockets to close + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver = LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + Start(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + NODESINPROGRESS = 0; + CURRENTNODE = 0; + + SetApplPorts(); + + WritetoConsole("\n\nPort Reinitialisation Complete\n"); + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + RigActive = Rig_Init(); + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + FreeConfig(); + + _beginthread(MonitorThread,0,0); + + ReportTimer = 0; + + OpenReportingSockets(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + return (1); + } + + return (0); +} + +DllExport INT APIENTRY CheckTimer() +{ + return Check_Timer(); +} + +Tell_Sessions() +{ + // + // Post a message to all listening sessions, so they call the + // API, and cause a new timer to be allocated + // + HWND hWnd; + int i; + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) + { + hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; + PostMessage(hWnd, BPQMsg,i, 1); + PostMessage(hWnd, BPQMsg,i, 2); + } + } + return (0); +} + +BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) +{ + DWORD n; + char buf[350]; + + int i; + unsigned int ProcessID; + + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + + switch( ul_reason_being_called ) + { + case DLL_PROCESS_ATTACH: + + if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(LINKTABLE) != LINK_TABLE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); + return 0; + } + if (sizeof(struct ROUTE) != ROUTE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + GetSemaphore(&Semaphore, 4); + + BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; + + LoadToolHelperRoutines(); + + Our_PID = GetCurrentProcessId(); + + QueryPerformanceFrequency(&lpFrequency); + + ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; + + lastRunTime.QuadPart = lpFrequency.QuadPart; + + GetProcess(Our_PID, pgm); + + if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 1; + } + + if (_stricmp(pgm,"BPQ32.exe") == 0) + BPQ32_EXE = TRUE; + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQMail.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = TRUE; + + if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end + { + FirstEntry = 0; + if (BPQ32_EXE) + CloseLast = FALSE; + } + else + { + if (BPQ32_EXE && AttachingProcess == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); + return 0; + } + } + + if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) + { + MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQUIUtil.exe") == 0) + { + MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + { + MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + AuthorisedProgram = TRUE; + + if (InitDone == 0) + { +// #pragma warning(push) +// #pragma warning(disable : 4996) + +// if (_winver < 0x0600) +// #pragma warning(pop) +// { +// // Below Vista +// +// REGTREE = HKEY_LOCAL_MACHINE; +// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); +// } + + hInstance=hInst; + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); + FreeSemaphore(&Semaphore); + + if (i != IDOK) return (0); + + CloseHandle(Mutex); + } + + if (!BPQ32_EXE) + { + if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed + { + // Wasn't Loaded, so we have started it, and should let it init system + + goto SkipInit; + } + } + + GetVersionInfo("bpq32.dll"); + + sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", + TextVerstring, Datestring, VerCopyright); + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); + + SetupConsoleWindow(); + SetupBPQDirectory(); + + if (!ProcessConfig()) + { + StartMinimized = FALSE; + MinimizetoTray = FALSE; + ShowWindow(FrameWnd, SW_MAXIMIZE); + ShowWindow(hConsWnd, SW_MAXIMIZE); + ShowWindow(StatusWnd, SW_HIDE); + + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); + + return (0); + } + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + if (Start() !=0) + { + Sleep(3000); + FreeSemaphore(&Semaphore); + return (0); + } + else + { + SetApplPorts(); + + GetUIConfig(); + + InitDone = &InitDone; + BPQMsg = RegisterWindowMessage(BPQWinMsg); +// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); +// TimerInst=GetCurrentProcessId(); + +/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + +*/ + Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// CreatePipe(&H1,&H2,NULL,1000); + +// GetLastError(); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + +// GetLastError(); + +/* + // + // Read SYSOP password + // + + if (PWTEXT[0] == 0) + { + handle = OpenConfigFile("PASSWORD.BPQ"); + + if (handle == INVALID_HANDLE_VALUE) + { + WritetoConsole("Can't open PASSWORD.BPQ\n"); + PWLen=0; + PWTEXT[0]=0; + } + else + { + ReadFile(handle,PWTEXT,78,&n,NULL); + CloseHandle(handle); + } + } +*/ + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + PWLen=i; + + } + } + else + { + if (InitDone != &InitDone) + { + MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + } + + // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread +SkipInit: + + _beginthread(MonitorTimerThread,0,0); + + FreeSemaphore(&Semaphore); + + AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); + + if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + // Set up local variables + + MCOM=1; + MTX=1; + MMASK=0xffffffffffffffff; + +// if (StartMinimized) +// if (MinimizetoTray) +// ShowWindow(FrameWnd, SW_HIDE); +// else +// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); +// else +// ShowWindow(FrameWnd, SW_RESTORE); + + return 1; + + case DLL_THREAD_ATTACH: + + return 1; + + case DLL_THREAD_DETACH: + + return 1; + + case DLL_PROCESS_DETACH: + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = FALSE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = FALSE; + + ProcessID=GetCurrentProcessId(); + + Debugprintf("BPQ32 Process %d Detaching", ProcessID); + + // Release any streams that the app has failed to release + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) + { + // If connected, disconnect + + SessionControl(i, 2, 0); + DeallocateStream(i); + } + } + + // Remove any Tray Icon Entries + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + char Log[80]; + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + if (Mutex) CloseHandle(Mutex); + + // Remove our entry from PID List + + for (i=0; i< AttachedProcesses; i++) + if (AttachedPIDList[i] == ProcessID) + break; + + for (; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + + AttachedProcesses--; + + if (TimerInst == ProcessID) + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + OutputDebugString("BPQ32 Process with Timer closing\n"); + + // Call Port Close Routines + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + WSACleanup(); + WSAGetLastError(); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + if (hConsWnd) DestroyWindow(hConsWnd); + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + else + { + // Not Timer Process + + if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left + { + Debugprintf("Only BPQ32.exe running - close it"); + CloseAllNeeded = TRUE; + } + } + + if (AttachedProcesses < 2) + { + if (AUTOSAVE) + SaveNodes(); + if (AUTOSAVEMH) + SaveMH(); + + if (needAIS) + SaveAIS(); + } + if (AttachedProcesses == 0) + { + Closing = TRUE; + KillTimer(NULL,TimerHandle); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + // Unload External Drivers + + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) + FreeLibrary(PORTVEC->DLLhandle); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + } + } + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + return 1; + } + return 1; +} + +DllExport int APIENTRY CloseBPQ32() +{ + // Unload External Drivers + + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + int ProcessID = GetCurrentProcessId(); + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + if (TimerInst == ProcessID) + { + OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + CloseTNCEmulator(); + WSACleanup(); + + if (hConsWnd) DestroyWindow(hConsWnd); + + Debugprintf("AttachedProcesses %d ", AttachedProcesses); + + if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + + return 0; +} + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); + +VOID SetupBPQDirectory() +{ + HKEY hKey = 0; + HKEY hKeyIn = 0; + HKEY hKeyOut = 0; + int disp; + int retCode,Type,Vallen=MAX_PATH,i; + char msg[512]; + char ValfromReg[MAX_PATH] = ""; + char DLLName[256]="Not Known"; + char LogDir[256]; + char Time[64]; + +/* +•NT4 was/is '4' +•Win 95 is 4.00.950 +•Win 98 is 4.10.1998 +•Win 98 SE is 4.10.2222 +•Win ME is 4.90.3000 +•2000 is NT 5.0.2195 +•XP is actually 5.1 +•Vista is 6.0 +•Win7 is 6.1 + + i = _osver; / Build + i = _winmajor; + i = _winminor; +*/ +/* +#pragma warning(push) +#pragma warning(disable : 4996) + +if (_winver < 0x0600) +#pragma warning(pop) + { + // Below Vista + + REGTREE = HKEY_LOCAL_MACHINE; + strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); + ValfromReg[0] = 0; + } + else +*/ + { + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); + } + else + { + // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_READ, + &hKeyIn); + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); + + // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); + + if (retCode != ERROR_SUCCESS) + if (hKeyIn) + CopyReg(hKeyIn, hKeyOut); + + RegCloseKey(hKeyIn); + RegCloseKey(hKeyOut); + } + } + + GetModuleFileName(hInstance,DLLName,256); + + BPQDirectory[0]=0; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + // Try "BPQ Directory" + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + + if (ValfromReg[0] == 0) + { + // BPQ Directory absent or = "" - try "Config File Location" + + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey,"Config File Location",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + } + + if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + + ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); + + // Also get "BPQ Program Directory" + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); + + // And Log Directory + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); + + RegCloseKey(hKey); + } + + strcpy(ConfigDirectory, BPQDirectory); + + if (LogDirectory[0] == 0) + strcpy(LogDirectory, BPQDirectory); + + if (BPQProgramDirectory[0] == 0) + strcpy(BPQProgramDirectory, BPQDirectory); + + sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); + WritetoConsole(msg); + OutputDebugString(msg); + FormatTime3(Time, time(NULL)); + sprintf(msg,"Loaded %s\n", Time); + WritetoConsole(msg); + OutputDebugString(msg); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#if _MSC_VER >= 1400 + +#define _winmajor 6 +#define _winminor 0 + +#endif + + i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); + +#pragma warning(pop) + + WritetoConsole(msg); + OutputDebugString(msg); + + i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); + WritetoConsole(&msg[6]); + msg[i-1]=0; + OutputDebugString(msg); + + // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, + // so will write the wrong tree on ) + + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); + } + else + { + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); + + RegCloseKey(hKey); + } + + // Make sure Logs Directory exists + + sprintf(LogDir, "%s/Logs", LogDirectory); + + CreateDirectory(LogDir, NULL); + + return; +} + +HANDLE OpenConfigFile(char *fn) +{ + HANDLE handle; + UCHAR Value[MAX_PATH]; + FILETIME LastWriteTime; + SYSTEMTIME Time; + char Msg[256]; + + + // If no directory, use current + if (BPQDirectory[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,fn); + } + + handle = CreateFile(Value, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + GetFileTime(handle, NULL, NULL, &LastWriteTime); + FileTimeToSystemTime(&LastWriteTime, &Time); + + sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, + Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); + + OutputDebugString(Msg); + + return(handle); +} + +#ifdef _WIN64 +int BPQHOSTAPI() +{ + return 0; +} +#endif + + +DllExport int APIENTRY GETBPQAPI() +{ + return (int)BPQHOSTAPI; +} + +//DllExport UINT APIENTRY GETMONDECODE() +//{ +// return (UINT)MONDECODE; +//} + + +DllExport INT APIENTRY BPQAPI(int Fn, char * params) +{ + +/* +; +; BPQ HOST MODE SUPPORT CODE +; +; 22/11/95 +; +; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED +; (OS2 VERSION HAD UPSET KANT VERISON +; +; +*/ + + +/* + + BPQHOSTPORT: +; +; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS +; +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH='B',AL='P',BH='Q',BL=' ' +; DH = major version number and DL = minor version number. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:SI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:DI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:DI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:SI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:DI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:SI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 13 Allocate/deallocate stream +; If AL=0, return first free stream +; If AL>0, CL=1, Allocate stream. If aleady allocated, +; return CX nonzero, else allocate, and return CX=0 +; If AL>0, CL=2, Release stream +; +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer + + +*/ + + + switch(Fn) + { + + case CHECKLOADED: + + params[0]=MAJORVERSION; + params[1]=MINORVERSION; + params[2]=QCOUNT; + + return (1); + } + return 0; +} + +DllExport int APIENTRY InitSwitch() +{ + return (0); +} + +/*DllExport int APIENTRY SwitchTimer() +{ + GetSemaphore((&Semaphore); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + return (0); +} +*/ +DllExport int APIENTRY GetFreeBuffs() +{ +// Returns number of free buffers +// (BPQHOST function 7 (part)). + return (QCOUNT); +} + +DllExport UCHAR * APIENTRY GetNodeCall() +{ + return (&MYNODECALL); +} + + +DllExport UCHAR * APIENTRY GetNodeAlias() +{ + return (&MYALIASTEXT[0]); +} + +DllExport UCHAR * APIENTRY GetBBSCall() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); +} + + +DllExport UCHAR * APIENTRY GetBBSAlias() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); +} + +DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) +{ + if (Appl < 1 || Appl > NumberofAppls ) return; + + strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); +} + +BOOL UpdateNodesForApp(int Appl); + +DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + +DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + + +DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) +{ + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + +BOOL UpdateNodesForApp(int Appl) +{ + int App=Appl-1; + int DestLen = sizeof (struct DEST_LIST); + int n = MAXDESTS; + + struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; + APPLCALLS * APPL=&APPLCALLTABLE[App]; + + if (DEST == NULL) + { + // No dest at the moment. If we have valid call and Qual, create an entry + + if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + + GetSemaphore(&Semaphore, 5); + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + break; + } + + if (n == 0) + { + // no dests + + FreeSemaphore(&Semaphore); + return FALSE; + } + + NUMBEROFNODES++; + APPL->NODEPOINTER = DEST; + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + + return TRUE; + } + + // We have a destination. If Quality is zero, remove it, else update it + + if (APPLCALLTABLE[App].APPLQUAL == 0) + { + GetSemaphore(&Semaphore, 6); + + REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + + APPL->NODEPOINTER=NULL; + + FreeSemaphore(&Semaphore); + return FALSE; + + } + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + GetSemaphore(&Semaphore, 7); + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + return TRUE; + +} + + +DllExport UCHAR * APIENTRY GetSignOnMsg() +{ + return (&SIGNONMSG[0]); +} + + +DllExport HKEY APIENTRY GetRegistryKey() +{ + return REGTREE; +} + +DllExport char * APIENTRY GetRegistryKeyText() +{ + return REGTREETEXT;; +} + +DllExport UCHAR * APIENTRY GetBPQDirectory() +{ + while (BPQDirectory[0] == 0) + { + Debugprintf("BPQ Directory not set up - waiting"); + Sleep(1000); + } + return (&BPQDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetProgramDirectory() +{ + return (&BPQProgramDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetLogDirectory() +{ + return (&LogDirectory[0]); +} + +// Version for Visual Basic + +DllExport char * APIENTRY CopyBPQDirectory(char * dir) +{ + return (strcpy(dir,BPQDirectory)); +} + +DllExport int APIENTRY GetMsgPerl(int stream, char * msg) +{ + int len,count; + + GetMsg(stream, msg, &len, &count ); + + return len; +} + +int Rig_Command(int Session, char * Command); + +BOOL Rig_CommandInt(int Session, char * Command) +{ + return Rig_Command(Session, Command); +} + +DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) +{ + BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; + return (0); +} + +#define L4USER 0 + +BPQVECSTRUC * PORTVEC ; + +VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + HINSTANCE ExtDriver=0; + char msg[128]; + int err=0; + HKEY hKey=0; + UCHAR Value[MAX_PATH]; + + // If no directory, use current + + if (BPQDirectory[0] == 0) + { + strcpy(Value,PORTVEC->PORT_DLL_NAME); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,PORTVEC->PORT_DLL_NAME); + } + + // Several Drivers are now built into bpq32.dll + + _strupr(Value); + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + +// if (strstr(Value, "SOUNDMODEM")) +// return SoundModemExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "UIARQ")) + return UIARQExtInit; + +// if (strstr(Value, "BAYCOM")) +// return (UINT) BaycomExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + if (strstr(Value, "6PACK")) + return SIXPACKExtInit; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[100]; + char Addr1[100]; + char Addr2[100]; + char City[100]; + char State[100]; + char Country[100]; + char PostCode[100]; + char Email[100]; + char Website[100]; + char Phone[100]; + char Data[100]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\"", + + WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len += sprintf(&RegLine[len], "\\\r\n", RegLine); + strcat(RegLine, "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len += sprintf(&RegLine[len], "%02x,", Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len += sprintf(&RegLine[len], "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len += sprintf(&RegLine[len], "%02x,", Value[k]); + if (len > 76) + { + len += sprintf(&RegLine[len], "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len += sprintf(&RegLine[len], "00,"); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(70); // Rigcontrol + + + if (hIPResWnd) + SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); + + SaveHostSessions(); +} + +DllExport BOOL APIENTRY CheckIfOwner() +{ + // + // Returns TRUE if current process is root process + // that loaded the DLL + // + + if (TimerInst == GetCurrentProcessId()) + + return (TRUE); + else + return (FALSE); +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +int GetListeningPortsPID(int Port) +{ + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + DWORD n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + + if (TcpTable == NULL) + return 0; + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } + return 0; // Not found +} + +DllExport char * APIENTRY GetLOC() +{ + return LOC; +} + +DllExport void APIENTRY GetLatLon(double * lat, double * lon) +{ + *lat = LatFromLOC; + *lon = LonFromLOC; + return; +} + + +// UZ7HO Dll PTT interface + +// 1 ext_PTT_info +// 2 ext_PTT_settings +// 3 ext_PTT_OFF +// 4 ext_PTT_ON +// 5 ext_PTT_close +// 6 ext_PTT_open + +extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +int WINAPI ext_PTT_info() +{ + return 0; +} + +int WINAPI ext_PTT_settings() +{ + return 0; +} + +int WINAPI ext_PTT_OFF(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +int WINAPI ext_PTT_ON(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 1, 0); + + return 0; +} +int WINAPI ext_PTT_close() +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +DllExport INT WINAPI ext_PTT_open() +{ + return 1; +} + +char * stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup(ch1); + chN2 = _strdup(ch2); + + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + chNdx = chN2; + + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + + chNdx = strstr(chN1, chN2); + + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + + free (chN1); + free (chN2); + return chRet; +} + diff --git a/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base b/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base index 78e8a98..bc0a854 100644 --- a/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base +++ b/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base @@ -1,2179 +1,2179 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Mail and Chat Server for BPQ32 Packet Switch -// -// FBB Forwarding Routines - -#include "bpqmail.h" - -#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) -void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); - - -void DeleteRestartData(CIRCUIT * conn); - -int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); -void MQTTMessageEvent(void* message); - -int MaxRXSize = 99999; -int MaxTXSize = 99999; - -struct FBBRestartData ** RestartData = NULL; -int RestartCount = 0; - -struct B2RestartData ** B2RestartRecs = NULL; -int B2RestartCount = 0; - -extern char ProperBaseDir[]; - -char RestartDir[MAX_PATH] = ""; - -void GetRestartData() -{ - int i; - struct FBBRestartData Restart; - struct FBBRestartData * RestartRec; - char MsgFile[MAX_PATH]; - FILE * hFile; - int FileSize; - struct stat STAT; - size_t ReadLen = 0; - time_t Age; - - strcpy(RestartDir, MailDir); - strcat(RestartDir, "/Restart"); - - // Make sure RestartDir exists - -#ifdef WIN32 - CreateDirectory(RestartDir, NULL); // Just in case -#else - mkdir(RestartDir, S_IRWXU | S_IRWXG | S_IRWXO); - chmod(RestartDir, S_IRWXU | S_IRWXG | S_IRWXO); -#endif - - // look for restart files. These will be numbered from 1 up - - for (i = 1; 1; i++) - { - sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, i); - - if (stat(MsgFile, &STAT) == -1) - break; - - FileSize = STAT.st_size; - - Age = time(NULL) - STAT.st_ctime; - - if (Age > 86400 * 2) // Max 2 days - continue; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - break; - - // Read Restart Record - - fread(&Restart, 1, sizeof(struct FBBRestartData), hFile); - - if ((Restart.MailBufferSize + sizeof(struct FBBRestartData)) != FileSize) // Duff file - { - fclose(hFile); - break; - } - - RestartRec = zalloc(sizeof (struct FBBRestartData)); - - GetSemaphore(&AllocSemaphore, 0); - - RestartData = realloc(RestartData,(++RestartCount+1) * sizeof(void *)); - RestartData[RestartCount] = RestartRec; - - FreeSemaphore(&AllocSemaphore); - - memcpy(RestartRec, &Restart, sizeof(struct FBBRestartData)); - RestartRec->MailBuffer = malloc(RestartRec->MailBufferSize); - ReadLen = fread(RestartRec->MailBuffer, 1, RestartRec->MailBufferSize, hFile); - - Logprintf(LOG_BBS, 0, '?', "Restart Data for %s %s Len %d Loaded", RestartRec->Call, RestartRec->bid, RestartRec->length); - fclose(hFile); - } -} - - -void SaveRestartData() -{ - // Save restart data to file so we can reload on restart - // Restart data has pointers to buffers so we must save copy of data and reconstitue on restart - - // Delete and resave all restart data to keep restart directory clean - - int i, n = 1; - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - struct FBBRestartData * RestartRec = NULL; - struct stat STAT; - time_t NOW = time(NULL); - - - for (i = 1; 1; i++) - { - sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, i); - - if (stat(MsgFile, &STAT) == -1) - break; - - DeleteFile(MsgFile); - } - - for (i = 1; i <= RestartCount; i++) - { - RestartRec = RestartData[i]; - - if (RestartRec == 0) - return; // Shouldn't happen! - - if ((NOW - RestartRec->TimeCreated) > 86400 * 2) // Max 2 days - continue; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, n++); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(RestartRec, 1, sizeof(struct FBBRestartData), hFile); // Save Header - WriteLen = fwrite(RestartRec->MailBuffer, 1, RestartRec->MailBufferSize, hFile); // Save Data - fclose(hFile); - } - } -} -VOID FBBputs(CIRCUIT * conn, char * buf) -{ - // Sends to user and logs - - int len = (int)strlen(buf); - - WriteLogLine(conn, '>', buf, len -1, LOG_BBS); - - QueueMsg(conn, buf, len); - - if (conn->BBSFlags & NEEDLF) - QueueMsg(conn, "\n", 1); -} - - -VOID ProcessFBBLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len) -{ - struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block - int i; - int Index = 0; // Message Type Index for Stats - char * ptr; - char * Context; - char seps[] = " \r"; - int RestartPtr; - char * Respptr; - BOOL AllRejected = TRUE; - char * MPS; - char * ROChar; - - if (conn->Flags & GETTINGMESSAGE) - { - ProcessMsgLine(conn, user, Buffer, len); - if (conn->Flags & GETTINGMESSAGE) - - // Still going - return; - - SetupNextFBBMessage(conn); - return; - } - - if (conn->Flags & GETTINGTITLE) - { - ProcessMsgTitle(conn, user, Buffer, len); - return; - } - - // Should be FA FB F> FS FF FQ - - if (Buffer[0] == ';') // winlink comment or BPQ Type Select - { - if (memcmp(Buffer, "; MSGTYPES", 7) == 0) - { - char * ptr; - - conn->SendB = conn->SendP = conn->SendT = FALSE; - - ptr = strchr(&Buffer[10], 'B'); - - if (ptr) - { - conn->SendB = TRUE; - conn->MaxBLen = atoi(++ptr); - if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; - } - - ptr = strchr(&Buffer[10], 'T'); - - if (ptr) - { - conn->SendT = TRUE; - conn->MaxTLen = atoi(++ptr); - if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; - } - ptr = strchr(&Buffer[10], 'P'); - - if (ptr) - { - conn->SendP = TRUE; - conn->MaxPLen = atoi(++ptr); - if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; - } - return; - } - - // Other ; Line - Ignore - - return; - } - - if (Buffer[0] != 'F') - { - if (strstr(Buffer, "*** Profanity detected") || strstr(Buffer, "*** Unknown message sender")) - { - // Winlink Check - hold message - - if (conn->FBBMsgsSent) - HoldSentMessages(conn, user); - } - - if (conn->BBSFlags & DISCONNECTING) - return; // Ignore if disconnect aleady started - - BBSputs(conn, "*** Protocol Error - Line should start with 'F'\r"); - Flush(conn); - Sleep(500); - conn->BBSFlags |= DISCONNECTING; - Disconnect(conn->BPQStream); - - return; - } - - switch (Buffer[1]) - { - case 'F': - - // Request Reverse - - if (conn->FBBMsgsSent) - FlagSentMessages(conn, user); - - if (!FBBDoForward(conn)) // Send proposal if anthing to forward - { - FBBputs(conn, "FQ\r"); - - conn->BBSFlags |= DISCONNECTING; - - // LinFBB needs a Disconnect Here - - if (conn->BPQBBS) - return; // BPQ will close when it sees FQ. Close collisions aren't good! - - if ((conn->SessType & Sess_PACTOR) == 0) - conn->CloseAfterFlush = 20; // 2 Secs - else - conn->CloseAfterFlush = 20; // PACTOR/WINMOR drivers support deferred disc so 5 secs should be enough - } - return; - - case 'S': - - // Proposal response - - Respptr=&Buffer[2]; - - for (i=0; i < conn->FBBIndex; i++) - { - FBBHeader = &conn->FBBHeaders[i]; - - if (FBBHeader->MsgType == 'P') - Index = PMSG; - else if (FBBHeader->MsgType == 'B') - Index = BMSG; - else if (FBBHeader->MsgType == 'T') - Index = TMSG; - - Respptr++; - - if (*Respptr == 'E') - { - // Rejected - - Logprintf(LOG_BBS, conn, '?', "Proposal %d Rejected by far end", i + 1); - } - - if ((*Respptr == '-') || (*Respptr == 'N') || (*Respptr == 'R') || (*Respptr == 'E')) // Not wanted - { - user->Total.MsgsRejectedOut[Index]++; - - // Zap the entry - - if (conn->Paclink || conn->RMSExpress || conn->PAT) // Not using Bit Masks - { - // Kill Messages sent to paclink/RMS Express unless BBS FWD bit set - - // What if WLE retrieves P message that is queued to differnet BBS? - // if we dont kill it will be offered again - - if (FBBHeader->FwdMsg->type == 'P' || (check_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber) == 0)) - FlagAsKilled(FBBHeader->FwdMsg, FALSE); - } - - clear_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(FBBHeader->FwdMsg->forw, user->BBSNumber); - - FBBHeader->FwdMsg->Locked = 0; // Unlock - - // Shouldn't we set P messages as Forwarded - // (or will check above have killed it if it is P with other FWD bits set) - // Maybe better to be safe !! - - if (FBBHeader->FwdMsg->type == 'P' || memcmp(FBBHeader->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - FBBHeader->FwdMsg->status = 'F'; // Mark as forwarded - FBBHeader->FwdMsg->datechanged=time(NULL); - } - - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); - - conn->UserPointer->ForwardingInfo->MsgCount--; - - SaveMessageDatabase(); - continue; - } - - // FBB uses H for HOLD, but I've never seen it. RMS Express sends H for Defer. - - - if (*Respptr == '=' || *Respptr == 'L' || (*Respptr == 'H' && conn->RMSExpress)) // Defer - { - // Remove entry from forwarding block - - FBBHeader->FwdMsg->Defered = 4; // Don't retry for the next few forward cycles - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); - continue; - } - - conn->RestartFrom = 0; // Assume Restart from - - if ((*Respptr == '!') || (*Respptr == 'A')) - { - // Restart - - char Num[10]; - char *numptr=&Num[0]; - - Respptr++; - - while (isdigit(*Respptr)) - { - *(numptr++) = *(Respptr++); - } - *numptr = 0; - - conn->RestartFrom = atoi(Num); - - *(--Respptr) = '+'; // So can drop through - } - - // FBB uses H for HOLD, but I've never seen it. RMS Express sends H for Defer. RMS use trapped above - - if ((*Respptr == '+') || (*Respptr == 'Y') || (*Respptr == 'H')) - { - struct tm * tm; - time_t now; - char * MsgBytes; - - conn->FBBMsgsSent = TRUE; // Messages to flag as complete when next command received - AllRejected = FALSE; - - if (conn->BBSFlags & FBBForwarding) - { - if (conn->BBSFlags & FBBB2Mode) - SendCompressedB2(conn, FBBHeader); - else - SendCompressed(conn, FBBHeader->FwdMsg); - } - else - { - nodeprintf(conn, "%s\r\n", FBBHeader->FwdMsg->title); - - MsgBytes = ReadMessageFile(FBBHeader->FwdMsg->number); - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r\n"); - FBBHeader->FwdMsg->length = (int)strlen(MsgBytes); - } - - now = time(NULL); - - tm = gmtime(&now); - - nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - FBBHeader->FwdMsg->number, BBSName, HRoute, RlineVer); - - if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - BBSputs(conn, "\r\n"); - - QueueMsg(conn, MsgBytes, FBBHeader->FwdMsg->length); - free(MsgBytes); - - user->Total.MsgsSent[Index]++; - user->Total.BytesForwardedOut[Index] += FBBHeader->FwdMsg->length; - - nodeprintf(conn, "%c\r\n", 26); - } - continue; - } - BBSputs(conn, "*** Protocol Error - Invalid Proposal Response'\r"); - } - - conn->FBBIndex = 0; // ready for next block; - conn->FBBChecksum = 0; - - - if (AllRejected && (conn->RMSExpress || conn->PAT)) - { - // RMS Express and PAT don't send FF or proposal after rejecting all messages - - FBBputs(conn, "FF\r"); - } - - return; - - case 'Q': - - if (conn->FBBMsgsSent) - FlagSentMessages(conn, user); - - conn->BBSFlags |= DISCONNECTING; - - Disconnect(conn->BPQStream); - return; - - case 'A': // Proposal - case 'B': // Proposal - - if (conn->FBBMsgsSent) - FlagSentMessages(conn, user); // Mark previously sent messages - - if (conn->DoReverse == FALSE) // Dont accept messages - return; - - // Accumulate checksum - - for (i=0; i< len; i++) - { - conn->FBBChecksum+=Buffer[i]; - } - - // Parse Header - - // Find free line - - for (i = 0; i < 5; i++) - { - FBBHeader = &conn->FBBHeaders[i]; - - if (FBBHeader->Format == 0) - break; - } - - if (i == 5) - { - BBSputs(conn, "*** Protocol Error - Too Many Proposals\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - } - - //FA P GM8BPQ G8BPQ G8BPQ 2209_GM8BPQ 8 - - FBBHeader->Format = Buffer[1]; - - ptr = strtok_s(&Buffer[3], seps, &Context); - - if (ptr == NULL) goto badparam; - - if (strlen(ptr) != 1) goto badparam; - - FBBHeader->MsgType = *ptr; - - if (FBBHeader->MsgType == 'P') - Index = PMSG; - else if (FBBHeader->MsgType == 'B') - Index = BMSG; - else if (FBBHeader->MsgType == 'T') - Index = TMSG; - - - ptr = strtok_s(NULL, seps, &Context); - - if (ptr == NULL) goto badparam; - strlop(ptr, '-'); // Remove any (illegal) ssid - - if (strlen(ptr) > 6 ) goto badparam; - - strcpy(FBBHeader->From, ptr); - - ptr = strtok_s(NULL, seps, &Context); - - if (ptr == NULL) goto badparam; - - if (strlen(ptr) > 40 ) goto badparam; - - strcpy(FBBHeader->ATBBS, ptr); - - ptr = strtok_s(NULL, seps, &Context); - - if (ptr == NULL) goto badparam; - - if (strlen(ptr) > 6) - { - // Temp fix - reject instead of breaking connection - - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; - Logprintf(LOG_BBS, conn, '?', "Message Rejected as TO field too long"); - - user->Total.MsgsRejectedIn[Index]++; - return; - } - - strlop(ptr, '-'); // Remove any (illegal) ssid - - strcpy(FBBHeader->To, ptr); - - ptr = strtok_s(NULL, seps, &Context); - - if (ptr == NULL) goto badparam; - - if (strlen(ptr) > 12 ) goto badparam; - - strcpy(FBBHeader->BID, ptr); - - ptr = strtok_s(NULL, seps, &Context); - - if (ptr == NULL) goto badparam; - - FBBHeader->Size = atoi(ptr); - - goto ok; - -badparam: - - BBSputs(conn, "*** Protocol Error - Proposal format error\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - return; - -ok: - - // Check Filters - - if (CheckRejFilters(FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->BID, FBBHeader->MsgType, FBBHeader->Size)) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; - Logprintf(LOG_BBS, conn, '?', "Message Rejected by Filters"); - - user->Total.MsgsRejectedIn[Index]++; - } - - // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message - // If we do, reject it. If not, accept it again. (do we need some loop protection ???) - - else if (DoWeWantIt(conn, FBBHeader) == FALSE) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; - user->Total.MsgsRejectedIn[Index]++; - } - else if ((RestartPtr = LookupRestart(conn, FBBHeader)) > 0) - { - conn->FBBReplyIndex += sprintf(&conn->FBBReplyChars[conn->FBBReplyIndex], "!%d", RestartPtr); - } - else if (LookupTempBID(FBBHeader->BID)) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '='; - } - else - { - - // Save BID in temp list in case we are offered it again before completion - - BIDRec * TempBID = AllocateTempBIDRecord(); - strcpy(TempBID->BID, FBBHeader->BID); - TempBID->u.conn = conn; - - conn->FBBReplyChars[conn->FBBReplyIndex++] = '+'; - } - - FBBHeader->B2Message = FALSE; - - return; - - case 'C': // B2 Proposal - - if (conn->FBBMsgsSent) - FlagSentMessages(conn, user); // Mark previously sent messages - - if (conn->DoReverse == FALSE) // Dont accept messages - return; - - // Accumulate checksum - - for (i=0; i< len; i++) - { - conn->FBBChecksum+=Buffer[i]; - } - - // Parse Header - - // Find free line - - for (i = 0; i < 5; i++) - { - FBBHeader = &conn->FBBHeaders[i]; - - if (FBBHeader->Format == 0) - break; - } - - if (i == 5) - { - BBSputs(conn, "*** Protocol Error - Too Many Proposals\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - } - - - // FC EM A3EDD4P00P55 377 281 0 - - /* - - FC Proposal code. Requires B2 SID feature. - Type Message type ( 1 or 2 alphanumeric characters - - CM WinLink 2000 Control message - EM Encapsulated Message - ID Unique Message Identifier (max length 12 characters) - U-Size Uncompressed size of message - C-size Compressed size of message - - */ - FBBHeader->Format = Buffer[1]; - - ptr = strtok_s(&Buffer[3], seps, &Context); - if (ptr == NULL) goto badparam2; - if (strlen(ptr) != 2) goto badparam2; - FBBHeader->MsgType = 'P'; //ptr[0]; - - ptr = strtok_s(NULL, seps, &Context); - - if (ptr == NULL) goto badparam2; - - // Relay In RO mode adds @MPS@R to the MID. Don't know why (yet!) - - MPS = strlop(ptr, '@'); - if (MPS) - ROChar = strlop(MPS, '@'); - - if (strlen(ptr) > 12 ) goto badparam; - strcpy(FBBHeader->BID, ptr); - - ptr = strtok_s(NULL, seps, &Context); - if (ptr == NULL) goto badparam2; - FBBHeader->Size = atoi(ptr); - - ptr = strtok_s(NULL, seps, &Context); - if (ptr == NULL) goto badparam2; - FBBHeader->CSize = atoi(ptr); - FBBHeader->B2Message = TRUE; - - // If using BPQ Extensions (From To AT in proposal) Check Filters - - Buffer[len - 1] = 0; - - if (conn->BPQBBS) - { - char * From = strtok_s(NULL, seps, &Context); - char * ATBBS = strtok_s(NULL, seps, &Context); - char * To = strtok_s(NULL, seps, &Context); - char * Type = strtok_s(NULL, seps, &Context); - - if (From && To && ATBBS && Type && CheckRejFilters(From, To, ATBBS, NULL, *Type, FBBHeader->Size)) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; - user->Total.MsgsRejectedIn[Index]++; - Logprintf(LOG_BBS, conn, '?', "Message Rejected by Filters"); - - return; - } - } - goto ok2; - -badparam2: - - BBSputs(conn, "*** Protocol Error - Proposal format error\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - return; - -ok2: - if (LookupBID(FBBHeader->BID)) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; - Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); - user->Total.MsgsRejectedIn[Index]++; - - } - else if (FBBHeader->Size > MaxRXSize) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; - Logprintf(LOG_BBS, conn, '?', "Message Rejected by Size Limit"); - user->Total.MsgsRejectedIn[Index]++; - - } - else if ((RestartPtr = LookupRestart(conn, FBBHeader)) > 0) - { - conn->FBBReplyIndex += sprintf(&conn->FBBReplyChars[conn->FBBReplyIndex], "!%d", RestartPtr); - } - - else if (LookupTempBID(FBBHeader->BID)) - { - memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header - conn->FBBReplyChars[conn->FBBReplyIndex++] = '='; - } - else - { - // Save BID in temp list in case we are offered it again before completion - - BIDRec * TempBID = AllocateTempBIDRecord(); - strcpy(TempBID->BID, FBBHeader->BID); - TempBID->u.conn = conn; - - conn->FBBReplyChars[conn->FBBReplyIndex++] = 'Y'; - } - - return; - - case '>': - - // Optional Checksum - - if (conn->DoReverse == FALSE) // Dont accept messages - { - Logprintf(LOG_BBS, conn, '?', "Reverse Forwarding not allowed"); - Disconnect(conn->BPQStream); - return; - } - - if (len > 3) - { - int sum; - - sscanf(&Buffer[3], "%x", &sum); - - conn->FBBChecksum+=sum; - - if (conn->FBBChecksum) - { - BBSputs(conn, "*** Proposal Checksum Error\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - return; - } - } - - // Return "FS ", followed by +-= for each proposal - - conn->FBBReplyChars[conn->FBBReplyIndex] = 0; - conn->FBBReplyIndex = 0; - - nodeprintfEx(conn, "FS %s\r", conn->FBBReplyChars); - - // if all rejected, send proposals or prompt, else set up for first message - - FBBHeader = &conn->FBBHeaders[0]; - - if (FBBHeader->MsgType == 0) - { - conn->FBBIndex = 0; // ready for first block; - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - conn->FBBChecksum = 0; - - if (!FBBDoForward(conn)) // Send proposal if anthing to forward - { - conn->InputMode = 0; - - if (conn->DoReverse) - FBBputs(conn, "FF\r"); - else - { - FBBputs(conn, "FQ\r"); - conn->CloseAfterFlush = 20; // 2 Secs - } - } - } - else - { - if (conn->BBSFlags & FBBForwarding) - { - conn->InputMode = 'B'; - } - - CreateMessage(conn, FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->MsgType, FBBHeader->BID, NULL); - } - - return; - - } - - return; -} - -VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user) -{ - struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block - int i; - - conn->FBBMsgsSent = FALSE; - - for (i=0; i < 5; i++) - { - FBBHeader = &conn->FBBHeaders[i]; - - if (FBBHeader && FBBHeader->MsgType) // Not a zapped entry - { - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Message %d Held\r\n", FBBHeader->FwdMsg->number); - sprintf(Title, "Message %d Held - Rejected by Winlink", FBBHeader->FwdMsg->number); - SendMessageToSYSOP(Title, MailBuffer, Length); - - FBBHeader->FwdMsg->status = 'H'; // Mark as Held - } - } - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - SaveMessageDatabase(); -} - - - -VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user) -{ - struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block - int i; - - // Called if FBB command received after sending a block of messages . Flag as as sent. - - conn->FBBMsgsSent = FALSE; - - for (i=0; i < 5; i++) - { - FBBHeader = &conn->FBBHeaders[i]; - - if (FBBHeader && FBBHeader->MsgType) // Not a zapped entry - { - if ((conn->Paclink || conn->RMSExpress || conn->PAT) && -// ((conn->UserPointer->flags & F_NTSMPS) == 0) && - (FBBHeader->FwdMsg->type == 'P')) - { - // Kill Messages sent to paclink/RMS Express unless BBS FWD bit set - - if (check_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber) == 0) - { - FlagAsKilled(FBBHeader->FwdMsg, FALSE); - continue; - } - } - - clear_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(FBBHeader->FwdMsg->forw, user->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(FBBHeader->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - FBBHeader->FwdMsg->status = 'F'; // Mark as forwarded - FBBHeader->FwdMsg->datechanged=time(NULL); - } - -#ifndef NOMQTT - if (MQTT) - MQTTMessageEvent(FBBHeader->FwdMsg); -#endif - - FBBHeader->FwdMsg->Locked = 0; // Unlock - conn->UserPointer->ForwardingInfo->MsgCount--; - } - } - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - SaveMessageDatabase(); -} - - -VOID SetupNextFBBMessage(CIRCUIT * conn) -{ - struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block - - memmove(&conn->FBBHeaders[0], &conn->FBBHeaders[1], 4 * sizeof(struct FBBHeaderLine)); - - memset(&conn->FBBHeaders[4], 0, sizeof(struct FBBHeaderLine)); - - FBBHeader = &conn->FBBHeaders[0]; - - if (FBBHeader->MsgType == 0) - { - conn->FBBIndex = 0; // ready for next block; - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - - conn->FBBChecksum = 0; - conn->InputMode = 0; - - if (!FBBDoForward(conn)) // Send proposal if anthing to forward - { - conn->InputMode = 0; - FBBputs(conn, "FF\r"); - } - } - else - { - if (conn->BBSFlags & FBBForwarding) - conn->InputMode = 'B'; - - CreateMessage(conn, FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->MsgType, FBBHeader->BID, NULL); - } -} - -BOOL FBBDoForward(CIRCUIT * conn) -{ - int i; - char proposal[100]; - int proplen; - - if (FindMessagestoForward(conn)) - { - // Send Proposal Block - - struct FBBHeaderLine * FBBHeader; - - for (i=0; i < conn->FBBIndex; i++) - { - FBBHeader = &conn->FBBHeaders[i]; - - if (conn->BBSFlags & FBBB2Mode) - - if (conn->BPQBBS) - - // Add From and To Header for Filters - - proplen = sprintf(proposal, "FC EM %s %d %d %s %s %s %c\r", - FBBHeader->BID, - FBBHeader->Size, - FBBHeader->CSize, - FBBHeader->From, - (FBBHeader->ATBBS[0]) ? FBBHeader->ATBBS : conn->UserPointer->Call, - FBBHeader->To, - FBBHeader->MsgType); - - else - - // FC EM A3EDD4P00P55 377 281 0 - - proplen = sprintf(proposal, "FC EM %s %d %d %d\r", - FBBHeader->BID, - FBBHeader->Size, - FBBHeader->CSize, 0); - - else - proplen = sprintf(proposal, "%s %c %s %s %s %s %d\r", - (conn->BBSFlags & FBBCompressed) ? "FA" : "FB", - FBBHeader->MsgType, - FBBHeader->From, - (FBBHeader->ATBBS[0]) ? FBBHeader->ATBBS : conn->UserPointer->Call, - FBBHeader->To, - FBBHeader->BID, - FBBHeader->Size); - - // Accumulate checksum - - while(proplen > 0) - { - conn->FBBChecksum+=proposal[--proplen]; - } - - FBBputs(conn, proposal); - } - - conn->FBBChecksum = - conn->FBBChecksum; - - nodeprintfEx(conn, "F> %02X\r", conn->FBBChecksum); - - return TRUE; - } - - return FALSE; -} - -VOID UnpackFBBBinary(CIRCUIT * conn) -{ - int MsgLen, i, offset, n; - UCHAR * ptr; - -loop: - - if (conn->CloseAfterFlush) // Failed (or complete), so discard rest of input - { - conn->InputLen = 0; - return; - } - - - ptr = conn->InputBuffer; - - if (conn->InputLen < 2) - return; // All formats need at least two bytes - - switch (*ptr) - { - case 1: // Header - - MsgLen = ptr[1] + 2; - - if (conn->InputLen < MsgLen) - return; // Wait for more - - if (strlen(&ptr[2]) > 60) - { - memcpy(conn->TempMsg->title, &ptr[2], 60); - conn->TempMsg->title[60] = 0; - Debugprintf("FBB Subject too long - truncated, %s", &ptr[2]); - } - else - strcpy(conn->TempMsg->title, &ptr[2]); - - offset = atoi(ptr+3+strlen(&ptr[2])); - - ptr += MsgLen; - - memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); - - conn->InputLen -= MsgLen; - - conn->FBBChecksum = 0; - - if (offset) - { - struct FBBRestartData * RestartRec; - - // Trying to restart - make sure we have restart data - - for (i = 1; i <= RestartCount; i++) - { - RestartRec = RestartData[i]; - - if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) - && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0)) - { - if (RestartRec->length <= offset) - { - conn->TempMsg->length = RestartRec->length; - conn->MailBuffer = RestartRec->MailBuffer; - conn->MailBufferSize = RestartRec->MailBufferSize; - - // FBB Seems to insert 6 Byte message - // It looks like the original csum and length - perhaps a a consistancy check - - // But Airmail Sends the Restart Data in the next packet, move the check code. - - conn->NeedRestartHeader = TRUE; - - goto GotRestart; - } - else - { - BBSputs(conn, "*** Trying to restart from invalid position.\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - - // Remove Restart info - - for (n = i; n < RestartCount; n++) - { - RestartData[n] = RestartData[n+1]; // move down all following entries - } - RestartCount--; - SaveRestartData(); - } - } - - // No Restart Data - - BBSputs(conn, "*** Trying to restart, but no restart data.\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - - // Create initial buffer of 10K. Expand if needed later - - if (conn->MailBufferSize == 0) - { - // Dont allocate if restarting - - conn->MailBuffer=malloc(10000); - conn->MailBufferSize=10000; - } - - GotRestart: - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to create Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - - goto loop; - - - - case 2: // Data Block - - if (ptr[1] == 0) - MsgLen = 256; - else - MsgLen = ptr[1]; - - if (conn->InputLen < (MsgLen + 2)) - return; // Wait for more - - // If waiting for Restart Header, see if it has arrived - - if (conn->NeedRestartHeader) - { - conn->NeedRestartHeader = FALSE; - - if (MsgLen == 6) - { - ptr = conn->InputBuffer+2; - conn->InputLen -=8; - - for (i=0; i<6; i++) - { - conn->FBBChecksum+=ptr[0]; - ptr++; - } - memmove(conn->InputBuffer, ptr, conn->InputLen); - } - else - { - BBSputs(conn, "*** Restart Header Missing.\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - } - - goto loop; - - } - // Process it - - ptr+=2; - - for (i=0; i< MsgLen; i++) - { - conn->FBBChecksum+=ptr[i]; - } - - ptr-=2; - - if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], &ptr[2], MsgLen); - - conn->TempMsg->length += MsgLen; - - MsgLen +=2; - - ptr += MsgLen; - - memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); - - conn->InputLen -= MsgLen; - - goto loop; - - - case 4: // EOM - - // Process EOM - - conn->FBBChecksum+=ptr[1]; - - if (conn->FBBChecksum == 0) - { -#ifndef LINBPQ - __try - { -#endif - conn->InputMode = 0; // So we won't save Restart data if decode fails - DeleteRestartData(conn); - Decode(conn, 0); // Setup Next Message will reset InputMode if needed -#ifndef LINBPQ - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - BBSputs(conn, "*** Program Error Decoding Message\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - return; - } -#endif - } - - else - { - BBSputs(conn, "*** Message Checksum Error\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - - // Don't allow restart, as saved data is probably duff - - conn->DontSaveRestartData = TRUE; - return; - } - ptr += 2; - - memmove(conn->InputBuffer, ptr, conn->InputLen-2); - - conn->InputLen -= 2; - - goto loop; - - default: - - BBSputs(conn, "*** Protocol Error - Invalid Binary Message Format (Invalid Block Type)\r"); - Flush(conn); - - if (conn->CloseAfterFlush == 0) - { - // Dont do it more than once - - conn->CloseAfterFlush = 20; // 2 Secs - - // Don't allow restart, as saved data is probably duff - - // Actually all but the last block is probably OK, but maybe - // not worth the risk of restarting - - // Actually I think it is - - if (conn->TempMsg->length > 256) - { - conn->TempMsg->length -= 256; - conn->DontSaveRestartData = FALSE; - } - else - conn->DontSaveRestartData = TRUE; - } - return; - } -} - -VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg) -{ - struct tm * tm; - char * MsgBytes, * Save; - UCHAR * Compressed, * Compressedptr; - UCHAR * UnCompressed; - char * Title; - UCHAR * Output, * Outputptr; - int i, OrigLen, MsgLen, CompLen, DataOffset; - char Rline[80]; - int RLineLen; - int Index; - time_t temp; - - if (FwdMsg->type == 'P') - Index = PMSG; - else if (FwdMsg->type == 'B') - Index = BMSG; - else if (FwdMsg->type == 'T') - Index = TMSG; - - MsgBytes = Save = ReadMessageFile(FwdMsg->number); - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r\n"); - FwdMsg->length = (int)strlen(MsgBytes); - } - - OrigLen = FwdMsg->length; - - Title = FwdMsg->title; - - Compressed = Compressedptr = zalloc(2 * OrigLen + 200); - Output = Outputptr = zalloc(2 * OrigLen + 200); - - *Outputptr++ = 1; - *Outputptr++ = (int)strlen(Title) + 8; - strcpy(Outputptr, Title); - Outputptr += strlen(Title) +1; - sprintf(Outputptr, "%6d", conn->RestartFrom); - Outputptr += 7; - - DataOffset = (int)(Outputptr - Output); // Used if restarting - - memcpy(&temp, &FwdMsg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - sprintf(Rline, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - FwdMsg->number, BBSName, HRoute, RlineVer); - - if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message - strcat(Rline, "\r\n"); - - RLineLen = (int)strlen(Rline); - - MsgLen = OrigLen + RLineLen; - - UnCompressed = zalloc(MsgLen+10); - - strcpy(UnCompressed, Rline); - - // If a B2 Message, Remove B2 Header - - if (FwdMsg->B2Flags & B2Msg) - { - char * ptr; - int BodyLen = OrigLen; - - // Remove all B2 Headers, and all but the first part. - - ptr = strstr(MsgBytes, "Body:"); - - if (ptr) - { - BodyLen = atoi(&ptr[5]); - ptr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (ptr) - ptr +=4; - else - ptr = MsgBytes; - - } - else - ptr = MsgBytes; - - if (memcmp(ptr, "R:", 2) == 0) // Already have RLines, so remove blank line after new R:line - RLineLen -= 2; - - memcpy(&UnCompressed[RLineLen], ptr, BodyLen); - - MsgLen = BodyLen + RLineLen; - } - else // Not B2 Message - { - memcpy(&UnCompressed[RLineLen], MsgBytes, OrigLen); - } - - CompLen = Encode(UnCompressed, Compressed, MsgLen, conn->BBSFlags & FBBB1Mode, conn->BBSFlags & FBBCompressed); - - conn->FBBChecksum = 0; - - // If restarting, send the checksum and length as a single record, then data from the restart point - // The count includes the header, so adjust count and pointers - - if (conn->RestartFrom) - { - *Outputptr++ = 2; - *Outputptr++ = 6; - - for (i=0; i< 6; i++) - { - conn->FBBChecksum+=Compressed[i]; - *Outputptr++ = Compressed[i]; - } - - for (i=conn->RestartFrom; i< CompLen; i++) - { - conn->FBBChecksum+=Compressed[i]; - } - - Compressedptr += conn->RestartFrom; - CompLen -= conn->RestartFrom; - } - else - { - for (i=0; i< CompLen; i++) - { - conn->FBBChecksum+=Compressed[i]; - } - } - - while (CompLen > 250) - { - *Outputptr++ = 2; - *Outputptr++ = 250; - - memcpy(Outputptr, Compressedptr, 250); - Outputptr += 250; - Compressedptr += 250; - CompLen -= 250; - } - - *Outputptr++ = 2; - *Outputptr++ = CompLen; - - memcpy(Outputptr, Compressedptr, CompLen); - - Outputptr += CompLen; - - *Outputptr++ = 4; - conn->FBBChecksum = - conn->FBBChecksum; - *Outputptr++ = conn->FBBChecksum; - - if (conn->OpenBCM) // Telnet, so escape any 0xFF - { - unsigned char * ptr1 = Output; - unsigned char * ptr2 = Compressed; // Reuse Compressed buffer - size_t Len = Outputptr - Output; - unsigned char c; - - while (Len--) - { - c = *(ptr1++); - *(ptr2++) = c; - if (c == 0xff) // FF becodes FFFF - *(ptr2++) = c; - } - - QueueMsg(conn, Compressed, (int)(ptr2 - Compressed)); - } - else - QueueMsg(conn, Output, (int)(Outputptr - Output)); - - free(Save); - free(Compressed); - free(UnCompressed); - free(Output); - -} - -BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline) -{ - char * MsgBytes; - UCHAR * Compressed; - UCHAR * UnCompressed; - int OrigLen, MsgLen, B2HddrLen, CompLen; - char Date[20]; - struct tm * tm; - char B2From[80]; - char B2To[80]; - struct MsgInfo * Msg = FBBHeader->FwdMsg; - struct UserInfo * FromUser; - int BodyLineToBody; - int RlineLen = (int)strlen(Rline) ; - char * TypeString; -#ifndef LINBPQ - struct _EXCEPTION_POINTERS exinfo; - - __try { -#endif - - if (Msg == NULL) - Debugprintf("Msg = NULL"); - - - MsgBytes = ReadMessageFile(Msg->number); - - if (MsgBytes == 0) - { - Debugprintf("B2 Message - Message File not found"); - return FALSE; - } - - UnCompressed = zalloc(Msg->length + 2000); - - if (UnCompressed == NULL) - Debugprintf("B2 Message - zalloc for %d failed", Msg->length + 2000); - - OrigLen = Msg->length; - - // If a B2 Message add R:line at start of Body, but otherwise leave intact. - // Unless a message to Paclink, when we must remove any HA from the TO address - // Or to a CMS, when we remove HA from From or Reply-to - - if (Msg->B2Flags & B2Msg) - { - char * ptr, *ptr2; - int BodyLen; - int BodyLineLen; - int Index; - - MsgLen = OrigLen + RlineLen; - - if (conn->Paclink) - { - // Remove any HA on the TO address - - ptr = strstr(MsgBytes, "To:"); - if (ptr) - { - ptr2 = strstr(ptr, "\r\n"); - if (ptr2) - { - while (ptr < ptr2) - { - if (*ptr == '.' || *ptr == '@') - { - memset(ptr, ' ', ptr2 - ptr); - break; - } - ptr++; - } - } - } - } - - if (conn->WL2K) - { - // Remove any HA on the From or Reply-To address - - ptr = strstr(MsgBytes, "From:"); - if (ptr == NULL) - ptr = strstr(MsgBytes, "Reply-To:"); - - if (ptr) - { - ptr2 = strstr(ptr, "\r\n"); - if (ptr2) - { - while (ptr < ptr2) - { - if (*ptr == '.' || *ptr == '@') - { - memset(ptr, ' ', ptr2 - ptr); - break; - } - ptr++; - } - } - } - } - - - // Add R: Line at start of body. Will Need to Update Body Length - - ptr = strstr(MsgBytes, "Body:"); - - if (ptr == 0) - { - Debugprintf("B2 Messages without Body: Line"); - return FALSE; - } - ptr2 = strstr(ptr, "\r\n"); - - Index = (int)(ptr - MsgBytes); // Bytes Before Body: line - - if (Index <= 0 || Index > MsgLen) - { - Debugprintf("B2 Message Body: line position invalid - %d", Index); - return FALSE; - } - - // If message to saildocs adding an R: line will mess up the message processing, so add as an X header - - if (strstr(MsgBytes, "To: query@saildocs.com")) - { - int x_Len; - - memcpy(UnCompressed, MsgBytes, Index); // Up to Old Body; - x_Len = sprintf(&UnCompressed[Index], "x-R: %s", &Rline[2]); - MsgLen = OrigLen + x_Len; - Index +=x_Len; - goto copyRest; - } - - BodyLen = atoi(&ptr[5]); - - if (BodyLen < 0 || BodyLen > MsgLen) - { - Debugprintf("B2 Message Length from Body: line invalid - Msg len %d From Body %d", MsgLen, BodyLen); - return FALSE; - } - - BodyLineLen = (int)(ptr2 - ptr) + 2; - MsgLen -= BodyLineLen; // Length of Body Line may change - - ptr = strstr(ptr2, "\r\n\r\n"); // Blank line before Body - - if (ptr == 0) - { - Debugprintf("B2 Message - No Blank Line before Body"); - return FALSE; - } - - ptr += 4; - - ptr2 += 2; // Line Following Original Body: Line - - BodyLineToBody = (int)(ptr - ptr2); - - if (memcmp(ptr, "R:", 2) != 0) // No R line, so must be our message - { - strcat(Rline, "\r\n"); - RlineLen += 2; - MsgLen += 2; - } - BodyLen += RlineLen; - - memcpy(UnCompressed, MsgBytes, Index); // Up to Old Body; - BodyLineLen = sprintf(&UnCompressed[Index], "Body: %d\r\n", BodyLen); - - MsgLen += BodyLineLen; // Length of Body Line may have changed - Index += BodyLineLen; - - if (BodyLineToBody < 0 || BodyLineToBody > 1000) - { - Debugprintf("B2 Message - Body too far from Body Line - %d", BodyLineToBody); - return FALSE; - } - memcpy(&UnCompressed[Index], ptr2, BodyLineToBody); // Stuff Between Body: Line and Body - - Index += BodyLineToBody; - - memcpy(&UnCompressed[Index], Rline, RlineLen); - Index += RlineLen; - -copyRest: - - memcpy(&UnCompressed[Index], ptr, MsgLen - Index); // Rest of Message - - FBBHeader->Size = MsgLen; - - Compressed = zalloc(2 * MsgLen + 200); -#ifndef LINBPQ - __try { -#endif - CompLen = Encode(UnCompressed, Compressed, MsgLen, TRUE, conn->BBSFlags & FBBCompressed); - - FBBHeader->CompressedMsg = Compressed; - FBBHeader->CSize = CompLen; - - free(UnCompressed); - return TRUE; -#ifndef LINBPQ - } My__except_Routine("Encode B2Message"); -#endif - return FALSE; - } - - - if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message - { - strcat(Rline, "\r\n"); - RlineLen += 2; - } - - MsgLen = OrigLen + RlineLen; - -// if (conn->RestartFrom == 0) -// { -// // save time first sent, or checksum will be wrong when we restart -// -// FwdMsg->datechanged=time(NULL); -// } - - tm = gmtime((time_t *)&Msg->datechanged); - - sprintf(Date, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - // We create the B2 Header -/* - MID: XR88I1J160EB - Date: 2009/07/25 18:17 - Type: Private - From: SMTP:john.wiseman@ntlworld.com - To: G8BPQ - Subject: RE: RMS Test Message - Mbo: SMTP - Body: 213 - -*/ - if (strcmp(Msg->to, "RMS") == 0) // Address is in via - strcpy(B2To, Msg->via); - else - if (Msg->via[0] && (!conn->Paclink)) - sprintf(B2To, "%s@%s", Msg->to, Msg->via); - else - strcpy(B2To, Msg->to); - - // Try to create a full from: addrsss so RMS Express can reply - - strcpy(B2From, Msg->from); - - Logprintf(LOG_BBS, conn, '?', "B2 From %s", B2From); - - if (strcmp(conn->Callsign, "RMS") != 0 && conn->WL2K == 0) // if going to RMS - just send calll - { - if (_stricmp(Msg->from, "SMTP:") == 0) // Address is in via - strcpy(B2From, Msg->emailfrom); - else - { - FromUser = LookupCall(Msg->from); - - if (FromUser) - { - Logprintf(LOG_BBS, conn, '?', "B2 From - Local User"); - - if (FromUser->HomeBBS[0]) - sprintf(B2From, "%s@%s", Msg->from, FromUser->HomeBBS); - else - sprintf(B2From, "%s@%s", Msg->from, BBSName); - } - else - { - WPRecP WP = LookupWP(Msg->from); - - Logprintf(LOG_BBS, conn, '?', "B2 From - not local User"); - - if (WP) - sprintf(B2From, "%s@%s", Msg->from, WP->first_homebbs); - } - } - } - - Logprintf(LOG_BBS, conn, '?', "B2 From Finally %s", B2From); - - if (Msg->type == 'P') - TypeString = "Private" ; - else if (Msg->type == 'B') - TypeString = "Bulletin"; - else if (Msg->type == 'T') - TypeString = "Traffic"; - - B2HddrLen = sprintf(UnCompressed, - "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n" - "Content-Type: text/plain\r\nContent-Transfer-Encoding: 8bit\r\nBody: %d\r\n\r\n", - Msg->bid, Date, TypeString, B2From, B2To, Msg->title, BBSName, MsgLen); - - - memcpy(&UnCompressed[B2HddrLen], Rline, RlineLen); - memcpy(&UnCompressed[B2HddrLen + RlineLen], MsgBytes, OrigLen); // Rest of Message - - MsgLen += B2HddrLen; - - FBBHeader->Size = MsgLen; - - Compressed = zalloc(2 * MsgLen + 200); - - CompLen = Encode(UnCompressed, Compressed, MsgLen, TRUE, conn->BBSFlags & FBBCompressed); - - FBBHeader->CompressedMsg = Compressed; - FBBHeader->CSize = CompLen; - - free(UnCompressed); - - return TRUE; -#ifndef LINBPQ - } My__except_Routine("CreateB2Message"); -#endif - return FALSE; - -} - -VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader) -{ - UCHAR * Compressed, * Compressedptr; - UCHAR * Output, * Outputptr; - int i, CompLen; - int Index; - - if (FBBHeader->FwdMsg->type == 'P') - Index = PMSG; - else if (FBBHeader->FwdMsg->type == 'B') - Index = BMSG; - else if (FBBHeader->FwdMsg->type == 'T') - Index = TMSG; - - Compressed = Compressedptr = FBBHeader->CompressedMsg; - - Output = Outputptr = zalloc(FBBHeader->CSize + 10000); - - *Outputptr++ = 1; - *Outputptr++ = (int)strlen(FBBHeader->FwdMsg->title) + 8; - strcpy(Outputptr, FBBHeader->FwdMsg->title); - Outputptr += strlen(FBBHeader->FwdMsg->title) +1; - sprintf(Outputptr, "%06d", conn->RestartFrom); - Outputptr += 7; - - CompLen = FBBHeader->CSize; - - conn->FBBChecksum = 0; - - // If restarting, send the checksum and length as a single record, then data from the restart point - // The count includes the header, so adjust count and pointers - - if (conn->RestartFrom) - { - *Outputptr++ = 2; - *Outputptr++ = 6; - - for (i=0; i< 6; i++) - { - conn->FBBChecksum+=Compressed[i]; - *Outputptr++ = Compressed[i]; - } - - for (i=conn->RestartFrom; i< CompLen; i++) - { - conn->FBBChecksum+=Compressed[i]; - } - - Compressedptr += conn->RestartFrom; - CompLen -= conn->RestartFrom; - } - else - { - for (i=0; i< CompLen; i++) - { - conn->FBBChecksum+=Compressed[i]; - } - conn->UserPointer->Total.MsgsSent[Index]++; - conn->UserPointer->Total.BytesForwardedOut[Index] += FBBHeader->FwdMsg->length; - - } - - while (CompLen > 256) - { - *Outputptr++ = 2; - *Outputptr++ = 0; - - memcpy(Outputptr, Compressedptr, 256); - Outputptr += 256; - Compressedptr += 256; - CompLen -= 256; - } - - *Outputptr++ = 2; - *Outputptr++ = CompLen; - - memcpy(Outputptr, Compressedptr, CompLen); - - Outputptr += CompLen; - - *Outputptr++ = 4; - conn->FBBChecksum = - conn->FBBChecksum; - *Outputptr++ = conn->FBBChecksum; - - if (conn->OpenBCM) // Telnet, so escape any 0xFF - { - unsigned char * ptr1 = Output; - unsigned char * ptr2 = Compressed; // Reuse Compressed buffer - int Len = (int)(Outputptr - Output); - unsigned char c; - - while (Len--) - { - c = *(ptr1++); - *(ptr2++) = c; - if (c == 0xff) // FF becodes FFFF - *(ptr2++) = c; - } - - QueueMsg(conn, Compressed, (int)(ptr2 - Compressed)); - } - else - QueueMsg(conn, Output, (int)(Outputptr - Output)); - - free(Compressed); - free(Output); -} - -// Restart Routines. - -VOID SaveFBBBinary(CIRCUIT * conn) -{ - // Disconnected during binary transfer - - char Msg[120]; - int i, len; - struct FBBRestartData * RestartRec = NULL; - - if (conn->TempMsg == NULL) - return; - - if (conn->TempMsg->length < 256) - return; // Not worth it. - - // If we already have a restart record, reuse it - - for (i = 1; i <= RestartCount; i++) - { - RestartRec = RestartData[i]; - - if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) - && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0)) - { - // Found it, so reuse - - // If we have more data, reset retry count - - if (RestartRec->length < conn->TempMsg->length) - RestartRec->Count = 0;; - - break; - } - } - - if (RestartRec == NULL) - { - RestartRec = zalloc(sizeof (struct FBBRestartData)); - - GetSemaphore(&AllocSemaphore, 0); - - RestartData=realloc(RestartData,(++RestartCount+1) * sizeof(void *)); - RestartData[RestartCount] = RestartRec; - - FreeSemaphore(&AllocSemaphore); - RestartRec->TimeCreated = time(NULL); - } - - strcpy(RestartRec->Call, conn->UserPointer->Call); - RestartRec->length = conn->TempMsg->length; - strcpy(RestartRec->bid, conn->TempMsg->bid); - RestartRec->MailBuffer = conn->MailBuffer; - RestartRec->MailBufferSize = conn->MailBufferSize; - - len = sprintf_s(Msg, sizeof(Msg), "Disconnect received from %s during Binary Transfer - %d Bytes Saved for restart", - conn->Callsign, conn->TempMsg->length); - - SaveRestartData(); - - WriteLogLine(conn, '|',Msg, len, LOG_BBS); -} - -void DeleteRestartData(CIRCUIT * conn) -{ - struct FBBRestartData * RestartRec = NULL; - int i, n; - - if (conn->TempMsg == NULL) - return; - - for (i = 1; i <= RestartCount; i++) - { - RestartRec = RestartData[i]; - - if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) - && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0)) - { - // Remove restrt data - - for (n = i; n < RestartCount; n++) - { - RestartData[n] = RestartData[n+1]; // move down all following entries - } - - RestartCount--; - SaveRestartData(); - return; - } - } -} - - -BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader) -{ - int i, n; - - struct FBBRestartData * RestartRec; - - if ((conn->BBSFlags & FBBB1Mode) == 0) - return FALSE; // Only B1 & B2 support restart - - for (i = 1; i <= RestartCount; i++) - { - RestartRec = RestartData[i]; - - if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) - && (strcmp(RestartRec->bid, FBBHeader->BID) == 0)) - { - char Msg[120]; - int len; - - RestartRec->Count++; - - if (RestartRec->Count > 10) - { - len = sprintf_s(Msg, sizeof(Msg), "Too many restarts for %s - Requesting restart from beginning", - FBBHeader->BID); - - WriteLogLine(conn, '|',Msg, len, LOG_BBS); - - // Remove restrt data - - for (n = i; n < RestartCount; n++) - { - RestartData[n] = RestartData[n+1]; // move down all following entries - } - - RestartCount--; - SaveRestartData(); - return FALSE; - } - - len = sprintf_s(Msg, sizeof(Msg), "Restart Data found for %s - Requesting restart from %d", - FBBHeader->BID, RestartRec->length); - - WriteLogLine(conn, '|',Msg, len, LOG_BBS); - - return (RestartRec->length); - } - } - - return FALSE; // Not Found -} - - - -BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader) -{ - struct MsgInfo * Msg; - BIDRec * BID; - int m; - - if (RefuseBulls && FBBHeader->MsgType == 'B') - { - Logprintf(LOG_BBS, conn, '?', "Message Rejected by RefuseBulls"); - return FALSE; - } - if (FBBHeader->Size > MaxRXSize) - { - Logprintf(LOG_BBS, conn, '?', "Message Rejected by Size Check"); - return FALSE; - } - - BID = LookupBID(FBBHeader->BID); - - if (BID) - { - if (FBBHeader->MsgType == 'B') - { - Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); - return FALSE; - } - - // Treat P messages to SYSOP@WW as Bulls - - if (strcmp(FBBHeader->To, "SYSOP") == 0 && strcmp(FBBHeader->ATBBS, "WW") == 0) - { - Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); - return FALSE; - } - - m = NumberofMessages; - - while (m > 0) - { - Msg = MsgHddrPtr[m]; - - if (Msg->number == BID->u.msgno) - { - // if the same TO we will assume the same message - - if (strcmp(Msg->to, FBBHeader->To) == 0) - { - // We have this message. If we have already forwarded it, we should accept it again - - if ((Msg->status == 'N') || (Msg->status == 'Y')|| (Msg->status == 'H')) - { - Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); - return FALSE; // Dont want it - } - else - return TRUE; // Get it again - } - - // Same number. but different message (why?) Accept for now - - return TRUE; - } - - m--; - } - - return TRUE; // A personal Message we have had before, but don't still have. - } - else - { - // We don't know the BID - - return TRUE; // We want it - } -} - - - - +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// FBB Forwarding Routines + +#include "bpqmail.h" + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + + +void DeleteRestartData(CIRCUIT * conn); + +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); +void MQTTMessageEvent(void* message); + +int MaxRXSize = 99999; +int MaxTXSize = 99999; + +struct FBBRestartData ** RestartData = NULL; +int RestartCount = 0; + +struct B2RestartData ** B2RestartRecs = NULL; +int B2RestartCount = 0; + +extern char ProperBaseDir[]; + +char RestartDir[MAX_PATH] = ""; + +void GetRestartData() +{ + int i; + struct FBBRestartData Restart; + struct FBBRestartData * RestartRec; + char MsgFile[MAX_PATH]; + FILE * hFile; + int FileSize; + struct stat STAT; + size_t ReadLen = 0; + time_t Age; + + strcpy(RestartDir, MailDir); + strcat(RestartDir, "/Restart"); + + // Make sure RestartDir exists + +#ifdef WIN32 + CreateDirectory(RestartDir, NULL); // Just in case +#else + mkdir(RestartDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(RestartDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + // look for restart files. These will be numbered from 1 up + + for (i = 1; 1; i++) + { + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, i); + + if (stat(MsgFile, &STAT) == -1) + break; + + FileSize = STAT.st_size; + + Age = time(NULL) - STAT.st_ctime; + + if (Age > 86400 * 2) // Max 2 days + continue; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + break; + + // Read Restart Record + + fread(&Restart, 1, sizeof(struct FBBRestartData), hFile); + + if ((Restart.MailBufferSize + sizeof(struct FBBRestartData)) != FileSize) // Duff file + { + fclose(hFile); + break; + } + + RestartRec = zalloc(sizeof (struct FBBRestartData)); + + GetSemaphore(&AllocSemaphore, 0); + + RestartData = realloc(RestartData,(++RestartCount+1) * sizeof(void *)); + RestartData[RestartCount] = RestartRec; + + FreeSemaphore(&AllocSemaphore); + + memcpy(RestartRec, &Restart, sizeof(struct FBBRestartData)); + RestartRec->MailBuffer = malloc(RestartRec->MailBufferSize); + ReadLen = fread(RestartRec->MailBuffer, 1, RestartRec->MailBufferSize, hFile); + + Logprintf(LOG_BBS, 0, '?', "Restart Data for %s %s Len %d Loaded", RestartRec->Call, RestartRec->bid, RestartRec->length); + fclose(hFile); + } +} + + +void SaveRestartData() +{ + // Save restart data to file so we can reload on restart + // Restart data has pointers to buffers so we must save copy of data and reconstitue on restart + + // Delete and resave all restart data to keep restart directory clean + + int i, n = 1; + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + struct FBBRestartData * RestartRec = NULL; + struct stat STAT; + time_t NOW = time(NULL); + + + for (i = 1; 1; i++) + { + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, i); + + if (stat(MsgFile, &STAT) == -1) + break; + + DeleteFile(MsgFile); + } + + for (i = 1; i <= RestartCount; i++) + { + RestartRec = RestartData[i]; + + if (RestartRec == 0) + return; // Shouldn't happen! + + if ((NOW - RestartRec->TimeCreated) > 86400 * 2) // Max 2 days + continue; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, n++); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(RestartRec, 1, sizeof(struct FBBRestartData), hFile); // Save Header + WriteLen = fwrite(RestartRec->MailBuffer, 1, RestartRec->MailBufferSize, hFile); // Save Data + fclose(hFile); + } + } +} +VOID FBBputs(CIRCUIT * conn, char * buf) +{ + // Sends to user and logs + + int len = (int)strlen(buf); + + WriteLogLine(conn, '>', buf, len -1, LOG_BBS); + + QueueMsg(conn, buf, len); + + if (conn->BBSFlags & NEEDLF) + QueueMsg(conn, "\n", 1); +} + + +VOID ProcessFBBLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len) +{ + struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block + int i; + int Index = 0; // Message Type Index for Stats + char * ptr; + char * Context; + char seps[] = " \r"; + int RestartPtr; + char * Respptr; + BOOL AllRejected = TRUE; + char * MPS; + char * ROChar; + + if (conn->Flags & GETTINGMESSAGE) + { + ProcessMsgLine(conn, user, Buffer, len); + if (conn->Flags & GETTINGMESSAGE) + + // Still going + return; + + SetupNextFBBMessage(conn); + return; + } + + if (conn->Flags & GETTINGTITLE) + { + ProcessMsgTitle(conn, user, Buffer, len); + return; + } + + // Should be FA FB F> FS FF FQ + + if (Buffer[0] == ';') // winlink comment or BPQ Type Select + { + if (memcmp(Buffer, "; MSGTYPES", 7) == 0) + { + char * ptr; + + conn->SendB = conn->SendP = conn->SendT = FALSE; + + ptr = strchr(&Buffer[10], 'B'); + + if (ptr) + { + conn->SendB = TRUE; + conn->MaxBLen = atoi(++ptr); + if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; + } + + ptr = strchr(&Buffer[10], 'T'); + + if (ptr) + { + conn->SendT = TRUE; + conn->MaxTLen = atoi(++ptr); + if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; + } + ptr = strchr(&Buffer[10], 'P'); + + if (ptr) + { + conn->SendP = TRUE; + conn->MaxPLen = atoi(++ptr); + if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; + } + return; + } + + // Other ; Line - Ignore + + return; + } + + if (Buffer[0] != 'F') + { + if (strstr(Buffer, "*** Profanity detected") || strstr(Buffer, "*** Unknown message sender")) + { + // Winlink Check - hold message + + if (conn->FBBMsgsSent) + HoldSentMessages(conn, user); + } + + if (conn->BBSFlags & DISCONNECTING) + return; // Ignore if disconnect aleady started + + BBSputs(conn, "*** Protocol Error - Line should start with 'F'\r"); + Flush(conn); + Sleep(500); + conn->BBSFlags |= DISCONNECTING; + Disconnect(conn->BPQStream); + + return; + } + + switch (Buffer[1]) + { + case 'F': + + // Request Reverse + + if (conn->FBBMsgsSent) + FlagSentMessages(conn, user); + + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + FBBputs(conn, "FQ\r"); + + conn->BBSFlags |= DISCONNECTING; + + // LinFBB needs a Disconnect Here + + if (conn->BPQBBS) + return; // BPQ will close when it sees FQ. Close collisions aren't good! + + if ((conn->SessType & Sess_PACTOR) == 0) + conn->CloseAfterFlush = 20; // 2 Secs + else + conn->CloseAfterFlush = 20; // PACTOR/WINMOR drivers support deferred disc so 5 secs should be enough + } + return; + + case 'S': + + // Proposal response + + Respptr=&Buffer[2]; + + for (i=0; i < conn->FBBIndex; i++) + { + FBBHeader = &conn->FBBHeaders[i]; + + if (FBBHeader->MsgType == 'P') + Index = PMSG; + else if (FBBHeader->MsgType == 'B') + Index = BMSG; + else if (FBBHeader->MsgType == 'T') + Index = TMSG; + + Respptr++; + + if (*Respptr == 'E') + { + // Rejected + + Logprintf(LOG_BBS, conn, '?', "Proposal %d Rejected by far end", i + 1); + } + + if ((*Respptr == '-') || (*Respptr == 'N') || (*Respptr == 'R') || (*Respptr == 'E')) // Not wanted + { + user->Total.MsgsRejectedOut[Index]++; + + // Zap the entry + + if (conn->Paclink || conn->RMSExpress || conn->PAT) // Not using Bit Masks + { + // Kill Messages sent to paclink/RMS Express unless BBS FWD bit set + + // What if WLE retrieves P message that is queued to differnet BBS? + // if we dont kill it will be offered again + + if (FBBHeader->FwdMsg->type == 'P' || (check_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber) == 0)) + FlagAsKilled(FBBHeader->FwdMsg, FALSE); + } + + clear_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(FBBHeader->FwdMsg->forw, user->BBSNumber); + + FBBHeader->FwdMsg->Locked = 0; // Unlock + + // Shouldn't we set P messages as Forwarded + // (or will check above have killed it if it is P with other FWD bits set) + // Maybe better to be safe !! + + if (FBBHeader->FwdMsg->type == 'P' || memcmp(FBBHeader->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + FBBHeader->FwdMsg->status = 'F'; // Mark as forwarded + FBBHeader->FwdMsg->datechanged=time(NULL); + } + + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); + + conn->UserPointer->ForwardingInfo->MsgCount--; + + SaveMessageDatabase(); + continue; + } + + // FBB uses H for HOLD, but I've never seen it. RMS Express sends H for Defer. + + + if (*Respptr == '=' || *Respptr == 'L' || (*Respptr == 'H' && conn->RMSExpress)) // Defer + { + // Remove entry from forwarding block + + FBBHeader->FwdMsg->Defered = 4; // Don't retry for the next few forward cycles + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); + continue; + } + + conn->RestartFrom = 0; // Assume Restart from + + if ((*Respptr == '!') || (*Respptr == 'A')) + { + // Restart + + char Num[10]; + char *numptr=&Num[0]; + + Respptr++; + + while (isdigit(*Respptr)) + { + *(numptr++) = *(Respptr++); + } + *numptr = 0; + + conn->RestartFrom = atoi(Num); + + *(--Respptr) = '+'; // So can drop through + } + + // FBB uses H for HOLD, but I've never seen it. RMS Express sends H for Defer. RMS use trapped above + + if ((*Respptr == '+') || (*Respptr == 'Y') || (*Respptr == 'H')) + { + struct tm * tm; + time_t now; + char * MsgBytes; + + conn->FBBMsgsSent = TRUE; // Messages to flag as complete when next command received + AllRejected = FALSE; + + if (conn->BBSFlags & FBBForwarding) + { + if (conn->BBSFlags & FBBB2Mode) + SendCompressedB2(conn, FBBHeader); + else + SendCompressed(conn, FBBHeader->FwdMsg); + } + else + { + nodeprintf(conn, "%s\r\n", FBBHeader->FwdMsg->title); + + MsgBytes = ReadMessageFile(FBBHeader->FwdMsg->number); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + FBBHeader->FwdMsg->length = (int)strlen(MsgBytes); + } + + now = time(NULL); + + tm = gmtime(&now); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + FBBHeader->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + BBSputs(conn, "\r\n"); + + QueueMsg(conn, MsgBytes, FBBHeader->FwdMsg->length); + free(MsgBytes); + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += FBBHeader->FwdMsg->length; + + nodeprintf(conn, "%c\r\n", 26); + } + continue; + } + BBSputs(conn, "*** Protocol Error - Invalid Proposal Response'\r"); + } + + conn->FBBIndex = 0; // ready for next block; + conn->FBBChecksum = 0; + + + if (AllRejected && (conn->RMSExpress || conn->PAT)) + { + // RMS Express and PAT don't send FF or proposal after rejecting all messages + + FBBputs(conn, "FF\r"); + } + + return; + + case 'Q': + + if (conn->FBBMsgsSent) + FlagSentMessages(conn, user); + + conn->BBSFlags |= DISCONNECTING; + + Disconnect(conn->BPQStream); + return; + + case 'A': // Proposal + case 'B': // Proposal + + if (conn->FBBMsgsSent) + FlagSentMessages(conn, user); // Mark previously sent messages + + if (conn->DoReverse == FALSE) // Dont accept messages + return; + + // Accumulate checksum + + for (i=0; i< len; i++) + { + conn->FBBChecksum+=Buffer[i]; + } + + // Parse Header + + // Find free line + + for (i = 0; i < 5; i++) + { + FBBHeader = &conn->FBBHeaders[i]; + + if (FBBHeader->Format == 0) + break; + } + + if (i == 5) + { + BBSputs(conn, "*** Protocol Error - Too Many Proposals\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + } + + //FA P GM8BPQ G8BPQ G8BPQ 2209_GM8BPQ 8 + + FBBHeader->Format = Buffer[1]; + + ptr = strtok_s(&Buffer[3], seps, &Context); + + if (ptr == NULL) goto badparam; + + if (strlen(ptr) != 1) goto badparam; + + FBBHeader->MsgType = *ptr; + + if (FBBHeader->MsgType == 'P') + Index = PMSG; + else if (FBBHeader->MsgType == 'B') + Index = BMSG; + else if (FBBHeader->MsgType == 'T') + Index = TMSG; + + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) goto badparam; + strlop(ptr, '-'); // Remove any (illegal) ssid + + if (strlen(ptr) > 6 ) goto badparam; + + strcpy(FBBHeader->From, ptr); + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) goto badparam; + + if (strlen(ptr) > 40 ) goto badparam; + + strcpy(FBBHeader->ATBBS, ptr); + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) goto badparam; + + if (strlen(ptr) > 6) + { + // Temp fix - reject instead of breaking connection + + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; + Logprintf(LOG_BBS, conn, '?', "Message Rejected as TO field too long"); + + user->Total.MsgsRejectedIn[Index]++; + return; + } + + strlop(ptr, '-'); // Remove any (illegal) ssid + + strcpy(FBBHeader->To, ptr); + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) goto badparam; + + if (strlen(ptr) > 12 ) goto badparam; + + strcpy(FBBHeader->BID, ptr); + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) goto badparam; + + FBBHeader->Size = atoi(ptr); + + goto ok; + +badparam: + + BBSputs(conn, "*** Protocol Error - Proposal format error\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + return; + +ok: + + // Check Filters + + if (CheckRejFilters(FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->BID, FBBHeader->MsgType, FBBHeader->Size)) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; + Logprintf(LOG_BBS, conn, '?', "Message Rejected by Filters"); + + user->Total.MsgsRejectedIn[Index]++; + } + + // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message + // If we do, reject it. If not, accept it again. (do we need some loop protection ???) + + else if (DoWeWantIt(conn, FBBHeader) == FALSE) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; + user->Total.MsgsRejectedIn[Index]++; + } + else if ((RestartPtr = LookupRestart(conn, FBBHeader)) > 0) + { + conn->FBBReplyIndex += sprintf(&conn->FBBReplyChars[conn->FBBReplyIndex], "!%d", RestartPtr); + } + else if (LookupTempBID(FBBHeader->BID)) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '='; + } + else + { + + // Save BID in temp list in case we are offered it again before completion + + BIDRec * TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, FBBHeader->BID); + TempBID->u.conn = conn; + + conn->FBBReplyChars[conn->FBBReplyIndex++] = '+'; + } + + FBBHeader->B2Message = FALSE; + + return; + + case 'C': // B2 Proposal + + if (conn->FBBMsgsSent) + FlagSentMessages(conn, user); // Mark previously sent messages + + if (conn->DoReverse == FALSE) // Dont accept messages + return; + + // Accumulate checksum + + for (i=0; i< len; i++) + { + conn->FBBChecksum+=Buffer[i]; + } + + // Parse Header + + // Find free line + + for (i = 0; i < 5; i++) + { + FBBHeader = &conn->FBBHeaders[i]; + + if (FBBHeader->Format == 0) + break; + } + + if (i == 5) + { + BBSputs(conn, "*** Protocol Error - Too Many Proposals\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + } + + + // FC EM A3EDD4P00P55 377 281 0 + + /* + + FC Proposal code. Requires B2 SID feature. + Type Message type ( 1 or 2 alphanumeric characters + + CM WinLink 2000 Control message + EM Encapsulated Message + ID Unique Message Identifier (max length 12 characters) + U-Size Uncompressed size of message + C-size Compressed size of message + + */ + FBBHeader->Format = Buffer[1]; + + ptr = strtok_s(&Buffer[3], seps, &Context); + if (ptr == NULL) goto badparam2; + if (strlen(ptr) != 2) goto badparam2; + FBBHeader->MsgType = 'P'; //ptr[0]; + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) goto badparam2; + + // Relay In RO mode adds @MPS@R to the MID. Don't know why (yet!) + + MPS = strlop(ptr, '@'); + if (MPS) + ROChar = strlop(MPS, '@'); + + if (strlen(ptr) > 12 ) goto badparam; + strcpy(FBBHeader->BID, ptr); + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) goto badparam2; + FBBHeader->Size = atoi(ptr); + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) goto badparam2; + FBBHeader->CSize = atoi(ptr); + FBBHeader->B2Message = TRUE; + + // If using BPQ Extensions (From To AT in proposal) Check Filters + + Buffer[len - 1] = 0; + + if (conn->BPQBBS) + { + char * From = strtok_s(NULL, seps, &Context); + char * ATBBS = strtok_s(NULL, seps, &Context); + char * To = strtok_s(NULL, seps, &Context); + char * Type = strtok_s(NULL, seps, &Context); + + if (From && To && ATBBS && Type && CheckRejFilters(From, To, ATBBS, NULL, *Type, FBBHeader->Size)) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; + user->Total.MsgsRejectedIn[Index]++; + Logprintf(LOG_BBS, conn, '?', "Message Rejected by Filters"); + + return; + } + } + goto ok2; + +badparam2: + + BBSputs(conn, "*** Protocol Error - Proposal format error\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + return; + +ok2: + if (LookupBID(FBBHeader->BID)) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; + Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); + user->Total.MsgsRejectedIn[Index]++; + + } + else if (FBBHeader->Size > MaxRXSize) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; + Logprintf(LOG_BBS, conn, '?', "Message Rejected by Size Limit"); + user->Total.MsgsRejectedIn[Index]++; + + } + else if ((RestartPtr = LookupRestart(conn, FBBHeader)) > 0) + { + conn->FBBReplyIndex += sprintf(&conn->FBBReplyChars[conn->FBBReplyIndex], "!%d", RestartPtr); + } + + else if (LookupTempBID(FBBHeader->BID)) + { + memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header + conn->FBBReplyChars[conn->FBBReplyIndex++] = '='; + } + else + { + // Save BID in temp list in case we are offered it again before completion + + BIDRec * TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, FBBHeader->BID); + TempBID->u.conn = conn; + + conn->FBBReplyChars[conn->FBBReplyIndex++] = 'Y'; + } + + return; + + case '>': + + // Optional Checksum + + if (conn->DoReverse == FALSE) // Dont accept messages + { + Logprintf(LOG_BBS, conn, '?', "Reverse Forwarding not allowed"); + Disconnect(conn->BPQStream); + return; + } + + if (len > 3) + { + int sum; + + sscanf(&Buffer[3], "%x", &sum); + + conn->FBBChecksum+=sum; + + if (conn->FBBChecksum) + { + BBSputs(conn, "*** Proposal Checksum Error\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + return; + } + } + + // Return "FS ", followed by +-= for each proposal + + conn->FBBReplyChars[conn->FBBReplyIndex] = 0; + conn->FBBReplyIndex = 0; + + nodeprintfEx(conn, "FS %s\r", conn->FBBReplyChars); + + // if all rejected, send proposals or prompt, else set up for first message + + FBBHeader = &conn->FBBHeaders[0]; + + if (FBBHeader->MsgType == 0) + { + conn->FBBIndex = 0; // ready for first block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + conn->FBBChecksum = 0; + + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + conn->InputMode = 0; + + if (conn->DoReverse) + FBBputs(conn, "FF\r"); + else + { + FBBputs(conn, "FQ\r"); + conn->CloseAfterFlush = 20; // 2 Secs + } + } + } + else + { + if (conn->BBSFlags & FBBForwarding) + { + conn->InputMode = 'B'; + } + + CreateMessage(conn, FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->MsgType, FBBHeader->BID, NULL); + } + + return; + + } + + return; +} + +VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user) +{ + struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block + int i; + + conn->FBBMsgsSent = FALSE; + + for (i=0; i < 5; i++) + { + FBBHeader = &conn->FBBHeaders[i]; + + if (FBBHeader && FBBHeader->MsgType) // Not a zapped entry + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", FBBHeader->FwdMsg->number); + sprintf(Title, "Message %d Held - Rejected by Winlink", FBBHeader->FwdMsg->number); + SendMessageToSYSOP(Title, MailBuffer, Length); + + FBBHeader->FwdMsg->status = 'H'; // Mark as Held + } + } + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + SaveMessageDatabase(); +} + + + +VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user) +{ + struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block + int i; + + // Called if FBB command received after sending a block of messages . Flag as as sent. + + conn->FBBMsgsSent = FALSE; + + for (i=0; i < 5; i++) + { + FBBHeader = &conn->FBBHeaders[i]; + + if (FBBHeader && FBBHeader->MsgType) // Not a zapped entry + { + if ((conn->Paclink || conn->RMSExpress || conn->PAT) && +// ((conn->UserPointer->flags & F_NTSMPS) == 0) && + (FBBHeader->FwdMsg->type == 'P')) + { + // Kill Messages sent to paclink/RMS Express unless BBS FWD bit set + + if (check_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber) == 0) + { + FlagAsKilled(FBBHeader->FwdMsg, FALSE); + continue; + } + } + + clear_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(FBBHeader->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(FBBHeader->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + FBBHeader->FwdMsg->status = 'F'; // Mark as forwarded + FBBHeader->FwdMsg->datechanged=time(NULL); + } + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(FBBHeader->FwdMsg); +#endif + + FBBHeader->FwdMsg->Locked = 0; // Unlock + conn->UserPointer->ForwardingInfo->MsgCount--; + } + } + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + SaveMessageDatabase(); +} + + +VOID SetupNextFBBMessage(CIRCUIT * conn) +{ + struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block + + memmove(&conn->FBBHeaders[0], &conn->FBBHeaders[1], 4 * sizeof(struct FBBHeaderLine)); + + memset(&conn->FBBHeaders[4], 0, sizeof(struct FBBHeaderLine)); + + FBBHeader = &conn->FBBHeaders[0]; + + if (FBBHeader->MsgType == 0) + { + conn->FBBIndex = 0; // ready for next block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + + conn->FBBChecksum = 0; + conn->InputMode = 0; + + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + conn->InputMode = 0; + FBBputs(conn, "FF\r"); + } + } + else + { + if (conn->BBSFlags & FBBForwarding) + conn->InputMode = 'B'; + + CreateMessage(conn, FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->MsgType, FBBHeader->BID, NULL); + } +} + +BOOL FBBDoForward(CIRCUIT * conn) +{ + int i; + char proposal[100]; + int proplen; + + if (FindMessagestoForward(conn)) + { + // Send Proposal Block + + struct FBBHeaderLine * FBBHeader; + + for (i=0; i < conn->FBBIndex; i++) + { + FBBHeader = &conn->FBBHeaders[i]; + + if (conn->BBSFlags & FBBB2Mode) + + if (conn->BPQBBS) + + // Add From and To Header for Filters + + proplen = sprintf(proposal, "FC EM %s %d %d %s %s %s %c\r", + FBBHeader->BID, + FBBHeader->Size, + FBBHeader->CSize, + FBBHeader->From, + (FBBHeader->ATBBS[0]) ? FBBHeader->ATBBS : conn->UserPointer->Call, + FBBHeader->To, + FBBHeader->MsgType); + + else + + // FC EM A3EDD4P00P55 377 281 0 + + proplen = sprintf(proposal, "FC EM %s %d %d %d\r", + FBBHeader->BID, + FBBHeader->Size, + FBBHeader->CSize, 0); + + else + proplen = sprintf(proposal, "%s %c %s %s %s %s %d\r", + (conn->BBSFlags & FBBCompressed) ? "FA" : "FB", + FBBHeader->MsgType, + FBBHeader->From, + (FBBHeader->ATBBS[0]) ? FBBHeader->ATBBS : conn->UserPointer->Call, + FBBHeader->To, + FBBHeader->BID, + FBBHeader->Size); + + // Accumulate checksum + + while(proplen > 0) + { + conn->FBBChecksum+=proposal[--proplen]; + } + + FBBputs(conn, proposal); + } + + conn->FBBChecksum = - conn->FBBChecksum; + + nodeprintfEx(conn, "F> %02X\r", conn->FBBChecksum); + + return TRUE; + } + + return FALSE; +} + +VOID UnpackFBBBinary(CIRCUIT * conn) +{ + int MsgLen, i, offset, n; + UCHAR * ptr; + +loop: + + if (conn->CloseAfterFlush) // Failed (or complete), so discard rest of input + { + conn->InputLen = 0; + return; + } + + + ptr = conn->InputBuffer; + + if (conn->InputLen < 2) + return; // All formats need at least two bytes + + switch (*ptr) + { + case 1: // Header + + MsgLen = ptr[1] + 2; + + if (conn->InputLen < MsgLen) + return; // Wait for more + + if (strlen(&ptr[2]) > 60) + { + memcpy(conn->TempMsg->title, &ptr[2], 60); + conn->TempMsg->title[60] = 0; + Debugprintf("FBB Subject too long - truncated, %s", &ptr[2]); + } + else + strcpy(conn->TempMsg->title, &ptr[2]); + + offset = atoi(ptr+3+strlen(&ptr[2])); + + ptr += MsgLen; + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= MsgLen; + + conn->FBBChecksum = 0; + + if (offset) + { + struct FBBRestartData * RestartRec; + + // Trying to restart - make sure we have restart data + + for (i = 1; i <= RestartCount; i++) + { + RestartRec = RestartData[i]; + + if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) + && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0)) + { + if (RestartRec->length <= offset) + { + conn->TempMsg->length = RestartRec->length; + conn->MailBuffer = RestartRec->MailBuffer; + conn->MailBufferSize = RestartRec->MailBufferSize; + + // FBB Seems to insert 6 Byte message + // It looks like the original csum and length - perhaps a a consistancy check + + // But Airmail Sends the Restart Data in the next packet, move the check code. + + conn->NeedRestartHeader = TRUE; + + goto GotRestart; + } + else + { + BBSputs(conn, "*** Trying to restart from invalid position.\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + + // Remove Restart info + + for (n = i; n < RestartCount; n++) + { + RestartData[n] = RestartData[n+1]; // move down all following entries + } + RestartCount--; + SaveRestartData(); + } + } + + // No Restart Data + + BBSputs(conn, "*** Trying to restart, but no restart data.\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + + // Create initial buffer of 10K. Expand if needed later + + if (conn->MailBufferSize == 0) + { + // Dont allocate if restarting + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + } + + GotRestart: + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to create Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + + goto loop; + + + + case 2: // Data Block + + if (ptr[1] == 0) + MsgLen = 256; + else + MsgLen = ptr[1]; + + if (conn->InputLen < (MsgLen + 2)) + return; // Wait for more + + // If waiting for Restart Header, see if it has arrived + + if (conn->NeedRestartHeader) + { + conn->NeedRestartHeader = FALSE; + + if (MsgLen == 6) + { + ptr = conn->InputBuffer+2; + conn->InputLen -=8; + + for (i=0; i<6; i++) + { + conn->FBBChecksum+=ptr[0]; + ptr++; + } + memmove(conn->InputBuffer, ptr, conn->InputLen); + } + else + { + BBSputs(conn, "*** Restart Header Missing.\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + } + + goto loop; + + } + // Process it + + ptr+=2; + + for (i=0; i< MsgLen; i++) + { + conn->FBBChecksum+=ptr[i]; + } + + ptr-=2; + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], &ptr[2], MsgLen); + + conn->TempMsg->length += MsgLen; + + MsgLen +=2; + + ptr += MsgLen; + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= MsgLen; + + goto loop; + + + case 4: // EOM + + // Process EOM + + conn->FBBChecksum+=ptr[1]; + + if (conn->FBBChecksum == 0) + { +#ifndef LINBPQ + __try + { +#endif + conn->InputMode = 0; // So we won't save Restart data if decode fails + DeleteRestartData(conn); + Decode(conn, 0); // Setup Next Message will reset InputMode if needed +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + BBSputs(conn, "*** Program Error Decoding Message\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + return; + } +#endif + } + + else + { + BBSputs(conn, "*** Message Checksum Error\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + // Don't allow restart, as saved data is probably duff + + conn->DontSaveRestartData = TRUE; + return; + } + ptr += 2; + + memmove(conn->InputBuffer, ptr, conn->InputLen-2); + + conn->InputLen -= 2; + + goto loop; + + default: + + BBSputs(conn, "*** Protocol Error - Invalid Binary Message Format (Invalid Block Type)\r"); + Flush(conn); + + if (conn->CloseAfterFlush == 0) + { + // Dont do it more than once + + conn->CloseAfterFlush = 20; // 2 Secs + + // Don't allow restart, as saved data is probably duff + + // Actually all but the last block is probably OK, but maybe + // not worth the risk of restarting + + // Actually I think it is + + if (conn->TempMsg->length > 256) + { + conn->TempMsg->length -= 256; + conn->DontSaveRestartData = FALSE; + } + else + conn->DontSaveRestartData = TRUE; + } + return; + } +} + +VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg) +{ + struct tm * tm; + char * MsgBytes, * Save; + UCHAR * Compressed, * Compressedptr; + UCHAR * UnCompressed; + char * Title; + UCHAR * Output, * Outputptr; + int i, OrigLen, MsgLen, CompLen, DataOffset; + char Rline[80]; + int RLineLen; + int Index; + time_t temp; + + if (FwdMsg->type == 'P') + Index = PMSG; + else if (FwdMsg->type == 'B') + Index = BMSG; + else if (FwdMsg->type == 'T') + Index = TMSG; + + MsgBytes = Save = ReadMessageFile(FwdMsg->number); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + FwdMsg->length = (int)strlen(MsgBytes); + } + + OrigLen = FwdMsg->length; + + Title = FwdMsg->title; + + Compressed = Compressedptr = zalloc(2 * OrigLen + 200); + Output = Outputptr = zalloc(2 * OrigLen + 200); + + *Outputptr++ = 1; + *Outputptr++ = (int)strlen(Title) + 8; + strcpy(Outputptr, Title); + Outputptr += strlen(Title) +1; + sprintf(Outputptr, "%6d", conn->RestartFrom); + Outputptr += 7; + + DataOffset = (int)(Outputptr - Output); // Used if restarting + + memcpy(&temp, &FwdMsg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + sprintf(Rline, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message + strcat(Rline, "\r\n"); + + RLineLen = (int)strlen(Rline); + + MsgLen = OrigLen + RLineLen; + + UnCompressed = zalloc(MsgLen+10); + + strcpy(UnCompressed, Rline); + + // If a B2 Message, Remove B2 Header + + if (FwdMsg->B2Flags & B2Msg) + { + char * ptr; + int BodyLen = OrigLen; + + // Remove all B2 Headers, and all but the first part. + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr) + { + BodyLen = atoi(&ptr[5]); + ptr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (ptr) + ptr +=4; + else + ptr = MsgBytes; + + } + else + ptr = MsgBytes; + + if (memcmp(ptr, "R:", 2) == 0) // Already have RLines, so remove blank line after new R:line + RLineLen -= 2; + + memcpy(&UnCompressed[RLineLen], ptr, BodyLen); + + MsgLen = BodyLen + RLineLen; + } + else // Not B2 Message + { + memcpy(&UnCompressed[RLineLen], MsgBytes, OrigLen); + } + + CompLen = Encode(UnCompressed, Compressed, MsgLen, conn->BBSFlags & FBBB1Mode, conn->BBSFlags & FBBCompressed); + + conn->FBBChecksum = 0; + + // If restarting, send the checksum and length as a single record, then data from the restart point + // The count includes the header, so adjust count and pointers + + if (conn->RestartFrom) + { + *Outputptr++ = 2; + *Outputptr++ = 6; + + for (i=0; i< 6; i++) + { + conn->FBBChecksum+=Compressed[i]; + *Outputptr++ = Compressed[i]; + } + + for (i=conn->RestartFrom; i< CompLen; i++) + { + conn->FBBChecksum+=Compressed[i]; + } + + Compressedptr += conn->RestartFrom; + CompLen -= conn->RestartFrom; + } + else + { + for (i=0; i< CompLen; i++) + { + conn->FBBChecksum+=Compressed[i]; + } + } + + while (CompLen > 250) + { + *Outputptr++ = 2; + *Outputptr++ = 250; + + memcpy(Outputptr, Compressedptr, 250); + Outputptr += 250; + Compressedptr += 250; + CompLen -= 250; + } + + *Outputptr++ = 2; + *Outputptr++ = CompLen; + + memcpy(Outputptr, Compressedptr, CompLen); + + Outputptr += CompLen; + + *Outputptr++ = 4; + conn->FBBChecksum = - conn->FBBChecksum; + *Outputptr++ = conn->FBBChecksum; + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = Output; + unsigned char * ptr2 = Compressed; // Reuse Compressed buffer + size_t Len = Outputptr - Output; + unsigned char c; + + while (Len--) + { + c = *(ptr1++); + *(ptr2++) = c; + if (c == 0xff) // FF becodes FFFF + *(ptr2++) = c; + } + + QueueMsg(conn, Compressed, (int)(ptr2 - Compressed)); + } + else + QueueMsg(conn, Output, (int)(Outputptr - Output)); + + free(Save); + free(Compressed); + free(UnCompressed); + free(Output); + +} + +BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline) +{ + char * MsgBytes; + UCHAR * Compressed; + UCHAR * UnCompressed; + int OrigLen, MsgLen, B2HddrLen, CompLen; + char Date[20]; + struct tm * tm; + char B2From[80]; + char B2To[80]; + struct MsgInfo * Msg = FBBHeader->FwdMsg; + struct UserInfo * FromUser; + int BodyLineToBody; + int RlineLen = (int)strlen(Rline) ; + char * TypeString; +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + + if (Msg == NULL) + Debugprintf("Msg = NULL"); + + + MsgBytes = ReadMessageFile(Msg->number); + + if (MsgBytes == 0) + { + Debugprintf("B2 Message - Message File not found"); + return FALSE; + } + + UnCompressed = zalloc(Msg->length + 2000); + + if (UnCompressed == NULL) + Debugprintf("B2 Message - zalloc for %d failed", Msg->length + 2000); + + OrigLen = Msg->length; + + // If a B2 Message add R:line at start of Body, but otherwise leave intact. + // Unless a message to Paclink, when we must remove any HA from the TO address + // Or to a CMS, when we remove HA from From or Reply-to + + if (Msg->B2Flags & B2Msg) + { + char * ptr, *ptr2; + int BodyLen; + int BodyLineLen; + int Index; + + MsgLen = OrigLen + RlineLen; + + if (conn->Paclink) + { + // Remove any HA on the TO address + + ptr = strstr(MsgBytes, "To:"); + if (ptr) + { + ptr2 = strstr(ptr, "\r\n"); + if (ptr2) + { + while (ptr < ptr2) + { + if (*ptr == '.' || *ptr == '@') + { + memset(ptr, ' ', ptr2 - ptr); + break; + } + ptr++; + } + } + } + } + + if (conn->WL2K) + { + // Remove any HA on the From or Reply-To address + + ptr = strstr(MsgBytes, "From:"); + if (ptr == NULL) + ptr = strstr(MsgBytes, "Reply-To:"); + + if (ptr) + { + ptr2 = strstr(ptr, "\r\n"); + if (ptr2) + { + while (ptr < ptr2) + { + if (*ptr == '.' || *ptr == '@') + { + memset(ptr, ' ', ptr2 - ptr); + break; + } + ptr++; + } + } + } + } + + + // Add R: Line at start of body. Will Need to Update Body Length + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr == 0) + { + Debugprintf("B2 Messages without Body: Line"); + return FALSE; + } + ptr2 = strstr(ptr, "\r\n"); + + Index = (int)(ptr - MsgBytes); // Bytes Before Body: line + + if (Index <= 0 || Index > MsgLen) + { + Debugprintf("B2 Message Body: line position invalid - %d", Index); + return FALSE; + } + + // If message to saildocs adding an R: line will mess up the message processing, so add as an X header + + if (strstr(MsgBytes, "To: query@saildocs.com")) + { + int x_Len; + + memcpy(UnCompressed, MsgBytes, Index); // Up to Old Body; + x_Len = sprintf(&UnCompressed[Index], "x-R: %s", &Rline[2]); + MsgLen = OrigLen + x_Len; + Index +=x_Len; + goto copyRest; + } + + BodyLen = atoi(&ptr[5]); + + if (BodyLen < 0 || BodyLen > MsgLen) + { + Debugprintf("B2 Message Length from Body: line invalid - Msg len %d From Body %d", MsgLen, BodyLen); + return FALSE; + } + + BodyLineLen = (int)(ptr2 - ptr) + 2; + MsgLen -= BodyLineLen; // Length of Body Line may change + + ptr = strstr(ptr2, "\r\n\r\n"); // Blank line before Body + + if (ptr == 0) + { + Debugprintf("B2 Message - No Blank Line before Body"); + return FALSE; + } + + ptr += 4; + + ptr2 += 2; // Line Following Original Body: Line + + BodyLineToBody = (int)(ptr - ptr2); + + if (memcmp(ptr, "R:", 2) != 0) // No R line, so must be our message + { + strcat(Rline, "\r\n"); + RlineLen += 2; + MsgLen += 2; + } + BodyLen += RlineLen; + + memcpy(UnCompressed, MsgBytes, Index); // Up to Old Body; + BodyLineLen = sprintf(&UnCompressed[Index], "Body: %d\r\n", BodyLen); + + MsgLen += BodyLineLen; // Length of Body Line may have changed + Index += BodyLineLen; + + if (BodyLineToBody < 0 || BodyLineToBody > 1000) + { + Debugprintf("B2 Message - Body too far from Body Line - %d", BodyLineToBody); + return FALSE; + } + memcpy(&UnCompressed[Index], ptr2, BodyLineToBody); // Stuff Between Body: Line and Body + + Index += BodyLineToBody; + + memcpy(&UnCompressed[Index], Rline, RlineLen); + Index += RlineLen; + +copyRest: + + memcpy(&UnCompressed[Index], ptr, MsgLen - Index); // Rest of Message + + FBBHeader->Size = MsgLen; + + Compressed = zalloc(2 * MsgLen + 200); +#ifndef LINBPQ + __try { +#endif + CompLen = Encode(UnCompressed, Compressed, MsgLen, TRUE, conn->BBSFlags & FBBCompressed); + + FBBHeader->CompressedMsg = Compressed; + FBBHeader->CSize = CompLen; + + free(UnCompressed); + return TRUE; +#ifndef LINBPQ + } My__except_Routine("Encode B2Message"); +#endif + return FALSE; + } + + + if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message + { + strcat(Rline, "\r\n"); + RlineLen += 2; + } + + MsgLen = OrigLen + RlineLen; + +// if (conn->RestartFrom == 0) +// { +// // save time first sent, or checksum will be wrong when we restart +// +// FwdMsg->datechanged=time(NULL); +// } + + tm = gmtime((time_t *)&Msg->datechanged); + + sprintf(Date, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + // We create the B2 Header +/* + MID: XR88I1J160EB + Date: 2009/07/25 18:17 + Type: Private + From: SMTP:john.wiseman@ntlworld.com + To: G8BPQ + Subject: RE: RMS Test Message + Mbo: SMTP + Body: 213 + +*/ + if (strcmp(Msg->to, "RMS") == 0) // Address is in via + strcpy(B2To, Msg->via); + else + if (Msg->via[0] && (!conn->Paclink)) + sprintf(B2To, "%s@%s", Msg->to, Msg->via); + else + strcpy(B2To, Msg->to); + + // Try to create a full from: addrsss so RMS Express can reply + + strcpy(B2From, Msg->from); + + Logprintf(LOG_BBS, conn, '?', "B2 From %s", B2From); + + if (strcmp(conn->Callsign, "RMS") != 0 && conn->WL2K == 0) // if going to RMS - just send calll + { + if (_stricmp(Msg->from, "SMTP:") == 0) // Address is in via + strcpy(B2From, Msg->emailfrom); + else + { + FromUser = LookupCall(Msg->from); + + if (FromUser) + { + Logprintf(LOG_BBS, conn, '?', "B2 From - Local User"); + + if (FromUser->HomeBBS[0]) + sprintf(B2From, "%s@%s", Msg->from, FromUser->HomeBBS); + else + sprintf(B2From, "%s@%s", Msg->from, BBSName); + } + else + { + WPRecP WP = LookupWP(Msg->from); + + Logprintf(LOG_BBS, conn, '?', "B2 From - not local User"); + + if (WP) + sprintf(B2From, "%s@%s", Msg->from, WP->first_homebbs); + } + } + } + + Logprintf(LOG_BBS, conn, '?', "B2 From Finally %s", B2From); + + if (Msg->type == 'P') + TypeString = "Private" ; + else if (Msg->type == 'B') + TypeString = "Bulletin"; + else if (Msg->type == 'T') + TypeString = "Traffic"; + + B2HddrLen = sprintf(UnCompressed, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n" + "Content-Type: text/plain\r\nContent-Transfer-Encoding: 8bit\r\nBody: %d\r\n\r\n", + Msg->bid, Date, TypeString, B2From, B2To, Msg->title, BBSName, MsgLen); + + + memcpy(&UnCompressed[B2HddrLen], Rline, RlineLen); + memcpy(&UnCompressed[B2HddrLen + RlineLen], MsgBytes, OrigLen); // Rest of Message + + MsgLen += B2HddrLen; + + FBBHeader->Size = MsgLen; + + Compressed = zalloc(2 * MsgLen + 200); + + CompLen = Encode(UnCompressed, Compressed, MsgLen, TRUE, conn->BBSFlags & FBBCompressed); + + FBBHeader->CompressedMsg = Compressed; + FBBHeader->CSize = CompLen; + + free(UnCompressed); + + return TRUE; +#ifndef LINBPQ + } My__except_Routine("CreateB2Message"); +#endif + return FALSE; + +} + +VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader) +{ + UCHAR * Compressed, * Compressedptr; + UCHAR * Output, * Outputptr; + int i, CompLen; + int Index; + + if (FBBHeader->FwdMsg->type == 'P') + Index = PMSG; + else if (FBBHeader->FwdMsg->type == 'B') + Index = BMSG; + else if (FBBHeader->FwdMsg->type == 'T') + Index = TMSG; + + Compressed = Compressedptr = FBBHeader->CompressedMsg; + + Output = Outputptr = zalloc(FBBHeader->CSize + 10000); + + *Outputptr++ = 1; + *Outputptr++ = (int)strlen(FBBHeader->FwdMsg->title) + 8; + strcpy(Outputptr, FBBHeader->FwdMsg->title); + Outputptr += strlen(FBBHeader->FwdMsg->title) +1; + sprintf(Outputptr, "%06d", conn->RestartFrom); + Outputptr += 7; + + CompLen = FBBHeader->CSize; + + conn->FBBChecksum = 0; + + // If restarting, send the checksum and length as a single record, then data from the restart point + // The count includes the header, so adjust count and pointers + + if (conn->RestartFrom) + { + *Outputptr++ = 2; + *Outputptr++ = 6; + + for (i=0; i< 6; i++) + { + conn->FBBChecksum+=Compressed[i]; + *Outputptr++ = Compressed[i]; + } + + for (i=conn->RestartFrom; i< CompLen; i++) + { + conn->FBBChecksum+=Compressed[i]; + } + + Compressedptr += conn->RestartFrom; + CompLen -= conn->RestartFrom; + } + else + { + for (i=0; i< CompLen; i++) + { + conn->FBBChecksum+=Compressed[i]; + } + conn->UserPointer->Total.MsgsSent[Index]++; + conn->UserPointer->Total.BytesForwardedOut[Index] += FBBHeader->FwdMsg->length; + + } + + while (CompLen > 256) + { + *Outputptr++ = 2; + *Outputptr++ = 0; + + memcpy(Outputptr, Compressedptr, 256); + Outputptr += 256; + Compressedptr += 256; + CompLen -= 256; + } + + *Outputptr++ = 2; + *Outputptr++ = CompLen; + + memcpy(Outputptr, Compressedptr, CompLen); + + Outputptr += CompLen; + + *Outputptr++ = 4; + conn->FBBChecksum = - conn->FBBChecksum; + *Outputptr++ = conn->FBBChecksum; + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = Output; + unsigned char * ptr2 = Compressed; // Reuse Compressed buffer + int Len = (int)(Outputptr - Output); + unsigned char c; + + while (Len--) + { + c = *(ptr1++); + *(ptr2++) = c; + if (c == 0xff) // FF becodes FFFF + *(ptr2++) = c; + } + + QueueMsg(conn, Compressed, (int)(ptr2 - Compressed)); + } + else + QueueMsg(conn, Output, (int)(Outputptr - Output)); + + free(Compressed); + free(Output); +} + +// Restart Routines. + +VOID SaveFBBBinary(CIRCUIT * conn) +{ + // Disconnected during binary transfer + + char Msg[120]; + int i, len; + struct FBBRestartData * RestartRec = NULL; + + if (conn->TempMsg == NULL) + return; + + if (conn->TempMsg->length < 256) + return; // Not worth it. + + // If we already have a restart record, reuse it + + for (i = 1; i <= RestartCount; i++) + { + RestartRec = RestartData[i]; + + if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) + && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0)) + { + // Found it, so reuse + + // If we have more data, reset retry count + + if (RestartRec->length < conn->TempMsg->length) + RestartRec->Count = 0;; + + break; + } + } + + if (RestartRec == NULL) + { + RestartRec = zalloc(sizeof (struct FBBRestartData)); + + GetSemaphore(&AllocSemaphore, 0); + + RestartData=realloc(RestartData,(++RestartCount+1) * sizeof(void *)); + RestartData[RestartCount] = RestartRec; + + FreeSemaphore(&AllocSemaphore); + RestartRec->TimeCreated = time(NULL); + } + + strcpy(RestartRec->Call, conn->UserPointer->Call); + RestartRec->length = conn->TempMsg->length; + strcpy(RestartRec->bid, conn->TempMsg->bid); + RestartRec->MailBuffer = conn->MailBuffer; + RestartRec->MailBufferSize = conn->MailBufferSize; + + len = sprintf_s(Msg, sizeof(Msg), "Disconnect received from %s during Binary Transfer - %d Bytes Saved for restart", + conn->Callsign, conn->TempMsg->length); + + SaveRestartData(); + + WriteLogLine(conn, '|',Msg, len, LOG_BBS); +} + +void DeleteRestartData(CIRCUIT * conn) +{ + struct FBBRestartData * RestartRec = NULL; + int i, n; + + if (conn->TempMsg == NULL) + return; + + for (i = 1; i <= RestartCount; i++) + { + RestartRec = RestartData[i]; + + if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) + && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0)) + { + // Remove restrt data + + for (n = i; n < RestartCount; n++) + { + RestartData[n] = RestartData[n+1]; // move down all following entries + } + + RestartCount--; + SaveRestartData(); + return; + } + } +} + + +BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader) +{ + int i, n; + + struct FBBRestartData * RestartRec; + + if ((conn->BBSFlags & FBBB1Mode) == 0) + return FALSE; // Only B1 & B2 support restart + + for (i = 1; i <= RestartCount; i++) + { + RestartRec = RestartData[i]; + + if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0) + && (strcmp(RestartRec->bid, FBBHeader->BID) == 0)) + { + char Msg[120]; + int len; + + RestartRec->Count++; + + if (RestartRec->Count > 10) + { + len = sprintf_s(Msg, sizeof(Msg), "Too many restarts for %s - Requesting restart from beginning", + FBBHeader->BID); + + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + // Remove restrt data + + for (n = i; n < RestartCount; n++) + { + RestartData[n] = RestartData[n+1]; // move down all following entries + } + + RestartCount--; + SaveRestartData(); + return FALSE; + } + + len = sprintf_s(Msg, sizeof(Msg), "Restart Data found for %s - Requesting restart from %d", + FBBHeader->BID, RestartRec->length); + + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + return (RestartRec->length); + } + } + + return FALSE; // Not Found +} + + + +BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader) +{ + struct MsgInfo * Msg; + BIDRec * BID; + int m; + + if (RefuseBulls && FBBHeader->MsgType == 'B') + { + Logprintf(LOG_BBS, conn, '?', "Message Rejected by RefuseBulls"); + return FALSE; + } + if (FBBHeader->Size > MaxRXSize) + { + Logprintf(LOG_BBS, conn, '?', "Message Rejected by Size Check"); + return FALSE; + } + + BID = LookupBID(FBBHeader->BID); + + if (BID) + { + if (FBBHeader->MsgType == 'B') + { + Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); + return FALSE; + } + + // Treat P messages to SYSOP@WW as Bulls + + if (strcmp(FBBHeader->To, "SYSOP") == 0 && strcmp(FBBHeader->ATBBS, "WW") == 0) + { + Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); + return FALSE; + } + + m = NumberofMessages; + + while (m > 0) + { + Msg = MsgHddrPtr[m]; + + if (Msg->number == BID->u.msgno) + { + // if the same TO we will assume the same message + + if (strcmp(Msg->to, FBBHeader->To) == 0) + { + // We have this message. If we have already forwarded it, we should accept it again + + if ((Msg->status == 'N') || (Msg->status == 'Y')|| (Msg->status == 'H')) + { + Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check"); + return FALSE; // Dont want it + } + else + return TRUE; // Get it again + } + + // Same number. but different message (why?) Accept for now + + return TRUE; + } + + m--; + } + + return TRUE; // A personal Message we have had before, but don't still have. + } + else + { + // We don't know the BID + + return TRUE; // We want it + } +} + + + + diff --git a/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base b/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base index 3ea8f94..5103cfe 100644 --- a/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base +++ b/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base @@ -1,402 +1,402 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// C replacement for TNCCode.asm -// -#define Kernel - -#define _CRT_SECURE_NO_DEPRECATE - -#pragma data_seg("_BPQDATA") - -#include "time.h" -#include "stdio.h" -#include - -#include "cheaders.h" -#include "tncinfo.h" - -int C_Q_COUNT(VOID *PQ); -VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg); - -VOID TNCTimerProc() -{ - // CALLED AT 10 HZ - - int n = BPQHOSTSTREAMS; - PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; - TRANSPORTENTRY * Session; - UCHAR DISCFLAG = 0; - - while (n--) - { - // Action any DISC Requests (must be done in timer owning process) - - if (HOSTSESS->HOSTFLAGS & 0x40) // DISC REQUEST - { - if (HOSTSESS->HOSTFLAGS & 0x20) // Stay? - DISCFLAG = 'S'; - - HOSTSESS->HOSTFLAGS &= 0x9F; // Clear Flags - - Session = HOSTSESS->HOSTSESSION; - - if (Session == 0) // Gone?? - { - HOSTSESS->HOSTFLAGS |= 3; // STATE CHANGE -#ifndef LINBPQ - if (HOSTSESS->HOSTHANDLE); - { - PostMessage(HOSTSESS->HOSTHANDLE, BPQMsg, HOSTSESS->HOSTSTREAM, 4); - } -#endif - continue; - } - - if (Session->L4CROSSLINK) - Session->L4CROSSLINK->STAYFLAG = DISCFLAG; - - HOSTSESS->HOSTSESSION = 0; - HOSTSESS->HOSTFLAGS |= 3; // STATE CHANGE - - PostStateChange(Session); - - CloseSessionPartner(Session); // SEND CLOSE TO PARTNER (IF PRESENT) - } - - // Check Trace Q - - if (HOSTSESS->HOSTAPPLFLAGS & 0x80) - { - if (HOSTSESS->HOSTTRACEQ) - { - int Count = C_Q_COUNT(&HOSTSESS->HOSTTRACEQ); - - if (Count > 100) - ReleaseBuffer((void *)Q_REM((void *)&HOSTSESS->HOSTTRACEQ)); - } - } - HOSTSESS++; - } -} - -VOID SendSmartID(struct PORTCONTROL * PORT) -{ - struct _MESSAGE * ID = IDMSG; - struct _MESSAGE * Buffer; - - PORT->SmartIDNeeded = 0; - - Buffer = GetBuff(); - - if (Buffer) - { - memcpy(Buffer, ID, ID->LENGTH); - - Buffer->PORT = PORT->PORTNUMBER; - - // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD - - if (PORT->PORTCALL[0] > 0x40) - { - memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); - Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT - } - - // If Pactor Style add to UI_Q - - if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF && PORT->UICAPABLE) - { - EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; - - C_Q_ADD(&EXTPORT->UI_Q, Buffer); - return; - } - - PUT_ON_PORT_Q(PORT, Buffer); - } -} - - -VOID SENDIDMSG() -{ - struct PORTCONTROL * PORT = PORTTABLE; - struct _MESSAGE * ID = IDMSG; - struct _MESSAGE * Buffer; - - while (PORT) - { - if (PORT->PROTOCOL < 10) // Not Pactor-style - { - Buffer = GetBuff(); - - if (Buffer) - { - memcpy(Buffer, ID, ID->LENGTH); - - Buffer->PORT = PORT->PORTNUMBER; - - // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD - - if (PORT->PORTCALL[0] > 0x40) - { - memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); - Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT - } - C_Q_ADD(&IDMSG_Q, Buffer); - } - } - PORT = PORT->PORTPOINTER; - } -} - - - -VOID SENDBTMSG() -{ - struct PORTCONTROL * PORT = PORTTABLE; - struct _MESSAGE * Buffer; - char * ptr1, * ptr2; - - while (PORT) - { - if (PORT->PROTOCOL >= 10 || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? - { - PORT = PORT->PORTPOINTER; - continue; - } - - Buffer = GetBuff(); - - if (Buffer) - { - memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); - Buffer->DEST[6] |= 0xC0; // Set Command bits - - // Send from BBSCALL unless PORTBCALL defined - - if (PORT->PORTBCALL[0] > 32) - memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); - else if (APPLCALLTABLE->APPLCALL[0] > 32) - memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); - else - memcpy(Buffer->ORIGIN, MYCALL, 7); - - ptr1 = &PORT->PORTUNPROTO[7]; // First Digi - ptr2 = &Buffer->CTL; // Digi field in buffer - - // Copy any digis - - while (*(ptr1)) - { - memcpy(ptr2, ptr1, 7); - ptr1 += 7; - ptr2 += 7; - } - - *(ptr2 - 1) |= 1; // Set End of Address - *(ptr2++) = UI; - - memcpy(ptr2, &BTHDDR.PID, BTHDDR.LENGTH); - ptr2 += BTHDDR.LENGTH; - Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); - Buffer->PORT = PORT->PORTNUMBER; - - C_Q_ADD(&IDMSG_Q, Buffer); - } - PORT = PORT->PORTPOINTER; - } -} - -VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg) -{ - struct PORTCONTROL * PORT = PORTTABLE; - struct _MESSAGE * Buffer; - char * ptr1, * ptr2; - - Msg->LENGTH -= MSGHDDRLEN; // Remove Header - - while (PORT) - { - if ((PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? - { - PORT = PORT->PORTPOINTER; - continue; - } - - Buffer = GetBuff(); - - if (Buffer) - { - memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); - Buffer->DEST[6] |= 0xC0; // Set Command bits - - // Send from BBSCALL unless PORTBCALL defined - - if (PORT->PORTBCALL[0] > 32) - memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); - else if (APPLCALLTABLE->APPLCALL[0] > 32) - memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); - else - memcpy(Buffer->ORIGIN, MYCALL, 7); - - ptr1 = &PORT->PORTUNPROTO[7]; // First Digi - ptr2 = &Buffer->CTL; // Digi field in buffer - - // Copy any digis - - while (*(ptr1)) - { - memcpy(ptr2, ptr1, 7); - ptr1 += 7; - ptr2 += 7; - } - - *(ptr2 - 1) |= 1; // Set End of Address - *(ptr2++) = UI; - - memcpy(ptr2, &Msg->PID, Msg->LENGTH); - ptr2 += Msg->LENGTH; - Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); - Buffer->PORT = PORT->PORTNUMBER; - - if (PORT->PROTOCOL == 10) - { - EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; - C_Q_ADD(&EXTPORT->UI_Q, Buffer); - } - else - C_Q_ADD(&IDMSG_Q, Buffer); - } - PORT = PORT->PORTPOINTER; - } -} - -Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port) -{ - // Block included the 7/11 byte header, Len does not - - struct PORTCONTROL * PORT; - PMESSAGE Copy; - - if (Len > 360 - 15) - return; - - if (QCOUNT < 50) - return; // Running low - - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == 0) - return; - - Copy = GetBuff(); - - if (Copy == 0) - return; - - memcpy(&Copy->DEST[0], &Block[MSGHDDRLEN], Len); - - Copy->LENGTH = (USHORT)Len + MSGHDDRLEN; - - if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF) - { - // Pactor Style. Probably will only be used for Tracker uneless we do APRS over V4 or WINMOR - - EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; - - if (EXTPORT->UI_Q) - C_Q_ADD(&EXTPORT->UI_Q, Copy); - else - C_Q_ADD(&EXTPORT->UI_Q, Copy); - return; - } - - Copy->PORT = Port; - - PUT_ON_PORT_Q(PORT, Copy); -} - - -TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask) -{ - // Create a Transport (L4) session linked to an incoming HOST (API) Session - - TRANSPORTENTRY * NewSess = L4TABLE; - int Index = 0; - - - while (Index < MAXCIRCUITS) - { - if (NewSess->L4USER[0] == 0) - { - // Got One - - UCHAR * ourcall = &MYCALL[0]; - - // IF APPL PORT USE APPL CALL, ELSE NODE CALL - - if (ApplMask) - { - // Circuit for APPL - look for an APPLCALL - - APPLCALLS * APPL = APPLCALLTABLE; - - while ((ApplMask & 1) == 0) - { - ApplMask >>= 1; - APPL++; - } - if (APPL->APPLCALL[0] > 0x40) // We have an applcall - ourcall = &APPL->APPLCALL[0]; - } - - memcpy(NewSess->L4USER, ourcall, 7); - memcpy(NewSess->L4MYCALL, ourcall, 7); - - NewSess->CIRCUITINDEX = Index; //OUR INDEX - NewSess->CIRCUITID = NEXTID; - - NEXTID++; - if (NEXTID == 0) - NEXTID++; // Keep Non-Zero - - NewSess->L4TARGET.HOST = HOST; - NewSess->L4STATE = 5; - - - NewSess->SESSIONT1 = L4T1; - NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; - NewSess->SESSPACLEN = PACLEN; // Default; - - return NewSess; - } - Index++; - NewSess++; - } - - // Table Full - - return NULL; -} - - - - +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// C replacement for TNCCode.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "cheaders.h" +#include "tncinfo.h" + +int C_Q_COUNT(VOID *PQ); +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg); + +VOID TNCTimerProc() +{ + // CALLED AT 10 HZ + + int n = BPQHOSTSTREAMS; + PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; + TRANSPORTENTRY * Session; + UCHAR DISCFLAG = 0; + + while (n--) + { + // Action any DISC Requests (must be done in timer owning process) + + if (HOSTSESS->HOSTFLAGS & 0x40) // DISC REQUEST + { + if (HOSTSESS->HOSTFLAGS & 0x20) // Stay? + DISCFLAG = 'S'; + + HOSTSESS->HOSTFLAGS &= 0x9F; // Clear Flags + + Session = HOSTSESS->HOSTSESSION; + + if (Session == 0) // Gone?? + { + HOSTSESS->HOSTFLAGS |= 3; // STATE CHANGE +#ifndef LINBPQ + if (HOSTSESS->HOSTHANDLE); + { + PostMessage(HOSTSESS->HOSTHANDLE, BPQMsg, HOSTSESS->HOSTSTREAM, 4); + } +#endif + continue; + } + + if (Session->L4CROSSLINK) + Session->L4CROSSLINK->STAYFLAG = DISCFLAG; + + HOSTSESS->HOSTSESSION = 0; + HOSTSESS->HOSTFLAGS |= 3; // STATE CHANGE + + PostStateChange(Session); + + CloseSessionPartner(Session); // SEND CLOSE TO PARTNER (IF PRESENT) + } + + // Check Trace Q + + if (HOSTSESS->HOSTAPPLFLAGS & 0x80) + { + if (HOSTSESS->HOSTTRACEQ) + { + int Count = C_Q_COUNT(&HOSTSESS->HOSTTRACEQ); + + if (Count > 100) + ReleaseBuffer((void *)Q_REM((void *)&HOSTSESS->HOSTTRACEQ)); + } + } + HOSTSESS++; + } +} + +VOID SendSmartID(struct PORTCONTROL * PORT) +{ + struct _MESSAGE * ID = IDMSG; + struct _MESSAGE * Buffer; + + PORT->SmartIDNeeded = 0; + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer, ID, ID->LENGTH); + + Buffer->PORT = PORT->PORTNUMBER; + + // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD + + if (PORT->PORTCALL[0] > 0x40) + { + memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); + Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT + } + + // If Pactor Style add to UI_Q + + if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF && PORT->UICAPABLE) + { + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + C_Q_ADD(&EXTPORT->UI_Q, Buffer); + return; + } + + PUT_ON_PORT_Q(PORT, Buffer); + } +} + + +VOID SENDIDMSG() +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * ID = IDMSG; + struct _MESSAGE * Buffer; + + while (PORT) + { + if (PORT->PROTOCOL < 10) // Not Pactor-style + { + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer, ID, ID->LENGTH); + + Buffer->PORT = PORT->PORTNUMBER; + + // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD + + if (PORT->PORTCALL[0] > 0x40) + { + memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); + Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT + } + C_Q_ADD(&IDMSG_Q, Buffer); + } + } + PORT = PORT->PORTPOINTER; + } +} + + + +VOID SENDBTMSG() +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * Buffer; + char * ptr1, * ptr2; + + while (PORT) + { + if (PORT->PROTOCOL >= 10 || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? + { + PORT = PORT->PORTPOINTER; + continue; + } + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); + Buffer->DEST[6] |= 0xC0; // Set Command bits + + // Send from BBSCALL unless PORTBCALL defined + + if (PORT->PORTBCALL[0] > 32) + memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); + else if (APPLCALLTABLE->APPLCALL[0] > 32) + memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); + else + memcpy(Buffer->ORIGIN, MYCALL, 7); + + ptr1 = &PORT->PORTUNPROTO[7]; // First Digi + ptr2 = &Buffer->CTL; // Digi field in buffer + + // Copy any digis + + while (*(ptr1)) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // Set End of Address + *(ptr2++) = UI; + + memcpy(ptr2, &BTHDDR.PID, BTHDDR.LENGTH); + ptr2 += BTHDDR.LENGTH; + Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); + Buffer->PORT = PORT->PORTNUMBER; + + C_Q_ADD(&IDMSG_Q, Buffer); + } + PORT = PORT->PORTPOINTER; + } +} + +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg) +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * Buffer; + char * ptr1, * ptr2; + + Msg->LENGTH -= MSGHDDRLEN; // Remove Header + + while (PORT) + { + if ((PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? + { + PORT = PORT->PORTPOINTER; + continue; + } + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); + Buffer->DEST[6] |= 0xC0; // Set Command bits + + // Send from BBSCALL unless PORTBCALL defined + + if (PORT->PORTBCALL[0] > 32) + memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); + else if (APPLCALLTABLE->APPLCALL[0] > 32) + memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); + else + memcpy(Buffer->ORIGIN, MYCALL, 7); + + ptr1 = &PORT->PORTUNPROTO[7]; // First Digi + ptr2 = &Buffer->CTL; // Digi field in buffer + + // Copy any digis + + while (*(ptr1)) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // Set End of Address + *(ptr2++) = UI; + + memcpy(ptr2, &Msg->PID, Msg->LENGTH); + ptr2 += Msg->LENGTH; + Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT->PROTOCOL == 10) + { + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + C_Q_ADD(&EXTPORT->UI_Q, Buffer); + } + else + C_Q_ADD(&IDMSG_Q, Buffer); + } + PORT = PORT->PORTPOINTER; + } +} + +Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port) +{ + // Block included the 7/11 byte header, Len does not + + struct PORTCONTROL * PORT; + PMESSAGE Copy; + + if (Len > 360 - 15) + return; + + if (QCOUNT < 50) + return; // Running low + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == 0) + return; + + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(&Copy->DEST[0], &Block[MSGHDDRLEN], Len); + + Copy->LENGTH = (USHORT)Len + MSGHDDRLEN; + + if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF) + { + // Pactor Style. Probably will only be used for Tracker uneless we do APRS over V4 or WINMOR + + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + if (EXTPORT->UI_Q) + C_Q_ADD(&EXTPORT->UI_Q, Copy); + else + C_Q_ADD(&EXTPORT->UI_Q, Copy); + return; + } + + Copy->PORT = Port; + + PUT_ON_PORT_Q(PORT, Copy); +} + + +TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask) +{ + // Create a Transport (L4) session linked to an incoming HOST (API) Session + + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + UCHAR * ourcall = &MYCALL[0]; + + // IF APPL PORT USE APPL CALL, ELSE NODE CALL + + if (ApplMask) + { + // Circuit for APPL - look for an APPLCALL + + APPLCALLS * APPL = APPLCALLTABLE; + + while ((ApplMask & 1) == 0) + { + ApplMask >>= 1; + APPL++; + } + if (APPL->APPLCALL[0] > 0x40) // We have an applcall + ourcall = &APPL->APPLCALL[0]; + } + + memcpy(NewSess->L4USER, ourcall, 7); + memcpy(NewSess->L4MYCALL, ourcall, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // Keep Non-Zero + + NewSess->L4TARGET.HOST = HOST; + NewSess->L4STATE = 5; + + + NewSess->SESSIONT1 = L4T1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + NewSess->SESSPACLEN = PACLEN; // Default; + + return NewSess; + } + Index++; + NewSess++; + } + + // Table Full + + return NULL; +} + + + + diff --git a/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base b/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base index 83b3f07..2a3d766 100644 --- a/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base +++ b/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base @@ -1,3137 +1,3137 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// DLL to provide interface to allow G8BPQ switch to use WINMOR as a Port Driver -// -// Uses BPQ EXTERNAL interface -// - - -// Version 1.0 January 2009 - Initial Version -// - -// March 22 2010 - -// Send FAULTS to Monitor Window -// Force PROTOCOL = WINMOR/PACTOR (to simplifiy Config) - -// July 2010 -// Support up to 32 BPQ Ports -// Support up to 32 Applications - -// Version 1.2.1.2 August 2010 - -// Save Minimized State -// Handle new "BLOCKED by Busy channel" message from TNC - -// Version 1.2.1.4 August 2010 - -// Add Scan control of BW setting -// Reset TNC if stuck in Disconnecting -// Add option to send reports to WL2K -// Disconnect if appl not available - -// Version 1.2.1.5 August 2010 - -// Updates to WL2K Reporting -// Send Watchdog polls every minute and restart if no response. -// Don't connect if channel is busy (unless specifically overridden) - -// Version 1.2.1.6 September 2010 - -// Add option to kill and restart TNC after each transfer -// Fix PTT operation after Node reconfig - -// Version 1.2.2.1 September 2010 - -// Add option to get config from bpq32.cfg -// Merge with BPQ32.dll - - -#define _CRT_SECURE_NO_DEPRECATE - -#include -#include - -#include "cheaders.h" - -#ifdef WIN32 -#include -#endif - -extern int (WINAPI FAR *GetModuleFileNameExPtr)(); -extern int (WINAPI FAR *EnumProcessesPtr)(); - - -#define SD_RECEIVE 0x00 -#define SD_SEND 0x01 -#define SD_BOTH 0x02 - -#include "bpq32.h" - -#include "tncinfo.h" - - -#define WSA_ACCEPT WM_USER + 1 -#define WSA_DATA WM_USER + 2 -#define WSA_CONNECT WM_USER + 3 - -static int Socket_Data(int sock, int error, int eventcode); - -int KillTNC(struct TNCINFO * TNC); -int RestartTNC(struct TNCINFO * TNC); -int KillPopups(struct TNCINFO * TNC); -VOID MoveWindows(struct TNCINFO * TNC); -int SendReporttoWL2K(struct TNCINFO * TNC); -char * CheckAppl(struct TNCINFO * TNC, char * Appl); -int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); -BOOL KillOldTNC(char * Path); -int standardParams(struct TNCINFO * TNC, char * buf); - -static char ClassName[]="WINMORSTATUS"; -static char WindowTitle[] = "WINMOR"; -static int RigControlRow = 165; - -#define WINMOR -#define NARROWMODE 21 -#define WIDEMODE 22 - -#ifndef LINBPQ -#include -#endif - -extern int SemHeldByAPI; - -static RECT Rect; - -static int ProcessLine(char * buf, int Port); - -// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 - -// There seem to be timing issues when calling SendMessage from multiple threads. -// Queue and process in main thread - -UINT * WINMORTraceQ; -UINT * SetWindowTextQ; - -VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) -{ - int index = 0; - UCHAR * ptr1 = Msg, * ptr2; - UCHAR Line[1000]; - int LineLen, i; - UCHAR Save; - int SaveLen = Len; - char Time[16]; - time_t T; - struct tm * tm; - - if (Len < 0) - return; - - Save = Msg[Len]; - Msg[Len] = 0; - -#ifndef LINBPQ - index=SendMessage(TNC->hMonitor, LB_SETCURSEL, -1, 0); -#endif - -lineloop: - - if (Len > 0) - { - // copy text to control a line at a time - - ptr2 = memchr(ptr1, 13, Len); - - if (ptr2) - { - ptr2++; - LineLen = (int)(ptr2 - ptr1); - Len -= LineLen; - memcpy(Line, ptr1, LineLen); - memcpy(&Line[LineLen - 1], "", 4); - LineLen += 3; - - if ((*ptr2) == 10) - { - memcpy(&Line[LineLen], "", 4); - LineLen += 4; - ptr2++; - Len --; - } - - Line[LineLen] = 0; - - // If line contains any data above 7f, assume binary and dont display - - for (i = 0; i < LineLen; i++) - { - if (Line[i] > 126 || Line[i] < 32) - goto Skip; - } - - // We now also pass to Monitor Window - - if (strlen(Line) < 250) - { - MESSAGE Monframe; - memset(&Monframe, 0, sizeof(Monframe)); - - Monframe.PORT = TNC->Port; - Monframe.LENGTH = 12 + strlen(Line); - Monframe.DEST[0] = 1; // Plain Text Monitor - strcpy(&Monframe.DEST[1], Line); - - time(&Monframe.Timestamp); - BPQTRACE((MESSAGE *)&Monframe, FALSE); - } - -#ifdef LINBPQ -#else - index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Line); -#endif - // Write to Web Buffer - - T = time(NULL); - tm = gmtime(&T); - - sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); - - strcat(TNC->WebBuffer, Time); - strcat(TNC->WebBuffer, Line); - strcat(TNC->WebBuffer, "\r\n"); - if (strlen(TNC->WebBuffer) > 4500) - memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved - Skip: - ptr1 = ptr2; - - goto lineloop; - - } - - // Process incomplete line - - for (i = 0; i < Len; i++) - { - if (ptr1[i] > 126 || ptr1[i] < 32) - break; - } - - if (i == Len) - { - if (Len < 250) - { - MESSAGE Monframe; - memset(&Monframe, 0, sizeof(Monframe)); - - Monframe.PORT = TNC->Port; - Monframe.LENGTH = 12 + Len; - Monframe.DEST[0] = 1; // Plain Text Monitor - - memcpy(&Monframe.DEST[1], ptr1, Len); - Monframe.DEST[1 + Len] = 0; - - time(&Monframe.Timestamp); - BPQTRACE((MESSAGE *)&Monframe, FALSE); - } - - -#ifdef LINBPQ -#else - index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); -#endif - T = time(NULL); - tm = gmtime(&T); - - sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); - strcat(TNC->WebBuffer, Time); - - strcat(TNC->WebBuffer, ptr1); - strcat(TNC->WebBuffer, "\r\n"); - if (strlen(TNC->WebBuffer) > 4500) - memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved - } - } - -#ifdef LINBPQ -#else - - if (index > 1200) - do - index=index=SendMessage(TNC->hMonitor, LB_DELETESTRING, 0, 0); - while (index > 1000); - - if (index > -1) - index=SendMessage(TNC->hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); -#endif - Msg[SaveLen] = Save; - -} - -VOID MySetWindowTextWithSem(HWND hWnd, char * Msg) -{ -#ifndef LINBPQ - - PMSGWITHLEN buffptr; - - buffptr = GetBuff(); - - if (buffptr) - { - buffptr->Len= (UINT)hWnd; - memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); - - C_Q_ADD(&SetWindowTextQ, buffptr); - } - -#endif -} - -int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); - -struct SEM SetWindTextSem = {0, 0, 0, 0}; - -VOID MySetWindowText(HWND hWnd, char * Msg) -{ -#ifndef LINBPQ - - PMSGWITHLEN buffptr; - - GetSemaphore(&SetWindTextSem, 61); - buffptr = zalloc(400); - - if (buffptr) - { - buffptr->Len= (UINT)hWnd; - memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); - - C_Q_ADD_NP(&SetWindowTextQ, buffptr); - } - - FreeSemaphore(&SetWindTextSem); -#endif -} - -VOID SetWindowTextSupport() -{ - PMSGWITHLEN Buffer; - - while (SetWindowTextQ) - { - GetSemaphore(&SetWindTextSem, 61); - Buffer = Q_REM_NP(&SetWindowTextQ); - SetWindowText((HWND)Buffer->Len, Buffer->Data); - FreeSemaphore(&SetWindTextSem); - free(Buffer); - } -} - - -VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len) -{ - // It seems writing from multiple threads can cause problems in Windows - // Queue and process in main thread - -#ifdef LINBPQ - WritetoTraceSupport(TNC, Msg, Len); -} -#else - UINT * buffptr; - BOOL Sem = FALSE; - - if (Len < 0) - return; - - // Get semaphore if it isn't set - - if (InterlockedExchange(&Semaphore.Flag, 1) == 0) - { - Sem = TRUE; - Semaphore.Gets++; - } - - buffptr = GetBuff(); - - if (buffptr) - { - if (Len > 340) - Len = 340; - - buffptr[1] = (UINT)TNC; - buffptr[2] = (UINT)Len; - memcpy(&buffptr[3], Msg, Len + 1); - - C_Q_ADD(&WINMORTraceQ, buffptr); - } - - if (Sem) - FreeSemaphore(&Semaphore); - -} -#endif - -static int ProcessLine(char * buf, int Port) -{ - UCHAR * ptr,* p_cmd; - char * p_ipad = 0; - char * p_port = 0; - unsigned short WINMORport = 0; - int BPQport; - int len=510; - struct TNCINFO * TNC; - char errbuf[256]; - - strcpy(errbuf, buf); - - ptr = strtok(buf, " \t\n\r"); - - if(ptr == NULL) return (TRUE); - - if(*ptr =='#') return (TRUE); // comment - - if(*ptr ==';') return (TRUE); // comment - - if (_stricmp(buf, "ADDR")) - return FALSE; // Must start with ADDR - - ptr = strtok(NULL, " \t\n\r"); - - BPQport = Port; - p_ipad = ptr; - - TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); - memset(TNC, 0, sizeof(struct TNCINFO)); - - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - - if (p_ipad == NULL) - p_ipad = strtok(NULL, " \t\n\r"); - - if (p_ipad == NULL) return (FALSE); - - p_port = strtok(NULL, " \t\n\r"); - - if (p_port == NULL) return (FALSE); - - WINMORport = atoi(p_port); - - TNC->destaddr.sin_family = AF_INET; - TNC->destaddr.sin_port = htons(WINMORport); - TNC->Datadestaddr.sin_family = AF_INET; - TNC->Datadestaddr.sin_port = htons(WINMORport+1); - - TNC->HostName = malloc(strlen(p_ipad)+1); - - if (TNC->HostName == NULL) return TRUE; - - strcpy(TNC->HostName,p_ipad); - - ptr = strtok(NULL, " \t\n\r"); - - if (ptr) - { - if (_stricmp(ptr, "PTT") == 0) - { - ptr = strtok(NULL, " \t\n\r"); - - if (ptr) - { - DecodePTTString(TNC, ptr); - ptr = strtok(NULL, " \t\n\r"); - } - } - } - - if (ptr) - { - if (_memicmp(ptr, "PATH", 4) == 0) - { - p_cmd = strtok(NULL, "\n\r"); - if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); - } - } - - // Read Initialisation lines - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; - - strcpy(errbuf, buf); - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - ptr = strchr(buf, ';'); - if (ptr) - { - *ptr++ = 13; - *ptr = 0; - } - - if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) - {} // Ignore - else -/* - if (_memicmp(buf, "PATH", 4) == 0) - { - char * Context; - p_cmd = strtok_s(&buf[5], "\n\r", &Context); - if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); - } - else -*/ - if (_memicmp(buf, "STARTINROBUST", 13) == 0) - TNC->StartInRobust = TRUE; - - else - if (_memicmp(buf, "ROBUST", 6) == 0) - { - if (_memicmp(&buf[7], "TRUE", 4) == 0) - TNC->Robust = TRUE; - - strcat (TNC->InitScript, buf); - } - else if (standardParams(TNC, buf) == FALSE) - strcat (TNC->InitScript, buf); - } - - - return (TRUE); -} - - - -void WINMORThread(void * portptr); -VOID ProcessDataSocketData(int port); -int ConnecttoWINMOR(int port); -static int ProcessReceivedData(struct TNCINFO * TNC); -int V4ProcessReceivedData(struct TNCINFO * TNC); -VOID ReleaseTNC(struct TNCINFO * TNC); -VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); -VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); -VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); - -static time_t ltime; - - -static SOCKADDR_IN sinx; -static SOCKADDR_IN rxaddr; - -static int addrlen=sizeof(sinx); - - - -VOID ChangeMYC(struct TNCINFO * TNC, char * Call) -{ - UCHAR TXMsg[100]; - int datalen; - - if (strcmp(Call, TNC->CurrentMYC) == 0) - return; // No Change - - strcpy(TNC->CurrentMYC, Call); - -// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); - - datalen = sprintf(TXMsg, "MYC %s\r\n", Call); - send(TNC->TCPSock,TXMsg, datalen, 0); - -// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); -// TNC->StartSent = TRUE; - - send(TNC->TCPSock, "MYC\r\n", 5, 0); -} - -static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) -{ - int i,winerr; - size_t datalen; - PMSGWITHLEN buffptr; - char txbuff[500]; - unsigned int bytes; - size_t txlen = 0; - char ErrMsg[255]; - size_t Param; - HKEY hKey=0; - struct TNCINFO * TNC = TNCInfo[port]; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - struct ScanEntry * Scan; - fd_set readfs; - fd_set writefs; - fd_set errorfs; - struct timeval timeout; - - if (TNC == NULL) - return 0; // Port not defined - - switch (fn) - { - case 1: // poll - - // Check session limit timer - - if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) - { - if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) - { - send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); - STREAM->Disconnecting = TRUE; - } - } - - while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q - { - buffptr = Q_REM(&TNC->PortRecord->UI_Q); - ReleaseBuffer(buffptr); - } - - - if (TNC->Busy) // Count down to clear - { - if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy - { - TNC->Busy--; - if (TNC->Busy == 0) - SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); - strcpy(TNC->WEB_CHANSTATE, "Clear"); - } - } - - if (TNC->ConnectCmd && TNC->BusyDelay) - { - // Still Busy? - - if (InterlockedCheckBusy(TNC) == FALSE) - { - // No, so send - - send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); - TNC->Streams[0].Connecting = TRUE; - - memset(TNC->Streams[0].RemoteCall, 0, 10); - memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - free(TNC->ConnectCmd); - TNC->ConnectCmd = 0; - - TNC->BusyDelay = 0; - } - else - { - // Wait Longer - - TNC->BusyDelay--; - - if (TNC->BusyDelay == 0) - { - // Timed out - Send Error Response - - PMSGWITHLEN buffptr = GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - buffptr->Len = 39; - memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); - - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - free(TNC->ConnectCmd); - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - } - } - } - - if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected - { - if (TNC->HeartBeat > 600 && TNC->hWnd) - { - char wtext[100]; - sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); - MySetWindowText(TNC->hWnd, wtext); - } - - TNC->HeartBeat = 0; - - if (TNC->CONNECTED) - { - // Probe link - - if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) - send(TNC->TCPSock, "MODE\r\n", 6, 0); - else - { - if (time(NULL) - TNC->WinmorRestartCodecTimer > 900) // 15 mins - { - send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); - send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); - } - else - send(TNC->TCPSock, "STATE\r\n", 7, 0); - } - } - } - - if (TNC->FECMode) - { - if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins - { - if (!TNC->Busy) - { - TNC->FECIDTimer = 0; - send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); - } - } - if (TNC->FECPending) // Check if FEC Send needed - { - if (!TNC->Busy) - { - TNC->FECPending = 0; - - if (TNC->FEC1600) - send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); - else - send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); - } - } - } - - if (STREAM->NeedDisc) - { - STREAM->NeedDisc--; - - if (STREAM->NeedDisc == 0) - { - // Send the DISCONNECT - - send(TNC->TCPSock, "DISCONNECT\r\n", 12, 0); - } - } - - if (TNC->DiscPending) - { - TNC->DiscPending--; - - if (TNC->DiscPending == 0) - { - // Too long in Disc Pending - Kill and Restart TNC - - if (TNC->PID) - { - KillTNC(TNC); - RestartTNC(TNC); - } - } - } - - if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive - { - // Restart TNC - - if (TNC->ProgramPath && TNC->CONNECTED) - { - if (strstr(TNC->ProgramPath, "WINMOR TNC")) - { - struct tm * tm; - char Time[80]; - - TNC->Restarts++; - TNC->LastRestart = time(NULL); - - tm = gmtime(&TNC->LastRestart); - - sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", - tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - SetWindowText(TNC->xIDC_RESTARTTIME, Time); - strcpy(TNC->WEB_RESTARTTIME, Time); - - sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); - SetWindowText(TNC->xIDC_RESTARTS, Time); - strcpy(TNC->WEB_RESTARTS, Time); - - KillTNC(TNC); - RestartTNC(TNC); - - TNC->TimeSinceLast = 0; - } - } - } - - if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) - { - // New Attach - - int calllen; - char Msg[80]; - - TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit - - TNC->Streams[0].Attached = TRUE; - - calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); - TNC->Streams[0].MyCall[calllen] = 0; - - // Stop Listening, and set MYCALL to user's call - - send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); - ChangeMYC(TNC, TNC->Streams[0].MyCall); - - // Stop other ports in same group - - SuspendOtherPorts(TNC); - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - // Stop Scanning - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Msg); - - } - - if (TNC->Streams[0].Attached) - CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); - - if (TNC->Streams[0].ReportDISC) - { - TNC->Streams[0].ReportDISC = FALSE; - buff->PORT = 0; - return -1; - } - - - - if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) - { - // See if time to reconnect - - time(<ime); - if (ltime - TNC->lasttime > 9 ) - { - ConnecttoWINMOR(port); - TNC->lasttime = ltime; - } - } - - FD_ZERO(&readfs); - - if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); - - FD_ZERO(&writefs); - - if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing - - FD_ZERO(&errorfs); - - if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); - - timeout.tv_sec = 0; - timeout.tv_usec = 0; // poll - - if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) - { - // See what happened - - if (FD_ISSET(TNC->TCPDataSock, &readfs)) - ProcessDataSocketData(port); - - if (FD_ISSET(TNC->TCPDataSock, &writefs)) - { - // Write block has cleared. Send rest of packet - - buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); - txlen = buffptr->Len; - memcpy(txbuff,buffptr->Data,txlen); - bytes=send(TNC->TCPSock, (const char FAR *)&txbuff, (int)txlen, 0); - ReleaseBuffer(buffptr); - } - - if (FD_ISSET(TNC->TCPDataSock, &errorfs)) - { - i=sprintf(ErrMsg, "WINMOR Data Connection lost for BPQ Port %d\r\n", port); - WritetoConsole(ErrMsg); - TNC->CONNECTING = FALSE; - TNC->CONNECTED = FALSE; - TNC->Streams[0].ReportDISC = TRUE; - } - } - - // See if any frames for this port - - if (TNC->WINMORtoBPQ_Q != 0) - { - buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); - - datalen = buffptr->Len; - - buff->PORT = 0; // Compatibility with Kam Driver - buff->PID = 0xf0; - memcpy(&buff->L2DATA[0], buffptr->Data, datalen); // Data goes to +7, but we have an extra byte - datalen = buffptr->Len; - - datalen += sizeof(void *) + 4; - PutLengthinBuffer(buff, (int)datalen); - - ReleaseBuffer(buffptr); - - return (1); - } - - return (0); - - case 2: // send - - if (!TNC->CONNECTED) - { - // Send Error Response - - PMSGWITHLEN buffptr = GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - buffptr->Len = 36; - memcpy(buffptr->Data, "No Connection to WINMOR Virtual TNC\r", 36); - - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - - return 0; // Don't try if not connected - } - - if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT - { - PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); - txlen = buffptr->Len; - memcpy(txbuff, buffptr->Data, txlen); - bytes = send(TNC->TCPDataSock, txbuff, (int)txlen, 0); - STREAM->bytesTXed += bytes; - WritetoTrace(TNC, txbuff, (int)txlen); - ReleaseBuffer(buffptr); - } - - if (TNC->SwallowSignon) - { - TNC->SwallowSignon = FALSE; // Discard *** connected - return 0; - } - - txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID - - if (TNC->Streams[0].Connected) - { - STREAM->PacketsSent++; - - if (STREAM->PacketsSent == 3) - { - if (TNC->Robust) - send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); - else - send(TNC->TCPSock, "ROBUST FALSE\r\n", 14, 0); - } - - bytes = send(TNC->TCPDataSock,buff->L2DATA, (int)txlen, 0); - STREAM->bytesTXed += bytes; - WritetoTrace(TNC, &buff->L2DATA[0], (int)txlen); - - } - else - { - if (_memicmp(buff->L2DATA, "D\r", 2) == 0) - { - TNC->Streams[0].ReportDISC = TRUE; // Tell Node - return 0; - } - - if (TNC->FECMode) - { - char Buffer[300]; - int len; - - // Send FEC Data - - buff->L2DATA[txlen] = 0; - len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA); - - send(TNC->TCPDataSock, Buffer, len, 0); - - if (TNC->BusyFlags) - { - TNC->FECPending = 1; - } - else - { - if (TNC->FEC1600) - send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); - else - send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); - } - return 0; - } - - - // See if Local command (eg RADIO) - - if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) - { - char cmd[56]; - - strcpy(cmd, &buff->L2DATA[6]); - sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &cmd); - - - if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) - { - } - else - { - PMSGWITHLEN buffptr = GetBuff(); - - if (buffptr == 0) return 1; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data, "%s", &buff->L2DATA[0]); - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - } - return 1; - } - - if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) - { - PMSGWITHLEN buffptr = GetBuff(); - - TNC->OverrideBusy = TRUE; - - if (buffptr) - { - buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - } - - return 0; - - } - - if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) - { - if (buff->L2DATA[9] != 13) - { - // Limit connects - - int tries = atoi(&buff->L2DATA[10]); - int len; - - if (tries > 10) tries = 10; - len = sprintf(&buff->L2DATA[0], "MAXCONREQ %d\r\nMAXCONREQ\r\n", tries); - - send(TNC->TCPSock, &buff->L2DATA[0], len, 0); - return 0; - } - } - - if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) - { - if (buff->L2DATA[16] != 13) - { - PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); - - TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; - - if (buffptr) - { - buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "Winmor} OK\r"); - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - } - return 0; - } - } - - if ((_memicmp(buff->L2DATA, "BW 500", 6) == 0) || (_memicmp(buff->L2DATA, "BW 1600", 7) == 0)) - { - // Generate a local response - - PMSGWITHLEN buffptr = GetBuff(); - - if (_memicmp(buff->L2DATA, "BW 500", 6) == 0) - TNC->WL2KMode = 21; - else - TNC->WL2KMode = 22; - - if (buffptr) - { - buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - } - TNC->WinmorCurrentMode = 0; // So scanner will set next value - } - - if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) - TNC->StartSent = TRUE; - - if (_memicmp(buff->L2DATA, "ROBUST", 6) == 0) - { - if (_memicmp(&buff->L2DATA[7], "TRUE", 4) == 0) - TNC->Robust = TRUE; - else - TNC->Robust = FALSE; - } - - if (_memicmp(buff->L2DATA, "D\r", 2) == 0) - { - TNC->Streams[0].ReportDISC = TRUE; // Tell Node - return 0; - } - - if (_memicmp(buff->L2DATA, "FEC\r", 4) == 0 || _memicmp(buff->L2DATA, "FEC ", 4) == 0) - { - TNC->FECMode = TRUE; - TNC->FECIDTimer = 0; - send(TNC->TCPSock,"FECRCV TRUE\r\nFECRCV\r\n", 21, 0); - - if (_memicmp(buff->L2DATA, "FEC 1600", 8) == 0) - TNC->FEC1600 = TRUE; - else - TNC->FEC1600 = FALSE; - - return 0; - } - - // See if a Connect Command. If so, start codec and set Connecting - - if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect - { - char Connect[80] = "CONNECT "; - - memcpy(&Connect[8], &buff->L2DATA[2], txlen); - txlen += 6; - Connect[txlen++] = 0x0a; - Connect[txlen] = 0; - - _strupr(Connect); - - ChangeMYC(TNC, TNC->Streams[0].MyCall); - - // See if Busy - - if (InterlockedCheckBusy(TNC)) - { - // Channel Busy. Unless override set, wait - - if (TNC->OverrideBusy == 0) - { - // Save Command, and wait up to 10 secs - - sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - TNC->ConnectCmd = _strdup(Connect); - TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs - return 0; - } - } - - TNC->OverrideBusy = FALSE; - - bytes = send(TNC->TCPSock, Connect, (int)txlen, 0); - TNC->Streams[0].Connecting = TRUE; - - memset(TNC->Streams[0].RemoteCall, 0, 10); - memcpy(TNC->Streams[0].RemoteCall, &Connect[8], txlen-10); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - } - else - { - buff->L2DATA[txlen++] = 0x0a; - bytes = send(TNC->TCPSock, &buff->L2DATA[0], (int)txlen, 0); - } - } - if (bytes != txlen) - { - - // WINMOR doesn't seem to recover from a blocked write. For now just reset - - winerr = WSAGetLastError(); - sprintf(ErrMsg, "WINMOR Write Failed for port %d - error code = %d\r\n", port, winerr); - WritetoConsole(ErrMsg); - closesocket(TNC->TCPSock); - TNC->CONNECTED = FALSE; - - return (0); - } - - return (0); - - case 3: - - // CHECK IF OK TO SEND (And check TNC Status) - - if (TNC->Streams[0].Attached == 0) - return TNC->CONNECTED << 8 | 1; - - return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK - - break; - - case 4: // reinit - - return (0); - - case 5: // Close - - send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); - Sleep(100); - shutdown(TNC->TCPDataSock, SD_BOTH); - shutdown(TNC->TCPSock, SD_BOTH); - Sleep(100); - - closesocket(TNC->TCPDataSock); - closesocket(TNC->TCPSock); - - if (TNC->PID && TNC->WeStartedTNC) - { - KillTNC(TNC); - } - - return (0); - - case 6: // Scan Stop Interface - - Param = (size_t)buff; - - if (Param == 2) // Check Permission (shouldn't happen) - { - Debugprintf("Scan Check Permission called on FLDIGI"); - return 1; // OK to change - } - - if (!TNC->TCPSock) - return 0; // No connection so no interlock - - if (Param == 1) // Request Permission - { - if (TNC->ConnectPending) - return TRUE; // Not OK to Change - - if (TNC->CONNECTED) - { - TNC->GavePermission = TRUE; - send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); - } - return FALSE; - } - - if (Param == 3) // Release Permission - { - if (TNC->CONNECTED) - { - if (TNC->GavePermission) - { - TNC->GavePermission = FALSE; - send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); - } - } - return 0; - } - - // Param is Address of a struct ScanEntry - - Scan = (struct ScanEntry *)buff; - - - if (Scan->Bandwidth == 'W') // Set Wide Mode - { - if (TNC->WinmorCurrentMode != 1600) - { - if (TNC->WinmorCurrentMode == 0) - if (TNC->CONNECTED) - send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); - - if (TNC->CONNECTED) - send(TNC->TCPSock, "BW 1600\r\n", 9, 0); - TNC->WinmorCurrentMode = 1600; - } - TNC->WL2KMode = 22; - return 0; - } - - - if (Scan->Bandwidth == 'N') // Set Narrow Mode - { - if (TNC->WinmorCurrentMode != 500) - { - if (TNC->WinmorCurrentMode == 0) - if (TNC->CONNECTED) - send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); - - TNC->WinmorCurrentMode = 500; - if (TNC->CONNECTED) - send(TNC->TCPSock, "BW 500\r\n", 8, 0); - } - TNC->WL2KMode = 21; - return 0; - } - - if (Scan->Bandwidth == 'X') // Dont Allow Connects - { - if (TNC->WinmorCurrentMode != 0) - { - if (TNC->CONNECTED) - send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); - TNC->WinmorCurrentMode = 0; - } - - TNC->WL2KMode = 0; - return 0; - } - - return 0; - } - return 0; -} - -VOID ReleaseTNC(struct TNCINFO * TNC) -{ - // Set mycall back to Node or Port Call, and Start Scanner - - UCHAR TXMsg[256]; - char wtext[100]; - - ChangeMYC(TNC, TNC->NodeCall); - - if (TNC->CONNECTED) - send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); - - strcpy(TNC->WEB_TNCSTATE, "Free"); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - if (TNC->hWnd) - { - sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); - MySetWindowText(TNC->hWnd, wtext); - } - - // Start Scanner - - sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); - - ReleaseOtherPorts(TNC); - -} - -VOID SuspendOtherPorts(struct TNCINFO * ThisTNC) -{ - // Disable other TNCs in same Interlock Group - - struct TNCINFO * TNC; - int i; - int rxInterlock = ThisTNC->RXRadio; - int txInterlock = ThisTNC->TXRadio; - - if (rxInterlock == 0 || txInterlock == 0) - return; - - for (i = 1; i <= MAXBPQPORTS; i++) - { - TNC = TNCInfo[i]; - if (TNC == NULL) - continue; - - if (TNC == ThisTNC) - continue; - - if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group - if (TNC->SuspendPortProc) - TNC->SuspendPortProc(TNC, ThisTNC); - } -} - -VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC) -{ - // Enable other TNCs in same Interlock Group - - struct TNCINFO * TNC; - int i; - int rxInterlock = ThisTNC->RXRadio; - int txInterlock = ThisTNC->TXRadio; - - if (rxInterlock == 0 && txInterlock == 0) - return; - - for (i=1; i <= MAXBPQPORTS; i++) - { - TNC = TNCInfo[i]; - if (TNC == NULL) - continue; - - if (TNC == ThisTNC) - continue; - - if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group - if (TNC->ReleasePortProc) - TNC->ReleasePortProc(TNC); - } -} - -VOID WinmorSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) -{ - if (TNC->CONNECTED) - send(TNC->TCPSock, "CODEC FALSE\r\n", 14, 0); - - if (TNC->Busy) - { - TNC->Busy = FALSE; // Can't clear detector if CODEC off. - MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); - strcpy(TNC->WEB_CHANSTATE, "Clear"); - } -} - -VOID WinmorReleasePort(struct TNCINFO * TNC) -{ - if (TNC->CONNECTED) - send(TNC->TCPSock, "CODEC TRUE\r\n", 13, 0); -} - -extern char WebProcTemplate[]; -extern char sliderBit[]; - -static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) -{ - int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "WINMOR Status", "WINMOR Status"); - - if (TNC->TXFreq) - Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); - - - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); - Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); -// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); - Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); - - Len += sprintf(&Buff[Len], "", TNC->WebBuffer); - Len = DoScanLine(TNC, Buff, Len); - - return Len; -} - - -void * WinmorExtInit(EXTPORTDATA * PortEntry) -{ - int i, port; - char Msg[255]; - char * ptr; - APPLCALLS * APPL; - struct TNCINFO * TNC; - char Aux[100] = "MYAUX "; - char Appl[11]; - char * TempScript; - - // - // Will be called once for each WINMOR port - // - // The Socket to connect to is in IOBASE - // - - port = PortEntry->PORTCONTROL.PORTNUMBER; - - ReadConfigFile(port, ProcessLine); - - TNC = TNCInfo[port]; - - if (TNC == NULL) - { - // Not defined in Config file - - sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); - WritetoConsole(Msg); - - return ExtProc; - } - - TNC->Port = port; - TNC->PortRecord = PortEntry; - - if (TNC->ProgramPath) - TNC->WeStartedTNC = RestartTNC(TNC); - - TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_WINMOR; - - if (TNC->BusyWait == 0) - TNC->BusyWait = 10; - - if (TNC->BusyHold == 0) - TNC->BusyHold = 1; - - - if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) - memcpy(TNC->NodeCall, MYNODECALL, 10); - else - ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); - - if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) - TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; - - PortEntry->PORTCONTROL.PROTOCOL = 10; - PortEntry->PORTCONTROL.PORTQUALITY = 0; - PortEntry->MAXHOSTMODESESSIONS = 1; - PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only - - if (PortEntry->PORTCONTROL.PORTPACLEN == 0) - PortEntry->PORTCONTROL.PORTPACLEN = 236; - - TNC->SuspendPortProc = WinmorSuspendPort; - TNC->ReleasePortProc = WinmorReleasePort; - - TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset - - ptr=strchr(TNC->NodeCall, ' '); - if (ptr) *(ptr) = 0; // Null Terminate - - // Set Essential Params and MYCALL - - // Put overridable ones on front, essential ones on end - - TempScript = malloc(1000); - - strcpy(TempScript, "DebugLog True\r\n"); - strcat(TempScript, "CWID False\r\n"); - strcat(TempScript, "BW 1600\r\n"); - strcat(TempScript, "ROBUST False\r\n"); - strcat(TempScript, "MODE AUTO\r\n"); - - strcat(TempScript, TNC->InitScript); - - free(TNC->InitScript); - TNC->InitScript = TempScript; - - TNC->WL2KMode = 22; // in case not scanning - - // Set MYCALL - - strcat(TNC->InitScript,"FECRCV True\r\n"); - strcat(TNC->InitScript,"AUTOBREAK True\r\n"); - - sprintf(Msg, "MYC %s\r\nCODEC TRUE\r\nLISTEN TRUE\r\nMYC\r\n", TNC->NodeCall); - strcat(TNC->InitScript, Msg); - strcat(TNC->InitScript,"PROCESSID\r\n"); - - for (i = 0; i < 32; i++) - { - APPL=&APPLCALLTABLE[i]; - - if (APPL->APPLCALL_TEXT[0] > ' ') - { - char * ptr; - memcpy(Appl, APPL->APPLCALL_TEXT, 10); - ptr=strchr(Appl, ' '); - - if (ptr) - { - *ptr++ = ','; - *ptr = 0; - } - - strcat(Aux, Appl); - } - } - strcat(TNC->InitScript, Aux); - strcat(TNC->InitScript,"\r\nMYAUX\r\n"); - - strcpy(TNC->CurrentMYC, TNC->NodeCall); - - if (TNC->WL2K == NULL) - if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded - TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; - - if (TNC->destaddr.sin_family == 0) - { - // not defined in config file, so use localhost and port from IOBASE - - TNC->destaddr.sin_family = AF_INET; - TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); - TNC->Datadestaddr.sin_family = AF_INET; - TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); - - TNC->HostName=malloc(10); - - if (TNC->HostName != NULL) - strcpy(TNC->HostName,"127.0.0.1"); - - } - - PortEntry->PORTCONTROL.TNC = TNC; - - TNC->WebWindowProc = WebProc; - TNC->WebWinX = 520; - TNC->WebWinY = 500; - TNC->WebBuffer = zalloc(5000); - - TNC->WEB_COMMSSTATE = zalloc(100); - TNC->WEB_TNCSTATE = zalloc(100); - TNC->WEB_CHANSTATE = zalloc(100); - TNC->WEB_BUFFERS = zalloc(100); - TNC->WEB_PROTOSTATE = zalloc(100); - TNC->WEB_RESTARTTIME = zalloc(100); - TNC->WEB_RESTARTS = zalloc(100); - - TNC->WEB_MODE = zalloc(20); - TNC->WEB_TRAFFIC = zalloc(100); - - -#ifndef LINBPQ - - CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); - - CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); - CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); - - TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | - LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, - 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); - - TNC->ClientHeight = 450; - TNC->ClientWidth = 500; - - TNC->hMenu = CreatePopupMenu(); - - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Winmor TNC"); - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Winmor TNC"); - AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); - AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); - - CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); - - MoveWindows(TNC); -#endif - Consoleprintf("WINMOR Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); - - ConnecttoWINMOR(port); - - time(&TNC->lasttime); // Get initial time value - - return ExtProc; -} - -int ConnecttoWINMOR(int port) -{ - _beginthread(WINMORThread, 0, (void *)(size_t)port); - - return 0; -} - -VOID WINMORThread(void * portptr) -{ - // Opens both sockets and looks for data on control socket. Data socket is polled from BG, - // but we need fast response to control messages for PTT porcessing - - int port = (int)(size_t)portptr; - - char Msg[255]; - int err, i, ret; - u_long param=1; - BOOL bcopt=TRUE; - struct hostent * HostEnt; - struct TNCINFO * TNC = TNCInfo[port]; - fd_set readfs; - fd_set errorfs; - struct timeval timeout; - if (TNC->HostName == NULL) - return; - - TNC->CONNECTING = TRUE; - - Sleep(3000); // Allow init to complete - -#ifdef WIN32 - if (strcmp(TNC->HostName, "127.0.0.1") == 0) - { - // can only check if running on local host - - TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); - if (TNC->PID == 0) - { - TNC->CONNECTING = FALSE; - return; // Not listening so no point trying to connect - } - } -#endif - -// // If we started the TNC make sure it is still running. - -// if (!IsProcess(TNC->PID)) -// { -// RestartTNC(TNC); -// Sleep(3000); -// } - - - TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - - if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) - { - // Resolve name to address - - HostEnt = gethostbyname (TNC->HostName); - - if (!HostEnt) - { - TNC->CONNECTING = FALSE; - return; // Resolve failed - } - memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); - memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); - - } - - if (TNC->TCPSock) - closesocket(TNC->TCPSock); - - TNC->TCPSock = 0; - - if (TNC->TCPDataSock) - closesocket(TNC->TCPDataSock); - - TNC->TCPDataSock = 0; - - TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); - - if (TNC->TCPSock == INVALID_SOCKET) - { - i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); - WritetoConsole(Msg); - - TNC->CONNECTING = FALSE; - return; - } - - setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); - - sinx.sin_family = AF_INET; - sinx.sin_addr.s_addr = INADDR_ANY; - sinx.sin_port = 0; - - if (bind(TNC->TCPSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) - { - // - // Bind Failed - // - - i=sprintf(Msg, "Bind Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); - WritetoConsole(Msg); - - closesocket(TNC->TCPSock); - TNC->TCPSock = 0; - TNC->CONNECTING = FALSE; - - return; - } - - if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) - { - // - // Connected successful - // - } - else - { - if (TNC->Alerted == FALSE) - { - err=WSAGetLastError(); - i=sprintf(Msg, "Connect Failed for WINMOR socket - error code = %d\r\n", err); - WritetoConsole(Msg); - sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - TNC->Alerted = TRUE; - } - - closesocket(TNC->TCPSock); - TNC->CONNECTING = FALSE; - TNC->TCPSock = 0; - - return; - } - - Sleep(1000); - - TNC->LastFreq = 0; // so V4 display will be updated - - TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); - - setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); - - if (TNC->TCPDataSock == INVALID_SOCKET) - { - i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); - WritetoConsole(Msg); - - closesocket(TNC->TCPSock); - TNC->TCPSock = 0; - TNC->CONNECTING = FALSE; - - return; - } - - if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) - { - // - // Bind Failed - // - - i=sprintf(Msg, "Bind Failed for WINMOR Data socket - error code = %d\r\n", WSAGetLastError()); - WritetoConsole(Msg); - - closesocket(TNC->TCPSock); - closesocket(TNC->TCPDataSock); - TNC->TCPSock = 0; - TNC->TCPDataSock = 0; - TNC->CONNECTING = FALSE; - - return; - } - - if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) - { - ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking - TNC->CONNECTED = TRUE; - TNC->CONNECTING = FALSE; - - // Send INIT script - - send(TNC->TCPSock, TNC->InitScript , (int)strlen(TNC->InitScript), 0); - TNC->Alerted = TRUE; - - if (TNC->Hardware == H_V4) - sprintf(TNC->WEB_COMMSSTATE, "Connected to V4 TNC"); - else - sprintf(TNC->WEB_COMMSSTATE, "Connected to WINMOR TNC"); - - GetSemaphore(&Semaphore, 40); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - FreeSemaphore(&Semaphore); - - } - else - { - sprintf(Msg, "Connect Failed for WINMOR Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); - WritetoConsole(Msg); - - closesocket(TNC->TCPSock); - closesocket(TNC->TCPDataSock); - TNC->TCPSock = 0; - TNC->TCPDataSock = 0; - TNC->CONNECTING = FALSE; - - return; - } - - TNC->HeartBeat = 0; - - while (TRUE) - { - FD_ZERO(&readfs); - FD_ZERO(&errorfs); - - FD_SET(TNC->TCPSock,&readfs); - FD_SET(TNC->TCPSock,&errorfs); - - timeout.tv_sec = 90; - timeout.tv_usec = 0; // We should get messages more frequently that this - - ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); - - if (ret == SOCKET_ERROR) - { - printf("Select failed %d ", WSAGetLastError()); - goto Lost; - } - if (ret > 0) - { - // See what happened - - if (FD_ISSET(TNC->TCPSock, &readfs)) - { - if (TNC->Hardware == H_V4) - V4ProcessReceivedData(TNC); - else - ProcessReceivedData(TNC); - } - - if (FD_ISSET(TNC->TCPSock, &errorfs)) - { -Lost: - sprintf(Msg, "WINMOR Connection lost for Port %d\r\n", TNC->Port); - WritetoConsole(Msg); - - sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); - GetSemaphore(&Semaphore, 40); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - FreeSemaphore(&Semaphore); - - TNC->CONNECTED = FALSE; - TNC->Alerted = FALSE; - - if (TNC->PTTMode) - Rig_PTT(TNC, FALSE); // Make sure PTT is down - - if (TNC->Streams[0].Attached) - TNC->Streams[0].ReportDISC = TRUE; - - closesocket(TNC->TCPSock); - closesocket(TNC->TCPDataSock); - - TNC->TCPSock = 0; - TNC->TCPDataSock = 0; - - return; - } - } - else - { - // 90 secs without data. Shouldn't happen - - sprintf(Msg, "WINMOR Connection Timeout Port %d\r\n", TNC->Port); - WritetoConsole(Msg); - - sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); - GetSemaphore(&Semaphore, 40); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - FreeSemaphore(&Semaphore); - - TNC->CONNECTED = FALSE; - TNC->Alerted = FALSE; - - if (TNC->PTTMode) - Rig_PTT(TNC, FALSE); // Make sure PTT is down - - if (TNC->Streams[0].Attached) - TNC->Streams[0].ReportDISC = TRUE; - - send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); - - Sleep(100); - shutdown(TNC->TCPDataSock, SD_BOTH); - shutdown(TNC->TCPSock, SD_BOTH); - Sleep(100); - - closesocket(TNC->TCPDataSock); - closesocket(TNC->TCPSock); - TNC->TCPDataSock = 0; - TNC->TCPSock= 0; - - if (TNC->PID && TNC->WeStartedTNC) - { - KillTNC(TNC); - RestartTNC(TNC); - } - return; - } - } -} - -#ifdef WIN32 - -BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) -{ - char wtext[100]; - struct TNCINFO * TNC = (struct TNCINFO *)lParam; - UINT ProcessId; - - GetWindowText(hwnd,wtext,99); - - if (memcmp(wtext,"WINMOR Sound Card TNC", 21) == 0) - { - GetWindowThreadProcessId(hwnd, &ProcessId); - - if (TNC->PID == ProcessId) - { - // Our Process - - TNC->hWnd = hwnd; // save so we can reset title when sessicn closes - sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); - SetWindowText(hwnd, wtext); - return FALSE; - } - } - - return (TRUE); -} -#endif - -VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) -{ - // Response on WINMOR control channel. Could be a reply to a command, or - // an Async Response - - PMSGWITHLEN buffptr; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - Buffer[MsgLen - 2] = 0; - - if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) - { - Debugprintf(Buffer); - - // Force a restart - - send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); - send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); - } - else - { - TNC->TimeSinceLast = 0; - } - - - if (_memicmp(Buffer, "STATE ", 6) == 0) - { - Debugprintf(Buffer); - - if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) - { - // Force a restart - - send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); - send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); - } - return; - } - - Buffer[MsgLen - 2] = 0; // Remove CRLF - - if (_memicmp(Buffer, "PTT T", 5) == 0) - { - TNC->Busy = TNC->BusyHold * 10; // BusyHold delay - - if (TNC->PTTMode) - Rig_PTT(TNC, TRUE); - return; - } - if (_memicmp(Buffer, "PTT F", 5) == 0) - { - if (TNC->PTTMode) - Rig_PTT(TNC, FALSE); - return; - } - - if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) - { - TNC->BusyFlags |= CDBusy; - TNC->Busy = TNC->BusyHold * 10; // BusyHold delay - - SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); - strcpy(TNC->WEB_CHANSTATE, "Busy"); - - TNC->WinmorRestartCodecTimer = time(NULL); - return; - } - - if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) - { - TNC->BusyFlags &= ~CDBusy; - if (TNC->BusyHold) - strcpy(TNC->WEB_CHANSTATE, "BusyHold"); - else - strcpy(TNC->WEB_CHANSTATE, "Clear"); - - SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); - TNC->WinmorRestartCodecTimer = time(NULL); - return; - } - - if (_memicmp(Buffer, "TARGET", 6) == 0) - { - Debugprintf(Buffer); - GetSemaphore(&Semaphore, 50); - WritetoTrace(TNC, Buffer, MsgLen - 2); - FreeSemaphore(&Semaphore); - memcpy(TNC->TargetCall, &Buffer[7], 10); - return; - } - - if (_memicmp(Buffer, "OFFSET", 6) == 0) - { -// WritetoTrace(TNC, Buffer, MsgLen - 2); -// memcpy(TNC->TargetCall, &Buffer[7], 10); - return; - } - - if (_memicmp(Buffer, "CONNECTED", 9) == 0) - { - char Call[11]; - char * ptr; - APPLCALLS * APPL; - char * ApplPtr = APPLS; - int App; - char Appl[10]; - struct WL2KInfo * WL2K = TNC->WL2K; - - Debugprintf(Buffer); - - GetSemaphore(&Semaphore, 50); - WritetoTrace(TNC, Buffer, MsgLen - 2); - FreeSemaphore(&Semaphore); - - STREAM->ConnectTime = time(NULL); - STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; - - if (TNC->StartInRobust) - send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); - - memcpy(Call, &Buffer[10], 10); - - ptr = strchr(Call, ' '); - if (ptr) *ptr = 0; - - TNC->HadConnect = TRUE; - - if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) - { - TRANSPORTENTRY * SESS; - - // Incomming Connect - - TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit - - // Stop other ports in same group - - SuspendOtherPorts(TNC); - - GetSemaphore(&Semaphore, 50); - - ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); - FreeSemaphore(&Semaphore); - - SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; - - SESS->Mode = TNC->WL2KMode; - - if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) - { - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); - SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq - SESS->Mode = TNC->WL2KMode; - } - else - { - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); - if (WL2K) - { - SESS->Frequency = WL2K->Freq; - SESS->Mode = WL2K->mode; - } - } - - if (WL2K) - strcpy(SESS->RMSCall, WL2K->RMSCall); - - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - // Check for ExcludeList - - if (ExcludeList[0]) - { - if (CheckExcludeList(SESS->L4USER) == FALSE) - { - char Status[64]; - - TidyClose(TNC, 0); - sprintf(Status, "%d SCANSTART 15", TNC->Port); - Rig_Command( (TRANSPORTENTRY *) -1, Status); - Debugprintf("WINMOR Call from %s rejected", Call); - return; - } - } - - // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT - - if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) - { - UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; - - while (TRUE) - { - if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID - break; - - ptr += 7; - - if ((*ptr) == 0) // Not in list - { - char Status[64]; - - TidyClose(TNC, 0); - sprintf(Status, "%d SCANSTART 15", TNC->Port); - Rig_Command( (TRANSPORTENTRY *) -1, Status); - Debugprintf("WINMOR Call from %s not in ValidCalls - rejected", Call); - return; - } - } - } - - if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT - { - PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); - - send(TNC->TCPDataSock, buffptr->Data, (int)buffptr->Len, 0); - STREAM->bytesTXed += (int)buffptr->Len; - WritetoTrace(TNC, buffptr->Data, (int)buffptr->Len); - ReleaseBuffer(buffptr); - } - - // See which application the connect is for - - for (App = 0; App < 32; App++) - { - APPL=&APPLCALLTABLE[App]; - memcpy(Appl, APPL->APPLCALL_TEXT, 10); - ptr=strchr(Appl, ' '); - - if (ptr) - *ptr = 0; - - if (_stricmp(TNC->TargetCall, Appl) == 0) - break; - } - - if (App < 32) - { - char AppName[13]; - - memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); - AppName[12] = 0; - - // Make sure app is available - - if (CheckAppl(TNC, AppName)) - { - MsgLen = sprintf(Buffer, "%s\r", AppName); - - GetSemaphore(&Semaphore, 50); - - buffptr = GetBuff(); - - if (buffptr == 0) - { - FreeSemaphore(&Semaphore); - return; // No buffers, so ignore - } - - buffptr->Len = MsgLen; - memcpy(buffptr->Data, Buffer, MsgLen); - - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - - FreeSemaphore(&Semaphore); - - TNC->SwallowSignon = TRUE; - - // Save Appl Call in case needed for - - } - else - { - char Msg[] = "Application not available\r\n"; - - // Send a Message, then a disconenct - - send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); - STREAM->NeedDisc = 100; // 10 secs - } - } - - return; - } - else - { - // Connect Complete - - char Reply[80]; - int ReplyLen; - - GetSemaphore(&Semaphore, 50); - - buffptr = GetBuff(); - - if (buffptr == 0) - { - FreeSemaphore(&Semaphore); - return; // No buffers, so ignore - } - ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); - - buffptr->Len = ReplyLen; - memcpy(buffptr->Data, Reply, ReplyLen); - - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - - FreeSemaphore(&Semaphore); - - TNC->Streams[0].Connecting = FALSE; - TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel - - - if (TNC->RIG) - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); - else - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - UpdateMH(TNC, Call, '+', 'O'); - return; - } - } - - if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) - { - Debugprintf(Buffer); - - if (TNC->FECMode) - return; - - if (TNC->StartSent) - { - TNC->StartSent = FALSE; // Disconnect reported following start codec - return; - } - - if (TNC->Streams[0].Connecting) - { - // Report Connect Failed, and drop back to command mode - - TNC->Streams[0].Connecting = FALSE; - - GetSemaphore(&Semaphore, 50); - - buffptr = GetBuff(); - - if (buffptr == 0) - { - FreeSemaphore(&Semaphore); - return; // No buffers, so ignore - } - - buffptr->Len = sprintf(buffptr->Data, "Winmor} Failure with %s\r", TNC->Streams[0].RemoteCall); - - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - - FreeSemaphore(&Semaphore); - - if (TNC->RestartAfterFailure) - { - if (TNC->PID) - { - KillTNC(TNC); - RestartTNC(TNC); - } - } - - return; - } - - - // Release Session - - if (TNC->Streams[0].Connected) - { - hookL4SessionDeleted(TNC, STREAM); - - GetSemaphore(&Semaphore, 50); - WritetoTrace(TNC, Buffer, MsgLen - 2); - FreeSemaphore(&Semaphore); - } - - - TNC->Streams[0].Connecting = FALSE; - TNC->Streams[0].Connected = FALSE; // Back to Command Mode - TNC->Streams[0].ReportDISC = TRUE; // Tell Node - - if (TNC->Streams[0].Disconnecting) // - ReleaseTNC(TNC); - - TNC->Streams[0].Disconnecting = FALSE; - - return; - } - - if (_memicmp(Buffer, "MONCALL", 7) == 0) - { - Debugprintf(Buffer); - - // Add to MHEARD - - GetSemaphore(&Semaphore, 50); - WritetoTrace(TNC, Buffer, MsgLen - 2); - FreeSemaphore(&Semaphore); - UpdateMH(TNC, &Buffer[8], '!', 0); - - if (!TNC->FECMode) - return; // If in FEC mode pass ID messages to user. - } - - if (_memicmp(Buffer, "CMD", 3) == 0) - { - return; - } - - if (_memicmp(Buffer, "BUFFERS", 7) == 0) - { - int inq, inrx, Sent, BPM; - - sscanf(&Buffer[8], "%d%d%d%d%d", &inq, &inrx, &TNC->Streams[0].BytesOutstanding, &Sent, &BPM); - - if (TNC->Streams[0].BytesOutstanding == 0) - { - // all sent - - if (TNC->Streams[0].Disconnecting) // Disconnect when all sent - { - if (STREAM->NeedDisc == 0) - STREAM->NeedDisc = 60; // 6 secs - } -// else -// if (TNC->TXRXState == 'S') -// send(TNC->TCPSock,"OVER\r\n", 6, 0); - - } - else - { - // Make sure Node Keepalive doesn't kill session. - - TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; - - if (SESS) - { - SESS->L4KILLTIMER = 0; - SESS = SESS->L4CROSSLINK; - if (SESS) - SESS->L4KILLTIMER = 0; - } - } - - SetWindowText(TNC->xIDC_TRAFFIC, &Buffer[8]); - strcpy(TNC->WEB_TRAFFIC, &Buffer[8]); - return; - } - - Debugprintf(Buffer); - - if (_memicmp(Buffer, "MODE", 4) == 0) - { - // Debugprintf("WINMOR RX: %s", Buffer); - - strcpy(TNC->WEB_MODE, &Buffer[5]); - GetSemaphore(&Semaphore, 50); - MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); - FreeSemaphore(&Semaphore); - return; - } - - if (_memicmp(Buffer, "PENDING", 6) == 0) - return; - - if (_memicmp(Buffer, "FAULT", 5) == 0) - { - WritetoTrace(TNC, Buffer, MsgLen - 2); - return; - } - - if (_memicmp(Buffer, "NEWSTATE", 8) == 0) - { - TNC->WinmorRestartCodecTimer = time(NULL); - - SetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); - strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); - - if (_memicmp(&Buffer[9], "CONNECTPENDING", 14) == 0) // Save Pending state for scan control - TNC->ConnectPending = TRUE; - else - TNC->ConnectPending = FALSE; - - if (_memicmp(&Buffer[9], "DISCONNECTING", 13) == 0) // So we can timout stuck discpending - { - TNC->DiscPending = 600; - return; - } - if (_memicmp(&Buffer[9], "DISCONNECTED", 12) == 0) - { - TNC->DiscPending = FALSE; - return; - } - - if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control - TNC->TXRXState = 'S'; - else if (strcmp(&Buffer[9], "IRS") == 0) - TNC->TXRXState = 'R'; - - return; - } - - - if (_memicmp(Buffer, "PROCESSID", 9) == 0) - { - HANDLE hProc; - char ExeName[256] = ""; - - TNC->PID = atoi(&Buffer[10]); - -#ifdef WIN32 - - // Get the File Name in case we want to restart it. - - if (TNC->ProgramPath == NULL) - { - if (GetModuleFileNameExPtr) - { - hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); - - if (hProc) - { - GetModuleFileNameExPtr(hProc, 0, ExeName, 255); - CloseHandle(hProc); - - TNC->ProgramPath = _strdup(ExeName); - } - } - } - - // Set Window Title to reflect BPQ Port Description - - EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); -#endif - } - - if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) - { - if (TNC->FECMode) - { - Sleep(1000); - - if (TNC->FEC1600) - send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); - else - send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); - return; - } - } - - if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) - { - TNC->PlaybackDevices = _strdup(&Buffer[16]); - } - // Others should be responses to commands - - if (_memicmp(Buffer, "BLOCKED", 6) == 0) - { - WritetoTrace(TNC, Buffer, MsgLen - 2); - return; - } - - if (_memicmp(Buffer, "OVER", 4) == 0) - { - WritetoTrace(TNC, Buffer, MsgLen - 2); - return; - } - - GetSemaphore(&Semaphore, 50); - - buffptr = GetBuff(); - - if (buffptr == 0) - { - FreeSemaphore(&Semaphore); - return; // No buffers, so ignore - } - - buffptr->Len = sprintf(buffptr->Data, "Winmor} %s\r", Buffer); - - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - FreeSemaphore(&Semaphore); -} - -static int ProcessReceivedData(struct TNCINFO * TNC) -{ - int InputLen, MsgLen; - char * ptr, * ptr2; - char Buffer[2000]; - - // May have several messages per packet, or message split over packets - - if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection - TNC->InputLen=0; - - InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); - - if (InputLen == 0 || InputLen == SOCKET_ERROR) - { - // Does this mean closed? - - closesocket(TNC->TCPSock); - closesocket(TNC->TCPDataSock); - - TNC->TCPSock = 0; - TNC->TCPDataSock = 0; - - TNC->CONNECTED = FALSE; - TNC->Streams[0].ReportDISC = TRUE; - - return 0; - } - - TNC->InputLen += InputLen; - -loop: - - ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); - - if (ptr) // CR in buffer - { - ptr2 = &TNC->TCPBuffer[TNC->InputLen]; - ptr++; // Assume LF Follows CR - - if (ptr == ptr2) - { - // Usual Case - single meg in buffer - - ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); - TNC->InputLen=0; - } - else - { - // buffer contains more that 1 message - - MsgLen = TNC->InputLen - (int)(ptr2-ptr); - - memcpy(Buffer, TNC->TCPBuffer, MsgLen); - - ProcessResponse(TNC, Buffer, MsgLen); - memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); - - TNC->InputLen -= MsgLen; - goto loop; - } - } - return 0; -} - - -VOID ProcessDataSocketData(int port) -{ - // Info on Data Socket - just packetize and send on - - struct TNCINFO * TNC = TNCInfo[port]; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - int InputLen, PacLen = 236; - PMSGWITHLEN buffptr; - char * msg; - - TNC->TimeSinceLast = 0; - -loop: - buffptr = GetBuff(); - - if (buffptr == NULL) return; // No buffers, so ignore - - InputLen = recv(TNC->TCPDataSock, buffptr->Data, PacLen, 0); - - if (InputLen == -1) - { - ReleaseBuffer(buffptr); - return; - } - - - //Debugprintf("Winmor: RXD %d bytes", InputLen); - - if (InputLen == 0) - { - // Does this mean closed? - - sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - TNC->CONNECTING = FALSE; - TNC->CONNECTED = FALSE; - TNC->Streams[0].ReportDISC = TRUE; - - ReleaseBuffer(buffptr); - return; - } - - STREAM->bytesRXed += InputLen; - - msg = &buffptr->Data[0]; - msg[InputLen] = 0; - - WritetoTrace(TNC, msg, InputLen); - - if (TNC->FECMode) - { - InputLen = (int)strlen(&buffptr->Data[0]); - - if (msg[InputLen - 1] == 3) // End of errored block - msg[InputLen++] = 13; // Add CR - - } - buffptr->Len = InputLen; - C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); - - goto loop; -} - -/* -INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - int Cmd = LOWORD(wParam); - - switch (message) - { - case WM_INITDIALOG: - { - struct TNCINFO * TNC = (struct TNCINFO * )lParam; - char * ptr1, *ptr2; - int ptr3 = 0; - char Line[1000]; - int len; - - ptr1 = TNC->CaptureDevices; - - if (!ptr1) - return 0; // No Devices - - - while (ptr2 = strchr(ptr1, ',')) - { - len = ptr2 - ptr1; - memcpy(&Line[ptr3], ptr1, len); - ptr3 += len; - Line[ptr3++] = '\r'; - Line[ptr3++] = '\n'; - - ptr1 = ++ptr2; - } - Line[ptr3] = 0; - strcat(Line, ptr1); - - SetDlgItemText(hDlg, IDC_CAPTURE, Line); - - ptr3 = 0; - - ptr1 = TNC->PlaybackDevices; - - if (!ptr1) - return 0; // No Devices - - - while (ptr2 = strchr(ptr1, ',')) - { - len = ptr2 - ptr1; - memcpy(&Line[ptr3], ptr1, len); - ptr3 += len; - Line[ptr3++] = '\r'; - Line[ptr3++] = '\n'; - - ptr1 = ++ptr2; - } - Line[ptr3] = 0; - strcat(Line, ptr1); - - SetDlgItemText(hDlg, IDC_PLAYBACK, Line); - - SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); - -// KillTNC(TNC); - - return TRUE; - } - - case WM_SIZING: - { - return TRUE; - } - - case WM_ACTIVATE: - -// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); - - break; - - - case WM_COMMAND: - - - if (Cmd == IDCANCEL) - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - - return (INT_PTR)TRUE; - - break; - } - return (INT_PTR)FALSE; -} -*/ - -#ifdef LINBPQ -#include -#include -#endif - - -int KillTNC(struct TNCINFO * TNC) -{ - if (TNC->ProgramPath && _memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) - { - // Try to Kill TNC on a remote host - - SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); - struct sockaddr_in destaddr; - char Msg[256]; - int Len; - - if (sock == INVALID_SOCKET) - return 0; - - destaddr.sin_family = AF_INET; - destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - destaddr.sin_port = htons(8500); - - if (destaddr.sin_addr.s_addr == INADDR_NONE) - { - // Resolve name to address - - struct hostent * HostEnt = gethostbyname (TNC->HostName); - - if (!HostEnt) - return 0; // Resolve failed - - memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); - } - - if (TNC->PID) - Len = sprintf(Msg, "KILL %d", TNC->PID); - else - Len = sprintf(Msg, "KILLBYNAME %s", &TNC->ProgramPath[7]); - - sendto(sock, Msg, Len, 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); - Sleep(100); - closesocket(sock); - - TNC->PID = 0; // So we don't try again - return 1; // Cant tell if it worked, but assume ok - } - - if (TNC->PID == 0) - return 0; - -#ifdef WIN32 - { - HANDLE hProc; - - Debugprintf("KillTNC Called for Pid %d", TNC->PID); - - if (TNC->PTTMode) - Rig_PTT(TNC, FALSE); // Make sure PTT is down - - hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); - - if (hProc) - { - TerminateProcess(hProc, 0); - CloseHandle(hProc); - } - } -#else - - printf("KillTNC Called for Pid %d Returned %d\n", TNC->PID, kill(TNC->PID, SIGTERM)); - -#endif - TNC->PID = 0; // So we don't try again - - return 0; -} - -BOOL RestartTNC(struct TNCINFO * TNC) -{ - if (TNC->ProgramPath == NULL || TNC->DontRestart) - return 0; - - if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) - { - int n; - - // Try to start TNC on a remote host - - SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); - struct sockaddr_in destaddr; - - Debugprintf("trying to restart TNC %s", TNC->ProgramPath); - - if (sock == INVALID_SOCKET) - return 0; - - destaddr.sin_family = AF_INET; - destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); - destaddr.sin_port = htons(8500); - - if (destaddr.sin_addr.s_addr == INADDR_NONE) - { - // Resolve name to address - - struct hostent * HostEnt = gethostbyname (TNC->HostName); - - if (!HostEnt) - return 0; // Resolve failed - - memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); - } - - n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); - - Debugprintf("Restart TNC - sendto returned %d", n); - - Sleep(100); - closesocket(sock); - - return 1; // Cant tell if it worked, but assume ok - } - - // Not Remote - - // Extract any parameters from command string - -#ifndef WIN32 - { - char * arg_list[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; - pid_t child_pid; - char * Copy, * Context; - signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. - - Copy = _strdup(TNC->ProgramPath); // Save as strtok mangles it - - arg_list[0] = strtok_s(Copy, " \n\r", &Context); - if (arg_list[0]) - arg_list[1] = strtok_s(NULL, " \n\r", &Context); - if (arg_list[1]) - arg_list[2] = strtok_s(NULL, " \n\r", &Context); - if (arg_list[2]) - arg_list[3] = strtok_s(NULL, " \n\r", &Context); - if (arg_list[3]) - arg_list[4] = strtok_s(NULL, " \n\r", &Context); - if (arg_list[4]) - arg_list[5] = strtok_s(NULL, " \n\r", &Context); - if (arg_list[5]) - arg_list[6] = strtok_s(NULL, " \n\r", &Context); - if (arg_list[6]) - arg_list[7] = strtok_s(NULL, " \n\r", &Context); - - // Fork and Exec TNC - - printf("Trying to start %s\n", TNC->ProgramPath); - - /* Duplicate this process. */ - - child_pid = fork (); - - if (child_pid == -1) - { - printf ("StartTNC fork() Failed\n"); - free(Copy); - return 0; - } - - if (child_pid == 0) - { - execvp (arg_list[0], arg_list); - - /* The execvp function returns only if an error occurs. */ - - printf ("Failed to start TNC\n"); - exit(0); // Kill the new process - } - else - { - TNC->PID = child_pid; - printf("Started TNC, Process ID = %d\n", TNC->PID); - } - free(Copy); - return TRUE; - } -#else - { - int n = 0; - - STARTUPINFO SInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION - char workingDirectory[256]; - int i = strlen(TNC->ProgramPath); - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); - - strcpy(workingDirectory, TNC->ProgramPath); - - while (i--) - { - if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') - { - workingDirectory[i] = 0; - break; - } - } - - while (KillOldTNC(TNC->ProgramPath) && n++ < 100) - { - Sleep(100); - } - - if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) - { - Debugprintf("Restart TNC OK"); - TNC->PID = PInfo.dwProcessId; - return TRUE; - } - else - { - Debugprintf("Restart TNC Failed %d ", GetLastError()); - return FALSE; - } - } -#endif - return 0; -} - -VOID TidyClose(struct TNCINFO * TNC, int Stream) -{ - // If all acked, send disc - - if (TNC->Streams[0].BytesOutstanding == 0) - send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); -} - -VOID ForcedClose(struct TNCINFO * TNC, int Stream) -{ - send(TNC->TCPSock,"DIRTYDISCONNECT\r\n", 17, 0); -} - -VOID CloseComplete(struct TNCINFO * TNC, int Stream) -{ - ReleaseTNC(TNC); - - if (TNC->FECMode) - { - TNC->FECMode = FALSE; - send(TNC->TCPSock,"SENDID 0\r\n", 10, 0); - } -} - -BOOL KillOldTNC(char * Path) -{ -#ifdef WIN32 - HANDLE hProc; - char ExeName[256] = ""; - DWORD Pid = 0; - - DWORD Processes[1024], Needed, Count; - unsigned int i; - - if (EnumProcessesPtr == NULL) - return FALSE; - - if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) - return FALSE; - - // Calculate how many process identifiers were returned. - - Count = Needed / sizeof(DWORD); - - for (i = 0; i < Count; i++) - { - if (Processes[i] != 0) - { - hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); - - if (hProc) - { - GetModuleFileNameExPtr(hProc, 0, ExeName, 255); - - // Path could have parameters, so use memcmp - - if (_memicmp(ExeName, Path, strlen(ExeName)) == 0) - { - Debugprintf("Killing Pid %d %s", Processes[i], ExeName); - TerminateProcess(hProc, 0); - CloseHandle(hProc); - return TRUE; - } - CloseHandle(hProc); - } - } - } -#endif - return FALSE; -} +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use WINMOR as a Port Driver +// +// Uses BPQ EXTERNAL interface +// + + +// Version 1.0 January 2009 - Initial Version +// + +// March 22 2010 + +// Send FAULTS to Monitor Window +// Force PROTOCOL = WINMOR/PACTOR (to simplifiy Config) + +// July 2010 +// Support up to 32 BPQ Ports +// Support up to 32 Applications + +// Version 1.2.1.2 August 2010 + +// Save Minimized State +// Handle new "BLOCKED by Busy channel" message from TNC + +// Version 1.2.1.4 August 2010 + +// Add Scan control of BW setting +// Reset TNC if stuck in Disconnecting +// Add option to send reports to WL2K +// Disconnect if appl not available + +// Version 1.2.1.5 August 2010 + +// Updates to WL2K Reporting +// Send Watchdog polls every minute and restart if no response. +// Don't connect if channel is busy (unless specifically overridden) + +// Version 1.2.1.6 September 2010 + +// Add option to kill and restart TNC after each transfer +// Fix PTT operation after Node reconfig + +// Version 1.2.2.1 September 2010 + +// Add option to get config from bpq32.cfg +// Merge with BPQ32.dll + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + +#ifdef WIN32 +#include +#endif + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int standardParams(struct TNCINFO * TNC, char * buf); + +static char ClassName[]="WINMORSTATUS"; +static char WindowTitle[] = "WINMOR"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + +// There seem to be timing issues when calling SendMessage from multiple threads. +// Queue and process in main thread + +UINT * WINMORTraceQ; +UINT * SetWindowTextQ; + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) +{ + int index = 0; + UCHAR * ptr1 = Msg, * ptr2; + UCHAR Line[1000]; + int LineLen, i; + UCHAR Save; + int SaveLen = Len; + char Time[16]; + time_t T; + struct tm * tm; + + if (Len < 0) + return; + + Save = Msg[Len]; + Msg[Len] = 0; + +#ifndef LINBPQ + index=SendMessage(TNC->hMonitor, LB_SETCURSEL, -1, 0); +#endif + +lineloop: + + if (Len > 0) + { + // copy text to control a line at a time + + ptr2 = memchr(ptr1, 13, Len); + + if (ptr2) + { + ptr2++; + LineLen = (int)(ptr2 - ptr1); + Len -= LineLen; + memcpy(Line, ptr1, LineLen); + memcpy(&Line[LineLen - 1], "", 4); + LineLen += 3; + + if ((*ptr2) == 10) + { + memcpy(&Line[LineLen], "", 4); + LineLen += 4; + ptr2++; + Len --; + } + + Line[LineLen] = 0; + + // If line contains any data above 7f, assume binary and dont display + + for (i = 0; i < LineLen; i++) + { + if (Line[i] > 126 || Line[i] < 32) + goto Skip; + } + + // We now also pass to Monitor Window + + if (strlen(Line) < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + strlen(Line); + Monframe.DEST[0] = 1; // Plain Text Monitor + strcpy(&Monframe.DEST[1], Line); + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Line); +#endif + // Write to Web Buffer + + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + + strcat(TNC->WebBuffer, Time); + strcat(TNC->WebBuffer, Line); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved + Skip: + ptr1 = ptr2; + + goto lineloop; + + } + + // Process incomplete line + + for (i = 0; i < Len; i++) + { + if (ptr1[i] > 126 || ptr1[i] < 32) + break; + } + + if (i == Len) + { + if (Len < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + Len; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], ptr1, Len); + Monframe.DEST[1 + Len] = 0; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); +#endif + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + strcat(TNC->WebBuffer, Time); + + strcat(TNC->WebBuffer, ptr1); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved + } + } + +#ifdef LINBPQ +#else + + if (index > 1200) + do + index=index=SendMessage(TNC->hMonitor, LB_DELETESTRING, 0, 0); + while (index > 1000); + + if (index > -1) + index=SendMessage(TNC->hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); +#endif + Msg[SaveLen] = Save; + +} + +VOID MySetWindowTextWithSem(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD(&SetWindowTextQ, buffptr); + } + +#endif +} + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); + +struct SEM SetWindTextSem = {0, 0, 0, 0}; + +VOID MySetWindowText(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + GetSemaphore(&SetWindTextSem, 61); + buffptr = zalloc(400); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD_NP(&SetWindowTextQ, buffptr); + } + + FreeSemaphore(&SetWindTextSem); +#endif +} + +VOID SetWindowTextSupport() +{ + PMSGWITHLEN Buffer; + + while (SetWindowTextQ) + { + GetSemaphore(&SetWindTextSem, 61); + Buffer = Q_REM_NP(&SetWindowTextQ); + SetWindowText((HWND)Buffer->Len, Buffer->Data); + FreeSemaphore(&SetWindTextSem); + free(Buffer); + } +} + + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len) +{ + // It seems writing from multiple threads can cause problems in Windows + // Queue and process in main thread + +#ifdef LINBPQ + WritetoTraceSupport(TNC, Msg, Len); +} +#else + UINT * buffptr; + BOOL Sem = FALSE; + + if (Len < 0) + return; + + // Get semaphore if it isn't set + + if (InterlockedExchange(&Semaphore.Flag, 1) == 0) + { + Sem = TRUE; + Semaphore.Gets++; + } + + buffptr = GetBuff(); + + if (buffptr) + { + if (Len > 340) + Len = 340; + + buffptr[1] = (UINT)TNC; + buffptr[2] = (UINT)Len; + memcpy(&buffptr[3], Msg, Len + 1); + + C_Q_ADD(&WINMORTraceQ, buffptr); + } + + if (Sem) + FreeSemaphore(&Semaphore); + +} +#endif + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else if (standardParams(TNC, buf) == FALSE) + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + + + +void WINMORThread(void * portptr); +VOID ProcessDataSocketData(int port); +int ConnecttoWINMOR(int port); +static int ProcessReceivedData(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + + + +VOID ChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + datalen = sprintf(TXMsg, "MYC %s\r\n", Call); + send(TNC->TCPSock,TXMsg, datalen, 0); + +// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); +// TNC->StartSent = TRUE; + + send(TNC->TCPSock, "MYC\r\n", 5, 0); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int i,winerr; + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes; + size_t txlen = 0; + char ErrMsg[255]; + size_t Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + fd_set readfs; + fd_set writefs; + fd_set errorfs; + struct timeval timeout; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 1: // poll + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); + STREAM->Disconnecting = TRUE; + } + } + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->ConnectCmd && TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + + send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->ConnectCmd = 0; + + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + if (TNC->HeartBeat > 600 && TNC->hWnd) + { + char wtext[100]; + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(TNC->hWnd, wtext); + } + + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + send(TNC->TCPSock, "MODE\r\n", 6, 0); + else + { + if (time(NULL) - TNC->WinmorRestartCodecTimer > 900) // 15 mins + { + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + send(TNC->TCPSock, "STATE\r\n", 7, 0); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); + } + } + if (TNC->FECPending) // Check if FEC Send needed + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + send(TNC->TCPSock, "DISCONNECT\r\n", 12, 0); + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath && TNC->CONNECTED) + { + if (strstr(TNC->ProgramPath, "WINMOR TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + SetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + SetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + ProcessDataSocketData(port); + + if (FD_ISSET(TNC->TCPDataSock, &writefs)) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = buffptr->Len; + memcpy(txbuff,buffptr->Data,txlen); + bytes=send(TNC->TCPSock, (const char FAR *)&txbuff, (int)txlen, 0); + ReleaseBuffer(buffptr); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + i=sprintf(ErrMsg, "WINMOR Data Connection lost for BPQ Port %d\r\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); // Data goes to +7, but we have an extra byte + datalen = buffptr->Len; + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(buffptr->Data, "No Connection to WINMOR Virtual TNC\r", 36); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes = send(TNC->TCPDataSock, txbuff, (int)txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, txbuff, (int)txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + STREAM->PacketsSent++; + + if (STREAM->PacketsSent == 3) + { + if (TNC->Robust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + else + send(TNC->TCPSock, "ROBUST FALSE\r\n", 14, 0); + } + + bytes = send(TNC->TCPDataSock,buff->L2DATA, (int)txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], (int)txlen); + + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + + if (TNC->BusyFlags) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &cmd); + + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) + { + if (buff->L2DATA[9] != 13) + { + // Limit connects + + int tries = atoi(&buff->L2DATA[10]); + int len; + + if (tries > 10) tries = 10; + len = sprintf(&buff->L2DATA[0], "MAXCONREQ %d\r\nMAXCONREQ\r\n", tries); + + send(TNC->TCPSock, &buff->L2DATA[0], len, 0); + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if ((_memicmp(buff->L2DATA, "BW 500", 6) == 0) || (_memicmp(buff->L2DATA, "BW 1600", 7) == 0)) + { + // Generate a local response + + PMSGWITHLEN buffptr = GetBuff(); + + if (_memicmp(buff->L2DATA, "BW 500", 6) == 0) + TNC->WL2KMode = 21; + else + TNC->WL2KMode = 22; + + if (buffptr) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + TNC->WinmorCurrentMode = 0; // So scanner will set next value + } + + if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(buff->L2DATA, "ROBUST", 6) == 0) + { + if (_memicmp(&buff->L2DATA[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + else + TNC->Robust = FALSE; + } + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(buff->L2DATA, "FEC\r", 4) == 0 || _memicmp(buff->L2DATA, "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; + send(TNC->TCPSock,"FECRCV TRUE\r\nFECRCV\r\n", 21, 0); + + if (_memicmp(buff->L2DATA, "FEC 1600", 8) == 0) + TNC->FEC1600 = TRUE; + else + TNC->FEC1600 = FALSE; + + return 0; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80] = "CONNECT "; + + memcpy(&Connect[8], &buff->L2DATA[2], txlen); + txlen += 6; + Connect[txlen++] = 0x0a; + Connect[txlen] = 0; + + _strupr(Connect); + + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + bytes = send(TNC->TCPSock, Connect, (int)txlen, 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &Connect[8], txlen-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + buff->L2DATA[txlen++] = 0x0a; + bytes = send(TNC->TCPSock, &buff->L2DATA[0], (int)txlen, 0); + } + } + if (bytes != txlen) + { + + // WINMOR doesn't seem to recover from a blocked write. For now just reset + + winerr = WSAGetLastError(); + sprintf(ErrMsg, "WINMOR Write Failed for port %d - error code = %d\r\n", port, winerr); + WritetoConsole(ErrMsg); + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + return (0); + } + + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + break; + + case 4: // reinit + + return (0); + + case 5: // Close + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return (0); + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on FLDIGI"); + return 1; // OK to change + } + + if (!TNC->TCPSock) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending) + return TRUE; // Not OK to Change + + if (TNC->CONNECTED) + { + TNC->GavePermission = TRUE; + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + } + return FALSE; + } + + if (Param == 3) // Release Permission + { + if (TNC->CONNECTED) + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + } + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + + if (Scan->Bandwidth == 'W') // Set Wide Mode + { + if (TNC->WinmorCurrentMode != 1600) + { + if (TNC->WinmorCurrentMode == 0) + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + + if (TNC->CONNECTED) + send(TNC->TCPSock, "BW 1600\r\n", 9, 0); + TNC->WinmorCurrentMode = 1600; + } + TNC->WL2KMode = 22; + return 0; + } + + + if (Scan->Bandwidth == 'N') // Set Narrow Mode + { + if (TNC->WinmorCurrentMode != 500) + { + if (TNC->WinmorCurrentMode == 0) + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + + TNC->WinmorCurrentMode = 500; + if (TNC->CONNECTED) + send(TNC->TCPSock, "BW 500\r\n", 8, 0); + } + TNC->WL2KMode = 21; + return 0; + } + + if (Scan->Bandwidth == 'X') // Dont Allow Connects + { + if (TNC->WinmorCurrentMode != 0) + { + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + TNC->WinmorCurrentMode = 0; + } + + TNC->WL2KMode = 0; + return 0; + } + + return 0; + } + return 0; +} + +VOID ReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[256]; + char wtext[100]; + + ChangeMYC(TNC, TNC->NodeCall); + + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->hWnd) + { + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(TNC->hWnd, wtext); + } + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC) +{ + // Disable other TNCs in same Interlock Group + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (rxInterlock == 0 || txInterlock == 0) + return; + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc) + TNC->SuspendPortProc(TNC, ThisTNC); + } +} + +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC) +{ + // Enable other TNCs in same Interlock Group + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (rxInterlock == 0 && txInterlock == 0) + return; + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc) + TNC->ReleasePortProc(TNC); + } +} + +VOID WinmorSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + if (TNC->CONNECTED) + send(TNC->TCPSock, "CODEC FALSE\r\n", 14, 0); + + if (TNC->Busy) + { + TNC->Busy = FALSE; // Can't clear detector if CODEC off. + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } +} + +VOID WinmorReleasePort(struct TNCINFO * TNC) +{ + if (TNC->CONNECTED) + send(TNC->TCPSock, "CODEC TRUE\r\n", 13, 0); +} + +extern char WebProcTemplate[]; +extern char sliderBit[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "WINMOR Status", "WINMOR Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +void * WinmorExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_WINMOR; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = WinmorSuspendPort; + TNC->ReleasePortProc = WinmorReleasePort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "DebugLog True\r\n"); + strcat(TempScript, "CWID False\r\n"); + strcat(TempScript, "BW 1600\r\n"); + strcat(TempScript, "ROBUST False\r\n"); + strcat(TempScript, "MODE AUTO\r\n"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + TNC->WL2KMode = 22; // in case not scanning + + // Set MYCALL + + strcat(TNC->InitScript,"FECRCV True\r\n"); + strcat(TNC->InitScript,"AUTOBREAK True\r\n"); + + sprintf(Msg, "MYC %s\r\nCODEC TRUE\r\nLISTEN TRUE\r\nMYC\r\n", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + strcat(TNC->InitScript,"PROCESSID\r\n"); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + *ptr++ = ','; + *ptr = 0; + } + + strcat(Aux, Appl); + } + } + strcat(TNC->InitScript, Aux); + strcat(TNC->InitScript,"\r\nMYAUX\r\n"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Winmor TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Winmor TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + Consoleprintf("WINMOR Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoWINMOR(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +int ConnecttoWINMOR(int port) +{ + _beginthread(WINMORThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID WINMORThread(void * portptr) +{ + // Opens both sockets and looks for data on control socket. Data socket is polled from BG, + // but we need fast response to control messages for PTT porcessing + + int port = (int)(size_t)portptr; + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + if (TNC->HostName == NULL) + return; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + } +#endif + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(TNC->TCPSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for WINMOR socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->CONNECTING = FALSE; + TNC->TCPSock = 0; + + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for WINMOR Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // Send INIT script + + send(TNC->TCPSock, TNC->InitScript , (int)strlen(TNC->InitScript), 0); + TNC->Alerted = TRUE; + + if (TNC->Hardware == H_V4) + sprintf(TNC->WEB_COMMSSTATE, "Connected to V4 TNC"); + else + sprintf(TNC->WEB_COMMSSTATE, "Connected to WINMOR TNC"); + + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + } + else + { + sprintf(Msg, "Connect Failed for WINMOR Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + TNC->HeartBeat = 0; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + printf("Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + if (TNC->Hardware == H_V4) + V4ProcessReceivedData(TNC); + else + ProcessReceivedData(TNC); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "WINMOR Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + return; + } + } + else + { + // 90 secs without data. Shouldn't happen + + sprintf(Msg, "WINMOR Connection Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + TNC->TCPDataSock = 0; + TNC->TCPSock= 0; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + return; + } + } +} + +#ifdef WIN32 + +BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"WINMOR Sound Card TNC", 21) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + TNC->hWnd = hwnd; // save so we can reset title when sessicn closes + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + // Response on WINMOR control channel. Could be a reply to a command, or + // an Async Response + + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 2] = 0; + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + Debugprintf(Buffer); + + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + return; + } + + Buffer[MsgLen - 2] = 0; // Remove CRLF + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + Debugprintf(Buffer); + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 2); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "CONNECTED", 9) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + + Debugprintf(Buffer); + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + if (TNC->StartInRobust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incomming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + GetSemaphore(&Semaphore, 50); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + FreeSemaphore(&Semaphore); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("WINMOR Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("WINMOR Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + + send(TNC->TCPDataSock, buffptr->Data, (int)buffptr->Len, 0); + STREAM->bytesTXed += (int)buffptr->Len; + WritetoTrace(TNC, buffptr->Data, (int)buffptr->Len); + ReleaseBuffer(buffptr); + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + Debugprintf(Buffer); + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "Winmor} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + if (TNC->RestartAfterFailure) + { + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + + return; + } + + + // Release Session + + if (TNC->Streams[0].Connected) + { + hookL4SessionDeleted(TNC, STREAM); + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "MONCALL", 7) == 0) + { + Debugprintf(Buffer); + + // Add to MHEARD + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + UpdateMH(TNC, &Buffer[8], '!', 0); + + if (!TNC->FECMode) + return; // If in FEC mode pass ID messages to user. + } + + if (_memicmp(Buffer, "CMD", 3) == 0) + { + return; + } + + if (_memicmp(Buffer, "BUFFERS", 7) == 0) + { + int inq, inrx, Sent, BPM; + + sscanf(&Buffer[8], "%d%d%d%d%d", &inq, &inrx, &TNC->Streams[0].BytesOutstanding, &Sent, &BPM); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } +// else +// if (TNC->TXRXState == 'S') +// send(TNC->TCPSock,"OVER\r\n", 6, 0); + + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + SetWindowText(TNC->xIDC_TRAFFIC, &Buffer[8]); + strcpy(TNC->WEB_TRAFFIC, &Buffer[8]); + return; + } + + Debugprintf(Buffer); + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + // Debugprintf("WINMOR RX: %s", Buffer); + + strcpy(TNC->WEB_MODE, &Buffer[5]); + GetSemaphore(&Semaphore, 50); + MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); + FreeSemaphore(&Semaphore); + return; + } + + if (_memicmp(Buffer, "PENDING", 6) == 0) + return; + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + TNC->WinmorRestartCodecTimer = time(NULL); + + SetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "CONNECTPENDING", 14) == 0) // Save Pending state for scan control + TNC->ConnectPending = TRUE; + else + TNC->ConnectPending = FALSE; + + if (_memicmp(&Buffer[9], "DISCONNECTING", 13) == 0) // So we can timout stuck discpending + { + TNC->DiscPending = 600; + return; + } + if (_memicmp(&Buffer[9], "DISCONNECTED", 12) == 0) + { + TNC->DiscPending = FALSE; + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->PID = atoi(&Buffer[10]); + +#ifdef WIN32 + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + // Set Window Title to reflect BPQ Port Description + + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "Winmor} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + FreeSemaphore(&Semaphore); +} + +static int ProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return 0; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &TNC->TCPBuffer[TNC->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); + TNC->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = TNC->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, TNC->TCPBuffer, MsgLen); + + ProcessResponse(TNC, Buffer, MsgLen); + memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + return 0; +} + + +VOID ProcessDataSocketData(int port) +{ + // Info on Data Socket - just packetize and send on + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int InputLen, PacLen = 236; + PMSGWITHLEN buffptr; + char * msg; + + TNC->TimeSinceLast = 0; + +loop: + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + InputLen = recv(TNC->TCPDataSock, buffptr->Data, PacLen, 0); + + if (InputLen == -1) + { + ReleaseBuffer(buffptr); + return; + } + + + //Debugprintf("Winmor: RXD %d bytes", InputLen); + + if (InputLen == 0) + { + // Does this mean closed? + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + ReleaseBuffer(buffptr); + return; + } + + STREAM->bytesRXed += InputLen; + + msg = &buffptr->Data[0]; + msg[InputLen] = 0; + + WritetoTrace(TNC, msg, InputLen); + + if (TNC->FECMode) + { + InputLen = (int)strlen(&buffptr->Data[0]); + + if (msg[InputLen - 1] == 3) // End of errored block + msg[InputLen++] = 13; // Add CR + + } + buffptr->Len = InputLen; + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + goto loop; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ + +#ifdef LINBPQ +#include +#include +#endif + + +int KillTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath && _memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + // Try to Kill TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + char Msg[256]; + int Len; + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->PID) + Len = sprintf(Msg, "KILL %d", TNC->PID); + else + Len = sprintf(Msg, "KILLBYNAME %s", &TNC->ProgramPath[7]); + + sendto(sock, Msg, Len, 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + Sleep(100); + closesocket(sock); + + TNC->PID = 0; // So we don't try again + return 1; // Cant tell if it worked, but assume ok + } + + if (TNC->PID == 0) + return 0; + +#ifdef WIN32 + { + HANDLE hProc; + + Debugprintf("KillTNC Called for Pid %d", TNC->PID); + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } +#else + + printf("KillTNC Called for Pid %d Returned %d\n", TNC->PID, kill(TNC->PID, SIGTERM)); + +#endif + TNC->PID = 0; // So we don't try again + + return 0; +} + +BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Extract any parameters from command string + +#ifndef WIN32 + { + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Copy, * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + Copy = _strdup(TNC->ProgramPath); // Save as strtok mangles it + + arg_list[0] = strtok_s(Copy, " \n\r", &Context); + if (arg_list[0]) + arg_list[1] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[1]) + arg_list[2] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[2]) + arg_list[3] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[3]) + arg_list[4] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[4]) + arg_list[5] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[5]) + arg_list[6] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[6]) + arg_list[7] = strtok_s(NULL, " \n\r", &Context); + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + free(Copy); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + else + { + TNC->PID = child_pid; + printf("Started TNC, Process ID = %d\n", TNC->PID); + } + free(Copy); + return TRUE; + } +#else + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + while (KillOldTNC(TNC->ProgramPath) && n++ < 100) + { + Sleep(100); + } + + if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + send(TNC->TCPSock,"DIRTYDISCONNECT\r\n", 17, 0); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + send(TNC->TCPSock,"SENDID 0\r\n", 10, 0); + } +} + +BOOL KillOldTNC(char * Path) +{ +#ifdef WIN32 + HANDLE hProc; + char ExeName[256] = ""; + DWORD Pid = 0; + + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (EnumProcessesPtr == NULL) + return FALSE; + + if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) + return FALSE; + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + + // Path could have parameters, so use memcmp + + if (_memicmp(ExeName, Path, strlen(ExeName)) == 0) + { + Debugprintf("Killing Pid %d %s", Processes[i], ExeName); + TerminateProcess(hProc, 0); + CloseHandle(hProc); + return TRUE; + } + CloseHandle(hProc); + } + } + } +#endif + return FALSE; +} diff --git a/.svn/pristine/18/189d898ab4fc086c05a5870e1f63d0706d6449c3.svn-base b/.svn/pristine/18/189d898ab4fc086c05a5870e1f63d0706d6449c3.svn-base index 4d12d2d..ec461d3 100644 --- a/.svn/pristine/18/189d898ab4fc086c05a5870e1f63d0706d6449c3.svn-base +++ b/.svn/pristine/18/189d898ab4fc086c05a5870e1f63d0706d6449c3.svn-base @@ -1,2172 +1,2172 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// DLL to inteface KAM TNC in Pactor Mode to BPQ32 switch -// -// Uses BPQ EXTERNAL interface -// - -// Version 1.2.1.2 July 2010 - -// Send Change to ISS before each transmission -// Support up to 32 BPQ Ports - -// Version 1.2.1.3 August 2010 - -// Drop RTS as well as DTR on close - -// Version 1.2.1.4 August 2010 - -// Save Minimized State - -// Version 1.2.1.5 September 2010 - -// Fix Freq Display after Node reconfig -// Only use AutoConnect APPL for Pactor Connects - -// Version 1.2.2.1 September 2010 - -// Add option to get config from bpq32.cfg - -//#define WIN32_LEAN_AND_MEAN -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_DEPRECATE - -#include -#include -#include "time.h" - -#include "cheaders.h" -#include "tncinfo.h" - -#include "bpq32.h" - -#define NARROWMODE Report_P1 -#define WIDEMODE Report_P1 // Only supports PI - -static char ClassName[]="KAMPACTORSTATUS"; -static char WindowTitle[] = "KAM Pactor"; -static int RigControlRow = 165; - -extern UCHAR LogDirectory[]; -static RECT Rect; - -int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); -VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); -VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); -VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); - -static FILE * LogHandle[32] = {0}; - -//char * Logs[4] = {"1", "2", "3", "4"}; - -static char BaseDir[MAX_PATH]="c:\\"; - -static BOOL WRITELOG = FALSE; - -BOOL KAMStopPort(struct PORTCONTROL * PORT) -{ - // Disable Port - close TCP Sockets or Serial Port - - struct TNCINFO * TNC = PORT->TNC; - - TNC->CONNECTED = FALSE; - TNC->Alerted = FALSE; - - if (TNC->Streams[0].Attached) - TNC->Streams[0].ReportDISC = TRUE; - - if (TNC->hDevice) - { - CloseCOMPort(TNC->hDevice); - TNC->hDevice = 0; - } - - TNC->HostMode = FALSE; - - sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); - MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); - - return TRUE; -} - -BOOL KAMStartPort(struct PORTCONTROL * PORT) -{ - // Restart Port - Open Sockets or Serial Port - - struct TNCINFO * TNC = PORT->TNC; - - TNC->ReopenTimer = 999; - TNC->ReinitState = 0; - - sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); - MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); - - return TRUE; -} - - - -static VOID CloseLogFile(int Flags) -{ - if (WRITELOG) - { - fclose(LogHandle[Flags]); - LogHandle[Flags] = NULL; - } -} - -static BOOL OpenLogFile(int Flags) -{ - if (WRITELOG) - { - UCHAR FN[MAX_PATH]; - - time_t T; - struct tm * tm; - - T = time(NULL); - tm = gmtime(&T); - - sprintf(FN,"%s/logs/KAMLog_%02d%02d_%d.txt", LogDirectory, tm->tm_mon + 1, tm->tm_mday, Flags); - - LogHandle[Flags] = fopen(FN, "ab"); - - return (LogHandle[Flags] != NULL); - } - return 0; -} - -static void WriteLogLine(int Port, char * Msg, int MsgLen) -{ - if (WRITELOG) - { - OpenLogFile(Port); - - if (LogHandle[Port]) - { - fwrite(Msg, 1, MsgLen, LogHandle[Port]); - fwrite("\r\n", 1, 2, LogHandle[Port]); - } - CloseLogFile(Port); - } -} - - - -int ProcessLine(char * buf, int Port) -{ - UCHAR * ptr,* p_cmd; - char * p_ipad = 0; - char * p_port = 0; - unsigned short WINMORport = 0; - int BPQport; - int len=510; - struct TNCINFO * TNC; - char errbuf[256]; - - strcpy(errbuf, buf); - - BPQport = Port; - - TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); - memset(TNC, 0, sizeof(struct TNCINFO)); - - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - - goto ConfigLine; - - - // Read Initialisation lines - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; -ConfigLine: - - strcpy(errbuf, buf); - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - ptr = strchr(buf, ';'); - if (ptr) - { - *ptr++ = 13; - *ptr = 0; - } - - if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log - WRITELOG = atoi(&buf[9]); - - else if (_memicmp(buf, "APPL", 4) == 0) - { - p_cmd = strtok(&buf[5], " \t\n\r"); - - if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#') - TNC->ApplCmd=_strdup(_strupr(p_cmd)); - } - - else if (_memicmp(buf, "OLDMODE", 7) == 0) - TNC->OldMode = TRUE; - - else if (_memicmp(buf, "VERYOLDMODE", 7) == 0) - { - TNC->OldMode = TRUE; - TNC->VeryOldMode = TRUE; - } - - else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy - TNC->BusyWait = atoi(&buf[8]); - - else if (_memicmp(buf, "WL2KREPORT", 10) == 0) - TNC->WL2K = DecodeWL2KReportLine(buf); - - else - strcat (TNC->InitScript, buf); - } - return (TRUE); -} - -#define FEND 0xC0 // KISS CONTROL CODES -#define FESC 0xDB -#define TFEND 0xDC -#define TFESC 0xDD - -static int MaxStreams = 26; - -BOOL CloseConnection(struct TNCINFO * conn); -static BOOL WriteCommBlock(struct TNCINFO * TNC); -BOOL DestroyTTYInfo(int port); -void CheckRXKAM(struct TNCINFO * TNC); -VOID KAMPoll(int Port); -VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); -static VOID ProcessTermModeResponse(struct TNCINFO * TNC); -static VOID DoTNCReinit(struct TNCINFO * TNC); -static VOID DoTermModeTimeout(struct TNCINFO * TNC); - -VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); -VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); - -// Note that Kantronics host Mode uses KISS format Packets (without a KISS COntrol Byte) - -VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); -static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); -static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); - -static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) -{ - int txlen; - PMSGWITHLEN buffptr; - - struct TNCINFO * TNC = TNCInfo[port]; - struct STREAMINFO * STREAM; - - int Stream; - - size_t Param; - struct ScanEntry * Scan; - - if (TNC == NULL) - return 0; - - if (TNC->hDevice == 0) - { - // Clear anything from UI_Q - - while (TNC->PortRecord->UI_Q) - { - buffptr = Q_REM(&TNC->PortRecord->UI_Q); - ReleaseBuffer(buffptr); - } - - // Try to reopen every 30 secs - - if (fn > 3 && fn < 7) - goto ok; - - TNC->ReopenTimer++; - - if (TNC->ReopenTimer < 300) - return 0; - - TNC->ReopenTimer = 0; - - if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) - OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); - - if (TNC->hDevice == 0) - return 0; - } - -ok: - - switch (fn) - { - case 7: - - // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances - - // G7TAJ's code to record activity for stats display - - if ( TNC->BusyFlags && CDBusy ) - TNC->PortRecord->PORTCONTROL.ACTIVE += 2; - - if ( TNC->PTTState ) - TNC->PortRecord->PORTCONTROL.SENDING += 2; - - CheckRXKAM(TNC); - KAMPoll(port); - - return 0; - - case 1: // poll - - while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q - { - buffptr = Q_REM(&TNC->PortRecord->UI_Q); - ReleaseBuffer(buffptr); - } - - for (Stream = 0; Stream <= MaxStreams; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - if (STREAM->ReportDISC) - { - STREAM->ReportDISC = FALSE; - buff->PORT = Stream; - - return -1; - } - } - - for (Stream = 0; Stream <= MaxStreams; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - if (STREAM->PACTORtoBPQ_Q !=0) - { - int datalen; - - buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); - - datalen = (int)buffptr->Len; - - buff->PORT = Stream; - buff->PID= 0xf0; - memcpy(buff->L2DATA, buffptr->Data, datalen); // Data goes to +7, but we have an extra byte - datalen += (MSGHDDRLEN + 1); - - PutLengthinBuffer(buff, datalen); - - ReleaseBuffer(buffptr); - - return (1); - } - } - - return 0; - - case 2: // send - - buffptr = GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - // Find TNC Record - - Stream = buff->PORT; - STREAM = &TNC->Streams[Stream]; - - if (!TNC->TNCOK) - { - // Send Error Response - - buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r"); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - return 0; - } - - txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); - - buffptr->Len = txlen; - memcpy(buffptr->Data, buff->L2DATA, txlen); - - C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); - - if(STREAM->Connected) - { - STREAM->FramesOutstanding++; - STREAM->FramesQueued++; - - STREAM->BytesOutstanding += txlen; - } - return (0); - - - case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding - - Stream = (int)(size_t)buff; - - STREAM = &TNC->Streams[Stream]; - - if (Stream == 0) - { - if (TNC->HFPacket) - { - if (TNC->Mem1 < 2000 || TNC->Streams[0].FramesOutstanding > 4) - return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); - - } - else - { - if (TNC->Streams[0].FramesQueued > 4) - return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); - } - } - else - { - if (STREAM->FramesOutstanding > 3 || STREAM->BytesOutstanding > 500 || TNC->Mem1 < 500) - return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); - } - - return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting - - case 4: // reinit - - return (0); - - case 5: // Close - - EncodeAndSend(TNC, "Q", 1); // Exit Host Mode - Sleep(50); - - CloseCOMPort(TNCInfo[port]->hDevice); - return (0); - - case 6: // Scan Control - - // Use P0 to Disable Pactor, P1 to enable - - Param = (size_t)buff; - - if (Param == 2) // Check Permission (shouldn't happen) - { - return 1; // OK to change - } - - if (!TNC->HostMode) - return 0; // No connection so no interlock - - if (Param == 1) // Request Permission - return 0; // OK to Change // Dont want to mess with disabling it - - if (Param == 3) // Release Permission - return 0; - - // Param is Address of a struct ScanEntry - - Scan = (struct ScanEntry *)buff; - - if (Scan->PMaxLevel >= '0' && Scan->PMaxLevel < '5') // 1 - 4 - { - if (TNC->Bandwidth != Scan->PMaxLevel) - { - // Enable or disable by setting mycall - - TNC->Bandwidth = Scan->PMaxLevel; - - if (Scan->PMaxLevel == '0') - { - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? - return 0; - } - if (TNC->OldMode) - EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen - else - EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen - - TNC->InternalCmd = 'T'; - } - } - } - return 0; -} - -VOID KAMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) -{ - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - strcpy(TNC->WEB_TNCSTATE, "Interlocked"); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - -// STREAM->CmdSet = STREAM->CmdSave = zalloc(100); -// sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects - -} - -VOID KAMReleasePort(struct TNCINFO * TNC) -{ - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - strcpy(TNC->WEB_TNCSTATE, "Free"); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - -} - - -static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) -{ - int Len = sprintf(Buff, "" - "KAM Pactor Status

KAM Pactor Status

"); - - Len += sprintf(&Buff[Len], ""); - - Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); - Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); - Len += sprintf(&Buff[Len], "", TNC->WEB_BUFFERS); - Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); - Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Free Space%s
Traffic%s
"); - - Len += sprintf(&Buff[Len], "", TNC->WebBuffer); - Len = DoScanLine(TNC, Buff, Len); - - return Len; -} - - -void * KAMExtInit(EXTPORTDATA * PortEntry) -{ - char msg[500]; - struct TNCINFO * TNC; - int port; - char * ptr; - char * TempScript; - - port=PortEntry->PORTCONTROL.PORTNUMBER; - - sprintf(msg,"KAM Pactor %s", PortEntry->PORTCONTROL.SerialPortName); - WritetoConsole(msg); - - ReadConfigFile(port, ProcessLine); - - TNC = TNCInfo[port]; - - if (TNC == NULL) - { - // Not defined in Config file - - sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); - WritetoConsole(msg); - - return ExtProc; - } - TNC->Port = port; - TNC->PortRecord = PortEntry; - - TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KAM; - - if (TNC->BusyWait == 0) - TNC->BusyWait = 10; - - PortEntry->MAXHOSTMODESESSIONS = 11; // Default - - - if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) - memcpy(TNC->NodeCall, MYNODECALL, 10); - else - ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); - - PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor - PortEntry->PORTCONTROL.PORTQUALITY = 0; - PortEntry->SCANCAPABILITIES = NONE; // No Scan Control - - if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) - TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; - - if (PortEntry->PORTCONTROL.PORTPACLEN == 0) - PortEntry->PORTCONTROL.PORTPACLEN = 100; - - PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; - PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; - -// TNC->SuspendPortProc = KAMSuspendPort; -// TNC->ReleasePortProc = KAMReleasePort; - - - - ptr=strchr(TNC->NodeCall, ' '); - if (ptr) *(ptr) = 0; // Null Terminate - - // Set Essential Params and MYCALL - - TempScript = malloc(4000); - - strcpy(TempScript, "MARK 1400\r"); - strcat(TempScript, "SPACE 1600\r"); - strcat(TempScript, "SHIFT MODEM\r"); - strcat(TempScript, "INV ON\r"); - strcat(TempScript, "PTERRS 30\r"); // Default Retries - strcat(TempScript, "MAXUSERS 1/10\r"); - strcat(TempScript, TNC->InitScript); - - free(TNC->InitScript); - TNC->InitScript = TempScript; - - // Others go on end so they can't be overriden - - strcat(TNC->InitScript, "ECHO OFF\r"); - strcat(TNC->InitScript, "XMITECHO ON\r"); - strcat(TNC->InitScript, "TXFLOW OFF\r"); - strcat(TNC->InitScript, "XFLOW OFF\r"); - strcat(TNC->InitScript, "TRFLOW OFF\r"); - strcat(TNC->InitScript, "AUTOCR 0\r"); - strcat(TNC->InitScript, "AUTOLF OFF\r"); - strcat(TNC->InitScript, "CRADD OFF\r"); - strcat(TNC->InitScript, "CRSUP OFF\r"); - strcat(TNC->InitScript, "CRSUP OFF/OFF\r"); - strcat(TNC->InitScript, "LFADD OFF/OFF\r"); - strcat(TNC->InitScript, "LFADD OFF\r"); - strcat(TNC->InitScript, "LFSUP OFF/OFF\r"); - strcat(TNC->InitScript, "LFSUP OFF\r"); - strcat(TNC->InitScript, "RING OFF\r"); - strcat(TNC->InitScript, "ARQBBS OFF\r"); - - // Set the ax.25 MYCALL - - - sprintf(msg, "MYCALL %s/%s\r", TNC->NodeCall, TNC->NodeCall); - strcat(TNC->InitScript, msg); - - // look for the MAXUSERS config line, and get the limits - - TNC->InitScript = _strupr(TNC->InitScript); - - ptr = strstr(TNC->InitScript, "MAXUSERS"); - - if (ptr) - { - ptr = strchr(ptr,'/'); // to the separator - if (ptr) - PortEntry->MAXHOSTMODESESSIONS = atoi(++ptr) + 1; - } - - if (PortEntry->MAXHOSTMODESESSIONS > 26) - PortEntry->MAXHOSTMODESESSIONS = 26; - - PortEntry->PORTCONTROL.TNC = TNC; - - TNC->WebWindowProc = WebProc; - TNC->WebWinX = 510; - TNC->WebWinY = 280; - - TNC->WEB_COMMSSTATE = zalloc(100); - TNC->WEB_TNCSTATE = zalloc(100); - strcpy(TNC->WEB_TNCSTATE, "Free"); - TNC->WEB_MODE = zalloc(100); - TNC->WEB_TRAFFIC = zalloc(100); - TNC->WEB_BUFFERS = zalloc(100); - TNC->WEB_STATE = zalloc(100); - TNC->WEB_TXRX = zalloc(100); - TNC->WebBuffer = zalloc(5000); - -#ifndef LINBPQ - - CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 500, ForcedClose); - - - CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Free Space", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); - TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | - LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, - 0,RigControlRow + 44,250,300, TNC->hDlg, NULL, hInstance, NULL); - - TNC->ClientHeight = 500; - TNC->ClientWidth = 500; - - MoveWindows(TNC); -#endif - OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); - - WritetoConsole("\n"); - - return ExtProc; -} - - - -void CheckRXKAM(struct TNCINFO * TNC) -{ - int Length, Len; - char debug[512] = "RX: "; - - // only try to read number of bytes in queue - - if (TNC->RXLen == 500) - TNC->RXLen = 0; - - Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); - - if (Len == 0) - return; - - TNC->RXLen += Len; - - memcpy(&debug[4], TNC->RXBuffer, TNC->RXLen); - debug[TNC->RXLen + 4] = 0; - WriteLogLine(TNC->Port, debug, TNC->RXLen + 4); - - Length = TNC->RXLen; - - // If first char != FEND, then probably a Terminal Mode Frame. Wait for CR on end - - if (TNC->RXBuffer[0] != FEND) - { - // Char Mode Frame I think we need to see cmd: on end - - // If we think we are in host mode, then to could be noise - just discard. - - if (TNC->HostMode) - { - TNC->RXLen = 0; // Ready for next frame - return; - } - - TNC->RXBuffer[TNC->RXLen] = 0; - -// if (TNC->RXBuffer[TNC->RXLen-2] != ':') - if (strstr(TNC->RXBuffer, "cmd:") == 0) - return; // Wait for rest of frame - - // Complete Char Mode Frame - - TNC->RXLen = 0; // Ready for next frame - - if (TNC->HostMode == 0) - { - // We think TNC is in Terminal Mode - ProcessTermModeResponse(TNC); - return; - } - // We thought it was in Host Mode, but are wrong. - - TNC->HostMode = FALSE; - return; - } - - // Receiving a Host Mode frame - - if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery - { - TNC->RXLen = 0; // Ready for next frame - return; - } - - if (Length < 3) // Minimum Frame Sise - return; - - if (TNC->RXBuffer[Length-1] != FEND) - return; // Wait till we have a full frame - - ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer - - TNC->RXLen = 0; // Ready for next frame - - - return; - -} - -VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len) -{ - UCHAR * FendPtr; - int NewLen; - - // Split into KISS Packets. By far the most likely is a single KISS frame - // so treat as special case - - if (rxbuffer[1] == FEND) // Two FENDS - probably got out of sync - { - rxbuffer++; - Len--; - } - - FendPtr = memchr(&rxbuffer[1], FEND, Len-1); - - if (FendPtr == &rxbuffer[Len-1]) - { - ProcessKHOSTPacket(TNC, &rxbuffer[1], Len - 2); - return; - } - - // Process the first Packet in the buffer - - NewLen = (int)(FendPtr - rxbuffer - 1); - - ProcessKHOSTPacket(TNC, &rxbuffer[1], NewLen); - - // Loop Back - - ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2); - return; - -} - - - -static BOOL WriteCommBlock(struct TNCINFO * TNC) -{ - char debug[512] = "TX: "; - - memcpy(&debug[4], TNC->TXBuffer, TNC->TXLen); - debug[TNC->TXLen + 4] = 0; - - WriteLogLine(TNC->Port, debug, TNC->TXLen + 4); - - WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); - - return TRUE; -} - -VOID KAMPoll(int Port) -{ - struct TNCINFO * TNC = TNCInfo[Port]; - struct STREAMINFO * STREAM; - - UCHAR * Poll = TNC->TXBuffer; - char Status[80]; - int Stream; - - if (TNC->PortRecord == 0) - Stream = 0; - - - // If Pactor Session has just been attached, drop back to cmd mode and set Pactor Call to - // the connecting user's callsign - - for (Stream = 0; Stream <= MaxStreams; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) - { - // New Attach - - STREAM->Attached = TRUE; - - if (Stream == 0) // HF Port - { - int calllen; - UCHAR TXMsg[1000] = "D20"; - int datalen; - char Msg[80]; - - TNC->HFPacket = FALSE; - TNC->TimeInRX = 0; - - calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); - TNC->Streams[0].MyCall[calllen] = 0; - - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? - if (TNC->VeryOldMode) - datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall); - else - datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall); - EncodeAndSend(TNC, TXMsg, datalen); - TNC->InternalCmd = 'M'; - - TNC->NeedPACTOR = 0; // Cancel enter Pactor - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - // Stop Scanning - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Msg); - - } - } - } - - if (TNC->Timeout) - { - TNC->Timeout--; - - if (TNC->Timeout) // Still waiting - return; - - // Timed Out - - if (TNC->HostMode == 0) - { - DoTermModeTimeout(TNC); - return; - } - - // Timed out in host mode - Clear any connection and reinit the TNC - - Debugprintf("KAM PACTOR - Link to TNC Lost"); - TNC->TNCOK = FALSE; - TNC->HostMode = 0; - TNC->ReinitState = 0; - - sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); - SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - for (Stream = 0; Stream <= MaxStreams; Stream++) - { - PMSGWITHLEN buffptr; - - STREAM = &TNC->Streams[Stream]; - - if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected - { - STREAM->Connected = FALSE; // Back to Command Mode - STREAM->ReportDISC = TRUE; // Tell Node - } - - STREAM->FramesQueued = 0; - - while(STREAM->BPQtoPACTOR_Q) - { - buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); - ReleaseBuffer(buffptr); - } - - while(STREAM->PACTORtoBPQ_Q) - { - buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); - ReleaseBuffer(buffptr); - } - } - } - - for (Stream = 0; Stream <= MaxStreams; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - if (STREAM->Attached) - CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); - - } - - // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence - - if (!TNC->HostMode) - { - DoTNCReinit(TNC); - return; - } - - if (TNC->BusyDelay) // Waiting to send connect - { - // Still Busy? - - if (InterlockedCheckBusy(TNC) == 0) - { - // No, so send - - EncodeAndSend(TNC, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd)); - free(TNC->ConnectCmd); - - TNC->Timeout = 50; - TNC->InternalCmd = 'C'; // So we dont send the reply to the user. - STREAM->Connecting = TRUE; - - TNC->Streams[0].Connecting = TRUE; - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - TNC->BusyDelay = 0; - return; - } - else - { - // Wait Longer - - TNC->BusyDelay--; - - if (TNC->BusyDelay == 0) - { - // Timed out - Send Error Response - - PMSGWITHLEN buffptr = GetBuff(); - - if (buffptr == 0) return; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); - - C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); - - free(TNC->ConnectCmd); - - } - } - } - - if (TNC->NeedPACTOR) - { - TNC->NeedPACTOR--; - - if (TNC->NeedPACTOR == 0) - { - int datalen; - UCHAR TXMsg[80] = "D20"; - - if (TNC->VeryOldMode) - datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall); - else - datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall); - EncodeAndSend(TNC, TXMsg, datalen); - - if (TNC->OldMode) - EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen - else - EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen - - TNC->InternalCmd = 'T'; - TNC->Timeout = 50; - TNC->IntCmdDelay--; - - // Restart Scanning - - sprintf(Status, "%d SCANSTART 15", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Status); - - return; - } - } - - for (Stream = 0; Stream <= MaxStreams; Stream++) - { - STREAM = &TNC->Streams[Stream]; - - // If in HF Packet mode, normal flow control doesn't seem to work - // If more that 4 packets sent, send a status poll. and use response to - // reset frames outstanding - - if ((Stream == 0) && (TNC->HFPacket) && (TNC->Streams[0].FramesOutstanding > 4)) - { - EncodeAndSend(TNC, "C10S", 4); - TNC->InternalCmd = 'S'; - TNC->Timeout = 50; - return; - - } - - if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q) - { - int datalen; - UCHAR TXMsg[1000] = "D20"; - PMSGWITHLEN buffptr; - UCHAR * MsgPtr; - char Status[80]; - - if (STREAM->Connected) - { - int Next; - - if (Stream > 0) - sprintf(TXMsg, "D1%c", Stream + '@'); - else if (TNC->HFPacket) - memcpy(TXMsg, "D2A", 3); - else - { - // Pactor - - // Limit amount in TX, so we keep some on the TX Q and don't send turnround too early - - if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200) - continue; - - // Dont send if IRS State - // If in IRS state for too long, force turnround - - if (TNC->TXRXState == 'R') - { - if (TNC->TimeInRX++ > 15) - EncodeAndSend(TNC, "T", 1); // Changeover to ISS - else - goto Poll; - } - TNC->TimeInRX = 0; - } - - buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); - STREAM->FramesQueued--; - - datalen = buffptr->Len; - MsgPtr = buffptr->Data; - - if (TNC->SwallowSignon && Stream == 0) - { - TNC->SwallowSignon = FALSE; - if (strstr(MsgPtr, "Connected")) // Discard *** connected - { - ReleaseBuffer(buffptr); - return; - } - } - - Next = 0; - STREAM->bytesTXed += datalen; - - if (Stream == 0) - { - while (datalen > 100) // Limit Pactor Sends - { - memcpy(&TXMsg[3], &MsgPtr[Next], 100); - EncodeAndSend(TNC, TXMsg, 103); - Next += 100; - datalen -= 100; - - WritetoTrace(TNC, &TXMsg[3], 100); - } - } - - memcpy(&TXMsg[3], &MsgPtr[Next], datalen); - EncodeAndSend(TNC, TXMsg, datalen + 3); - - WritetoTrace(TNC, &TXMsg[3], datalen); - - ReleaseBuffer(buffptr); - - if (Stream == 0) - { - sprintf(Status, "RX %d TX %d ACKED %d ", - TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); - SetWindowText(TNC->xIDC_TRAFFIC, Status); - - if ((TNC->HFPacket == 0) && (TNC->Streams[0].BPQtoPACTOR_Q == 0)) // Nothing following - { - EncodeAndSend(TNC, "E", 1); // Changeover when all sent - } - } - - if (STREAM->Disconnecting) - { - Debugprintf("Send with Disc Pending, Q = %x", STREAM->BPQtoPACTOR_Q); - if (STREAM->BPQtoPACTOR_Q == 0) // All Sent - - // KAM doesnt have a tidy close! - - STREAM->DisconnectingTimeout = 100; // Give 5 secs to get to other end - } - return; - } - else // Not Connected - { - buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); - datalen = buffptr->Len; - MsgPtr = buffptr->Data; - - // Command. Do some sanity checking and look for things to process locally - - datalen--; // Exclude CR - MsgPtr[datalen] = 0; // Null Terminate - _strupr(MsgPtr); - - if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0) - { - sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); - if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40])) - { - ReleaseBuffer(buffptr); - } - else - { - buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - return; - } - - if (_memicmp(MsgPtr, "D\r", 2) == 0) - { - STREAM->ReportDISC = TRUE; // Tell Node - return; - } - - if ((Stream == 0) && memcmp(MsgPtr, "HFPACKET", 8) == 0) - { - TNC->HFPacket = TRUE; - buffptr->Len = sprintf(buffptr->Data, "KAM} OK\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - return; - } - - if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect - { - memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); - STREAM->Connecting = TRUE; - - // If Stream 0, Convert C CALL to PACTOR CALL - - if (Stream == 0) - { - if (TNC->HFPacket) - datalen = sprintf(TXMsg, "C2AC %s", TNC->Streams[0].RemoteCall); - else - datalen = sprintf(TXMsg, "C20PACTOR %s", TNC->Streams[0].RemoteCall); - - // If Pactor, check busy detecters on any interlocked ports - - if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) - { - // Channel Busy. Wait - - TNC->ConnectCmd = _strdup(TXMsg); - - sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - TNC->BusyDelay = TNC->BusyWait * 10; - - return; - } - - TNC->OverrideBusy = FALSE; - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", - TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - } - else - datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); - - EncodeAndSend(TNC, TXMsg, datalen); - TNC->Timeout = 50; - TNC->InternalCmd = 'C'; // So we dont send the reply to the user. - ReleaseBuffer(buffptr); - STREAM->Connecting = TRUE; - - return; - } - - if (memcmp(MsgPtr, "GTOR ", 5) == 0) // GTOR Connect - { - memcpy(STREAM->RemoteCall, &MsgPtr[5], 9); - STREAM->Connecting = TRUE; - - // If Stream 0, Convert C CALL to PACTOR CALL - - if (Stream == 0) - { - datalen = sprintf(TXMsg, "C20GTOR %s", TNC->Streams[0].RemoteCall); - - // If Pactor, check busy detecters on any interlocked ports - - if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) - { - // Channel Busy. Wait - - TNC->ConnectCmd = _strdup(TXMsg); - - sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - TNC->BusyDelay = TNC->BusyWait * 10; - - return; - } - - TNC->OverrideBusy = FALSE; - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", - TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - } - else - datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); - - EncodeAndSend(TNC, TXMsg, datalen); - TNC->Timeout = 50; - TNC->InternalCmd = 'C'; // So we dont send the reply to the user. - ReleaseBuffer(buffptr); - STREAM->Connecting = TRUE; - - return; - } - - if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect - { - if (Stream == 0) - { - if (TNC->HFPacket) - EncodeAndSend(TNC, "C2AD", 4); // ??Return to packet mode?? - else - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? - - TNC->NeedPACTOR = 50; - } - else - { - sprintf(TXMsg, "C1%cD", Stream + '@'); - EncodeAndSend(TNC, TXMsg, 4); - TNC->CmdStream = Stream; - TNC->Timeout = 50; - } - - TNC->Timeout = 0; // Don't expect a response - STREAM->Connecting = FALSE; - STREAM->ReportDISC = TRUE; - ReleaseBuffer(buffptr); - - return; - } - - // Other Command ?? - - if (Stream > 0) - datalen = sprintf(TXMsg, "C1%c%s", Stream + '@', MsgPtr); - else - datalen = sprintf(TXMsg, "C20%s", MsgPtr); - EncodeAndSend(TNC, TXMsg, datalen); - ReleaseBuffer(buffptr); - TNC->Timeout = 50; - TNC->InternalCmd = 0; - TNC->CmdStream = Stream; - - } - } - } - -Poll: - - // Need to poll data and control channel (for responses to commands) - - // Also check status if we have data buffered (for flow control) - - if (TNC->TNCOK) - { - if (TNC->IntCmdDelay == 50) - { - EncodeAndSend(TNC, "C10S", 4); - TNC->InternalCmd = 'S'; - TNC->Timeout = 50; - TNC->IntCmdDelay--; - return; - } - - if (TNC->IntCmdDelay <=0) - { - if (TNC->VeryOldMode == FALSE) - { - EncodeAndSend(TNC, "?", 1); - TNC->InternalCmd = '?'; - TNC->Timeout = 50; - } - TNC->IntCmdDelay = 100; // Every 30 - return; - } - else - TNC->IntCmdDelay--; - } - - return; - -} - -static VOID DoTNCReinit(struct TNCINFO * TNC) -{ - UCHAR * Poll = TNC->TXBuffer; - - if (TNC->ReinitState == 1) // Forcing back to Term - TNC->ReinitState = 0; // Got Response, so must be back in term mode - - if (TNC->ReinitState == 0) - { - // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode - - sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); - SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - Poll[0] = 13; - TNC->TXLen = 1; - - WriteCommBlock(TNC); - TNC->Timeout = 50; - - return; - } - - if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands - { - char * start, * end; - int len; - - start = TNC->InitPtr; - - if (*(start) == 0) // End of Script - { - // Put into Host Mode - - memcpy(Poll, "INTFACE HOST\r", 13); - - TNC->TXLen = 13; - WriteCommBlock(TNC); - TNC->Timeout = 50; - - TNC->ReinitState = 4; // Need Reset - - return; - } - - end = strchr(start, 13); - len = (int)(++end - start); - TNC->InitPtr = end; - memcpy(Poll, start, len); - - TNC->TXLen = len; - WriteCommBlock(TNC); - TNC->Timeout = 50; - - return; - - } -} - -static VOID DoTermModeTimeout(struct TNCINFO * TNC) -{ - UCHAR * Poll = TNC->TXBuffer; - - if (TNC->ReinitState == 0) - { - //Checking if in Terminal Mode - Try to set back to Term Mode - - TNC->ReinitState = 1; - Poll[0] = 3; - Poll[1] = 0x58; // ?? Back to cmd: mode ?? - TNC->TXLen = 2; - - Poll[0] = 0xc0; - Poll[1] = 'Q'; // ?? Back to cmd: mode ?? - Poll[2] = 0xc0; - TNC->TXLen = 3; - - WriteCommBlock(TNC); - - return; - } - if (TNC->ReinitState == 1) - { - // Forcing back to Term Mode - - TNC->ReinitState = 0; - DoTNCReinit(TNC); // See if worked - return; - } - - if (TNC->ReinitState == 3) - { - // Entering Host Mode - - // Assume ok - - TNC->HostMode = TRUE; - return; - } -} - - - -static VOID ProcessTermModeResponse(struct TNCINFO * TNC) -{ - UCHAR * Poll = TNC->TXBuffer; - - if (TNC->ReinitState == 0 || TNC->ReinitState == 1) - { - // Testing if in Term Mode. It is, so can now send Init Commands - - TNC->InitPtr = TNC->InitScript; - TNC->ReinitState = 2; - DoTNCReinit(TNC); // Send First Command - return; - } - if (TNC->ReinitState == 2) - { - // Sending Init Commands - - DoTNCReinit(TNC); // Send Next Command - return; - } - - if (TNC->ReinitState == 4) // Send INTFACE, Need RESET - { - TNC->ReinitState = 5; - - memcpy(Poll, "RESET\r", 6); - - TNC->TXLen = 6; - WriteCommBlock(TNC); - TNC->Timeout = 50; - TNC->HostMode = TRUE; // Should now be in Host Mode - TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs - - return; - } - - if (TNC->ReinitState == 5) // RESET sent - { - TNC->ReinitState = 5; - - return; - } - - - -} - -VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len) -{ - PMSGWITHLEN buffptr; - char * Buffer = &Msg[3]; // Data portion of frame - char * Call; - char Status[80]; - int Stream = 0; - struct STREAMINFO * STREAM; - - // Any valid frame is an ACK - - TNC->TNCOK = TRUE; - - Len = KissDecode(Msg, Msg, Len); // Remove KISS transparency - - if (Msg[1] == '0' && Msg[2] == '0') - Stream = 0; - else - if (Msg[1] == '2') Stream = 0; else Stream = Msg[2] - '@'; - - STREAM = &TNC->Streams[Stream]; - - // See if Poll Reply or Data - - Msg[Len] = 0; // Terminate - - if (Msg[0] == 'M') // Monitor - { - DoMonitor(TNC, Msg, Len); - return; - } - - - if (Msg[0] == 'E') // Data Echo - { - if (Msg[1] == '2') // HF Port - { - if (TNC->Streams[0].bytesTXed) - TNC->Streams[0].BytesAcked += Len - 3; // We get an ack before the first send - - sprintf(Status, "RX %d TX %d ACKED %d ", - TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); - SetWindowText(TNC->xIDC_TRAFFIC, Status); - - if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked < 500) - TNC->Streams[0].FramesOutstanding = 0; - } - return; - } - - if (Msg[0] == 'D') // Data - { - // Pass to Appl - - buffptr = GetBuff(); - if (buffptr == NULL) return; // No buffers, so ignore - - Len-=3; // Remove Header - - buffptr->Len = Len; // Length - STREAM->bytesRXed += Len; - memcpy(buffptr->Data, Buffer, Len); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - if (Stream == 0) - { - sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", - TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); - SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); - } - - WritetoTrace(TNC, Buffer, Len); - - return; - } - - - if (Msg[0] == 'C') // Command Reponse - { - TNC->Timeout = 0; - - // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc - - // See if a response to internal command - - if (TNC->InternalCmd) - { - // Process it - - if (TNC->InternalCmd == 'S') // Status - { - char * Line; - char * ptr; - - // Message is line giving free bytes, followed by a line for each active (packet) stream - - // FREE BYTES 1366/5094 - // A/2 #1145(12) CONNECTED to KE7XO-3 - // S/2 CONNECTED to NLV - - // each line is teminated by CR, and by the time it gets here it is null terminated - - //FREE BYTES 2628 - //A/H #80(1) CONNECTED to DK0MNL.. - - if (TNC->HFPacket) - TNC->Streams[0].FramesOutstanding = 0; - - Line = strchr(&Msg[3], 13); - if (Line == 0) return; - - *(Line) = 0; - - ptr = strchr(&Msg[13], '/'); - TNC->Mem1 = atoi(&Msg[13]); - if (ptr) - TNC->Mem2 = atoi(++ptr); - else - TNC->Mem2 = 0; - - SetWindowText(TNC->xIDC_BUFFERS, &Msg[14]); - strcpy(TNC->WEB_BUFFERS, &Msg[14]); - - while (Line[1] != 0) // End of stream - { - Stream = Line[1] - '@'; - STREAM = &TNC->Streams[Stream]; - - if (Line[5] == '#') - { - STREAM->BytesOutstanding = atoi(&Line[6]); - ptr = strchr(&Line[6], '('); - if (ptr) - STREAM->FramesOutstanding = atoi(++ptr); - } - else - { - STREAM->BytesOutstanding = 0; - STREAM->FramesOutstanding = 0; - } - - Line = strchr(&Line[1], 13); - } - return; - } - return; - } - - - WritetoTrace(TNC, Buffer, Len); - - - // Pass to Appl - - Stream = TNC->CmdStream; - - - buffptr = GetBuff(); - - if (buffptr == NULL) return; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data,"KAM} %s", Buffer); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - return; - } - - if (Msg[0] == 'I') // ISS/IRS State - { - if (Msg[2] == '1') - { - strcpy(TNC->WEB_TXRX, "Sender"); - SetWindowText(TNC->xIDC_TXRX, "Sender"); - TNC->TXRXState = 'S'; - } - else - { - strcpy(TNC->WEB_TXRX, "Receiver"); - SetWindowText(TNC->xIDC_TXRX, "Receiver"); - TNC->TXRXState = 'R'; - } - return; - } - - if (Msg[0] == '?') // Status - { - TNC->Timeout = 0; - return; - } - - if (Msg[0] == 'S') // Status - { - if (Len < 4) - { - // Reset Response FEND FEND S00 FEND - - TNC->Timeout = 0; - - sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); - SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - return; - } - - // Pass to Appl - - if (strstr(Buffer, "STANDBY>") || strstr(Buffer, "*** DISCONNECTED")) - { - if ((STREAM->Connecting | STREAM->Connected) == 0) - { - // Not connected or Connecting. Probably response to going into Pactor Listen Mode - - return; - } - - if (STREAM->Connecting && STREAM->Disconnecting == FALSE) - { - // Connect Failed - - buffptr = GetBuff(); - if (buffptr == 0) return; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - STREAM->Connecting = FALSE; - STREAM->Connected = FALSE; // In case! - STREAM->FramesOutstanding = 0; - - return; - } - - // Must Have been connected or disconnecting - Release Session - - STREAM->Connecting = FALSE; - STREAM->Connected = FALSE; // Back to Command Mode - STREAM->FramesOutstanding = 0; - - if (STREAM->Disconnecting == FALSE) - STREAM->ReportDISC = TRUE; // Tell Node - - STREAM->Disconnecting = FALSE; - - if (Stream == 0) - { - // Need to reset Pactor Call in case it was changed - - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? - TNC->NeedPACTOR = 20; - } - - return; - } - - if (Msg[2] == '0') - Call = strstr(Buffer, "HFPacket == 0) - { - Buffer[Len-4] = 0; - } - - STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0; - STREAM->ConnectTime = time(NULL); - - if (Stream == 0) - { - // Stop Scanner - - char Msg[80]; - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Msg); - - sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", - TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); - - SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); - } - - if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) - { - // Incoming Connect - - TRANSPORTENTRY * SESS; - - if (Msg[1] == '2' && Msg[2] == 'A') - TNC->HFPacket = TRUE; - - // Stop other ports in same group - - SuspendOtherPorts(TNC); - - ProcessIncommingConnect(TNC, Call, Stream, TRUE); - - SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; - - if (Stream == 0) - { - struct WL2KInfo * WL2K = TNC->WL2K; - char FreqAppl[10] = ""; // Frequecy-specific application - - if (TNC->RIG && TNC->RIG != &TNC->DummyRig) - { - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->NodeCall, TNC->RIG->Valchar); - SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq - - // If Scan Entry has a Appl, save it - - if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0]) - strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]); - } - else - { - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall); - - if (WL2K) - { - SESS->Frequency = WL2K->Freq; - } - } - - SESS->Mode = 11; // P1 - - if (WL2K) - strcpy(SESS->RMSCall, WL2K->RMSCall); - - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - EncodeAndSend(TNC, "T", 1); // Changeover to ISS - - // If an autoconnect APPL is defined, send it - - if (FreqAppl[0]) // Frequency spcific APPL overrides TNC APPL - { - buffptr = GetBuff(); - if (buffptr == 0) return; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl); - C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); - TNC->SwallowSignon = TRUE; - return; - } - - if (TNC->ApplCmd) - { - buffptr = GetBuff(); - if (buffptr == 0) return; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data, "%s\r", TNC->ApplCmd); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - TNC->SwallowSignon = TRUE; - return; - } - } - - if (FULL_CTEXT && HFCTEXTLEN == 0) - { - char CTBuff[300] = "D20"; - int Len = CTEXTLEN, CTPaclen = 50; - int Next = 0; - - if (Stream > 0) - sprintf(CTBuff, "D1%c", Stream + '@'); - - while (Len > CTPaclen) // CTEXT Paclen - { - memcpy(&CTBuff[3], &CTEXTMSG[Next], CTPaclen); - EncodeAndSend(TNC, CTBuff, CTPaclen + 3); - Next += CTPaclen; - Len -= CTPaclen; - } - - memcpy(&CTBuff[3], &CTEXTMSG[Next], Len); - EncodeAndSend(TNC, CTBuff, Len + 3); - EncodeAndSend(TNC, "E", 1); // Changeover when all sent - TNC->Streams[0].bytesTXed += CTEXTLEN; - } - return; - - } - else - { - // Connect Complete - - buffptr = GetBuff(); - if (buffptr == 0) return; // No buffers, so ignore - - buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", Call);; - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - STREAM->Connecting = FALSE; - STREAM->Connected = TRUE; // Subsequent data to data channel - - - if (Stream == 0) - { - if (TNC->RIG) - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); - else - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); - - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - UpdateMH(TNC, Call, '+', 'O'); - - } - - return; - } - } - } -} - - -static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) -{ - int i,txptr=0; - UCHAR c; - - for (i=0;iTXLen = KissEncode(txbuffer, TNC->TXBuffer, Len); - WriteCommBlock(TNC); -} - -static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len) -{ - int i,txptr=0; - UCHAR c; - - outbuff[0]=FEND; - txptr=1; - - for (i=0;i'); - - if (ptr) *(ptr) = 0; - - UpdateMH(TNC, &Msg[3], ' ', 0); - -} -VOID TidyClose(struct TNCINFO * TNC, int Stream) -{ - if (Stream == 0) // Pactor Stream - { - TNC->TimeInRX = 0; - if (TNC->HFPacket) - EncodeAndSend(TNC, "C2AD", 4); // Disconnect - else - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? -// EncodeAndSend(TNC, "C20TOR", 6); // Disconnect - - TNC->HFPacket = FALSE; - } - else - { - UCHAR TXMsg[10]; - - sprintf(TXMsg, "C1%cD", Stream + '@'); - EncodeAndSend(TNC, TXMsg, 4); - TNC->Timeout = 50; - } -} - -VOID ForcedClose(struct TNCINFO * TNC, int Stream) -{ - if (Stream == 0) // Pactor Stream - { - TNC->TimeInRX = 0; - - if (TNC->HFPacket) - EncodeAndSend(TNC, "C2AD", 4); // Disconnect - else - EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? - - TNC->HFPacket = FALSE; - } - else - { - UCHAR TXMsg[10]; - - sprintf(TXMsg, "C1%cD", Stream + '@'); - EncodeAndSend(TNC, TXMsg, 4); // Send twice - must force a disconnect - TNC->Timeout = 50; - }} - -VOID CloseComplete(struct TNCINFO * TNC, int Stream) -{ - sprintf(TNC->WEB_TNCSTATE, "Free"); - SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - ReleaseOtherPorts(TNC); - TNC->NeedPACTOR = 50; -} - - - -/* - - -MARK 1400 -SPACE 1600 -SHIFT MODEM -INV ON -MAXUSERS 1/10 -PTERRS 30 - - // Others go on end so they can't be overriden - -ECHO OFF -XMITECHO ON -TXFLOW OFF -XFLOW OFF -TRFLOW OFF -AUTOCR 0 -AUTOLF OFF -CRADD OFF -CRSUP OFF -CRSUP OFF/OFF -LFADD OFF/OFF -LFADD OFF -LFSUP OFF/OFF -LFSUP OFF -RING OFF -ARQBBS OFF - -MYCALL - -*/ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface KAM TNC in Pactor Mode to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +// Version 1.2.1.2 July 2010 + +// Send Change to ISS before each transmission +// Support up to 32 BPQ Ports + +// Version 1.2.1.3 August 2010 + +// Drop RTS as well as DTR on close + +// Version 1.2.1.4 August 2010 + +// Save Minimized State + +// Version 1.2.1.5 September 2010 + +// Fix Freq Display after Node reconfig +// Only use AutoConnect APPL for Pactor Connects + +// Version 1.2.2.1 September 2010 + +// Add option to get config from bpq32.cfg + +//#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#include "cheaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define NARROWMODE Report_P1 +#define WIDEMODE Report_P1 // Only supports PI + +static char ClassName[]="KAMPACTORSTATUS"; +static char WindowTitle[] = "KAM Pactor"; +static int RigControlRow = 165; + +extern UCHAR LogDirectory[]; +static RECT Rect; + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + +static FILE * LogHandle[32] = {0}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +static char BaseDir[MAX_PATH]="c:\\"; + +static BOOL WRITELOG = FALSE; + +BOOL KAMStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + if (TNC->hDevice) + { + CloseCOMPort(TNC->hDevice); + TNC->hDevice = 0; + } + + TNC->HostMode = FALSE; + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + +BOOL KAMStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->ReopenTimer = 999; + TNC->ReinitState = 0; + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + + +static VOID CloseLogFile(int Flags) +{ + if (WRITELOG) + { + fclose(LogHandle[Flags]); + LogHandle[Flags] = NULL; + } +} + +static BOOL OpenLogFile(int Flags) +{ + if (WRITELOG) + { + UCHAR FN[MAX_PATH]; + + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s/logs/KAMLog_%02d%02d_%d.txt", LogDirectory, tm->tm_mon + 1, tm->tm_mday, Flags); + + LogHandle[Flags] = fopen(FN, "ab"); + + return (LogHandle[Flags] != NULL); + } + return 0; +} + +static void WriteLogLine(int Port, char * Msg, int MsgLen) +{ + if (WRITELOG) + { + OpenLogFile(Port); + + if (LogHandle[Port]) + { + fwrite(Msg, 1, MsgLen, LogHandle[Port]); + fwrite("\r\n", 1, 2, LogHandle[Port]); + } + CloseLogFile(Port); + } +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + goto ConfigLine; + + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log + WRITELOG = atoi(&buf[9]); + + else if (_memicmp(buf, "APPL", 4) == 0) + { + p_cmd = strtok(&buf[5], " \t\n\r"); + + if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(_strupr(p_cmd)); + } + + else if (_memicmp(buf, "OLDMODE", 7) == 0) + TNC->OldMode = TRUE; + + else if (_memicmp(buf, "VERYOLDMODE", 7) == 0) + { + TNC->OldMode = TRUE; + TNC->VeryOldMode = TRUE; + } + + else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + + else if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + + else + strcat (TNC->InitScript, buf); + } + return (TRUE); +} + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +static int MaxStreams = 26; + +BOOL CloseConnection(struct TNCINFO * conn); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +void CheckRXKAM(struct TNCINFO * TNC); +VOID KAMPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +static VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); + +VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +// Note that Kantronics host Mode uses KISS format Packets (without a KISS COntrol Byte) + +VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int txlen; + PMSGWITHLEN buffptr; + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + + int Stream; + + size_t Param; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; + + if (TNC->hDevice == 0) + { + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + // Try to reopen every 30 secs + + if (fn > 3 && fn < 7) + goto ok; + + TNC->ReopenTimer++; + + if (TNC->ReopenTimer < 300) + return 0; + + TNC->ReopenTimer = 0; + + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + if (TNC->hDevice == 0) + return 0; + } + +ok: + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + CheckRXKAM(TNC); + KAMPoll(port); + + return 0; + + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; + buff->PID= 0xf0; + memcpy(buff->L2DATA, buffptr->Data, datalen); // Data goes to +7, but we have an extra byte + datalen += (MSGHDDRLEN + 1); + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + STREAM = &TNC->Streams[Stream]; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + if(STREAM->Connected) + { + STREAM->FramesOutstanding++; + STREAM->FramesQueued++; + + STREAM->BytesOutstanding += txlen; + } + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + if (TNC->HFPacket) + { + if (TNC->Mem1 < 2000 || TNC->Streams[0].FramesOutstanding > 4) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); + + } + else + { + if (TNC->Streams[0].FramesQueued > 4) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); + } + } + else + { + if (STREAM->FramesOutstanding > 3 || STREAM->BytesOutstanding > 500 || TNC->Mem1 < 500) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); + } + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + EncodeAndSend(TNC, "Q", 1); // Exit Host Mode + Sleep(50); + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + // Use P0 to Disable Pactor, P1 to enable + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + return 1; // OK to change + } + + if (!TNC->HostMode) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + return 0; // OK to Change // Dont want to mess with disabling it + + if (Param == 3) // Release Permission + return 0; + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->PMaxLevel >= '0' && Scan->PMaxLevel < '5') // 1 - 4 + { + if (TNC->Bandwidth != Scan->PMaxLevel) + { + // Enable or disable by setting mycall + + TNC->Bandwidth = Scan->PMaxLevel; + + if (Scan->PMaxLevel == '0') + { + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? + return 0; + } + if (TNC->OldMode) + EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen + else + EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen + + TNC->InternalCmd = 'T'; + } + } + } + return 0; +} + +VOID KAMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// STREAM->CmdSet = STREAM->CmdSave = zalloc(100); +// sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects + +} + +VOID KAMReleasePort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "KAM Pactor Status

KAM Pactor Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_BUFFERS); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Free Space%s
Traffic%s
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +void * KAMExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + char * TempScript; + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + sprintf(msg,"KAM Pactor %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KAM; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + PortEntry->MAXHOSTMODESESSIONS = 11; // Default + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // No Scan Control + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + +// TNC->SuspendPortProc = KAMSuspendPort; +// TNC->ReleasePortProc = KAMReleasePort; + + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + TempScript = malloc(4000); + + strcpy(TempScript, "MARK 1400\r"); + strcat(TempScript, "SPACE 1600\r"); + strcat(TempScript, "SHIFT MODEM\r"); + strcat(TempScript, "INV ON\r"); + strcat(TempScript, "PTERRS 30\r"); // Default Retries + strcat(TempScript, "MAXUSERS 1/10\r"); + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "ECHO OFF\r"); + strcat(TNC->InitScript, "XMITECHO ON\r"); + strcat(TNC->InitScript, "TXFLOW OFF\r"); + strcat(TNC->InitScript, "XFLOW OFF\r"); + strcat(TNC->InitScript, "TRFLOW OFF\r"); + strcat(TNC->InitScript, "AUTOCR 0\r"); + strcat(TNC->InitScript, "AUTOLF OFF\r"); + strcat(TNC->InitScript, "CRADD OFF\r"); + strcat(TNC->InitScript, "CRSUP OFF\r"); + strcat(TNC->InitScript, "CRSUP OFF/OFF\r"); + strcat(TNC->InitScript, "LFADD OFF/OFF\r"); + strcat(TNC->InitScript, "LFADD OFF\r"); + strcat(TNC->InitScript, "LFSUP OFF/OFF\r"); + strcat(TNC->InitScript, "LFSUP OFF\r"); + strcat(TNC->InitScript, "RING OFF\r"); + strcat(TNC->InitScript, "ARQBBS OFF\r"); + + // Set the ax.25 MYCALL + + + sprintf(msg, "MYCALL %s/%s\r", TNC->NodeCall, TNC->NodeCall); + strcat(TNC->InitScript, msg); + + // look for the MAXUSERS config line, and get the limits + + TNC->InitScript = _strupr(TNC->InitScript); + + ptr = strstr(TNC->InitScript, "MAXUSERS"); + + if (ptr) + { + ptr = strchr(ptr,'/'); // to the separator + if (ptr) + PortEntry->MAXHOSTMODESESSIONS = atoi(++ptr) + 1; + } + + if (PortEntry->MAXHOSTMODESESSIONS > 26) + PortEntry->MAXHOSTMODESESSIONS = 26; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WebBuffer = zalloc(5000); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 500, ForcedClose); + + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Free Space", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,RigControlRow + 44,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 500; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + WritetoConsole("\n"); + + return ExtProc; +} + + + +void CheckRXKAM(struct TNCINFO * TNC) +{ + int Length, Len; + char debug[512] = "RX: "; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + memcpy(&debug[4], TNC->RXBuffer, TNC->RXLen); + debug[TNC->RXLen + 4] = 0; + WriteLogLine(TNC->Port, debug, TNC->RXLen + 4); + + Length = TNC->RXLen; + + // If first char != FEND, then probably a Terminal Mode Frame. Wait for CR on end + + if (TNC->RXBuffer[0] != FEND) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->RXBuffer[TNC->RXLen-2] != ':') + if (strstr(TNC->RXBuffer, "cmd:") == 0) + return; // Wait for rest of frame + + // Complete Char Mode Frame + + TNC->RXLen = 0; // Ready for next frame + + if (TNC->HostMode == 0) + { + // We think TNC is in Terminal Mode + ProcessTermModeResponse(TNC); + return; + } + // We thought it was in Host Mode, but are wrong. + + TNC->HostMode = FALSE; + return; + } + + // Receiving a Host Mode frame + + if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + if (Length < 3) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[Length-1] != FEND) + return; // Wait till we have a full frame + + ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer + + TNC->RXLen = 0; // Ready for next frame + + + return; + +} + +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + int NewLen; + + // Split into KISS Packets. By far the most likely is a single KISS frame + // so treat as special case + + if (rxbuffer[1] == FEND) // Two FENDS - probably got out of sync + { + rxbuffer++; + Len--; + } + + FendPtr = memchr(&rxbuffer[1], FEND, Len-1); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessKHOSTPacket(TNC, &rxbuffer[1], Len - 2); + return; + } + + // Process the first Packet in the buffer + + NewLen = (int)(FendPtr - rxbuffer - 1); + + ProcessKHOSTPacket(TNC, &rxbuffer[1], NewLen); + + // Loop Back + + ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2); + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + char debug[512] = "TX: "; + + memcpy(&debug[4], TNC->TXBuffer, TNC->TXLen); + debug[TNC->TXLen + 4] = 0; + + WriteLogLine(TNC->Port, debug, TNC->TXLen + 4); + + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return TRUE; +} + +VOID KAMPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + int Stream; + + if (TNC->PortRecord == 0) + Stream = 0; + + + // If Pactor Session has just been attached, drop back to cmd mode and set Pactor Call to + // the connecting user's callsign + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + STREAM->Attached = TRUE; + + if (Stream == 0) // HF Port + { + int calllen; + UCHAR TXMsg[1000] = "D20"; + int datalen; + char Msg[80]; + + TNC->HFPacket = FALSE; + TNC->TimeInRX = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? + if (TNC->VeryOldMode) + datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall); + else + datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall); + EncodeAndSend(TNC, TXMsg, datalen); + TNC->InternalCmd = 'M'; + + TNC->NeedPACTOR = 0; // Cancel enter Pactor + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + if (TNC->HostMode == 0) + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + Debugprintf("KAM PACTOR - Link to TNC Lost"); + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + TNC->ReinitState = 0; + + sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + PMSGWITHLEN buffptr; + + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + + STREAM->FramesQueued = 0; + + while(STREAM->BPQtoPACTOR_Q) + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + } + + while(STREAM->PACTORtoBPQ_Q) + { + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + ReleaseBuffer(buffptr); + } + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->BusyDelay) // Waiting to send connect + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == 0) + { + // No, so send + + EncodeAndSend(TNC, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd)); + free(TNC->ConnectCmd); + + TNC->Timeout = 50; + TNC->InternalCmd = 'C'; // So we dont send the reply to the user. + STREAM->Connecting = TRUE; + + TNC->Streams[0].Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = 0; + return; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + free(TNC->ConnectCmd); + + } + } + } + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + UCHAR TXMsg[80] = "D20"; + + if (TNC->VeryOldMode) + datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall); + else + datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall); + EncodeAndSend(TNC, TXMsg, datalen); + + if (TNC->OldMode) + EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen + else + EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen + + TNC->InternalCmd = 'T'; + TNC->Timeout = 50; + TNC->IntCmdDelay--; + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Status); + + return; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // If in HF Packet mode, normal flow control doesn't seem to work + // If more that 4 packets sent, send a status poll. and use response to + // reset frames outstanding + + if ((Stream == 0) && (TNC->HFPacket) && (TNC->Streams[0].FramesOutstanding > 4)) + { + EncodeAndSend(TNC, "C10S", 4); + TNC->InternalCmd = 'S'; + TNC->Timeout = 50; + return; + + } + + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q) + { + int datalen; + UCHAR TXMsg[1000] = "D20"; + PMSGWITHLEN buffptr; + UCHAR * MsgPtr; + char Status[80]; + + if (STREAM->Connected) + { + int Next; + + if (Stream > 0) + sprintf(TXMsg, "D1%c", Stream + '@'); + else if (TNC->HFPacket) + memcpy(TXMsg, "D2A", 3); + else + { + // Pactor + + // Limit amount in TX, so we keep some on the TX Q and don't send turnround too early + + if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200) + continue; + + // Dont send if IRS State + // If in IRS state for too long, force turnround + + if (TNC->TXRXState == 'R') + { + if (TNC->TimeInRX++ > 15) + EncodeAndSend(TNC, "T", 1); // Changeover to ISS + else + goto Poll; + } + TNC->TimeInRX = 0; + } + + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + datalen = buffptr->Len; + MsgPtr = buffptr->Data; + + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Next = 0; + STREAM->bytesTXed += datalen; + + if (Stream == 0) + { + while (datalen > 100) // Limit Pactor Sends + { + memcpy(&TXMsg[3], &MsgPtr[Next], 100); + EncodeAndSend(TNC, TXMsg, 103); + Next += 100; + datalen -= 100; + + WritetoTrace(TNC, &TXMsg[3], 100); + } + } + + memcpy(&TXMsg[3], &MsgPtr[Next], datalen); + EncodeAndSend(TNC, TXMsg, datalen + 3); + + WritetoTrace(TNC, &TXMsg[3], datalen); + + ReleaseBuffer(buffptr); + + if (Stream == 0) + { + sprintf(Status, "RX %d TX %d ACKED %d ", + TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); + SetWindowText(TNC->xIDC_TRAFFIC, Status); + + if ((TNC->HFPacket == 0) && (TNC->Streams[0].BPQtoPACTOR_Q == 0)) // Nothing following + { + EncodeAndSend(TNC, "E", 1); // Changeover when all sent + } + } + + if (STREAM->Disconnecting) + { + Debugprintf("Send with Disc Pending, Q = %x", STREAM->BPQtoPACTOR_Q); + if (STREAM->BPQtoPACTOR_Q == 0) // All Sent + + // KAM doesnt have a tidy close! + + STREAM->DisconnectingTimeout = 100; // Give 5 secs to get to other end + } + return; + } + else // Not Connected + { + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + datalen = buffptr->Len; + MsgPtr = buffptr->Data; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (_memicmp(MsgPtr, "D\r", 2) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return; + } + + if ((Stream == 0) && memcmp(MsgPtr, "HFPACKET", 8) == 0) + { + TNC->HFPacket = TRUE; + buffptr->Len = sprintf(buffptr->Data, "KAM} OK\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + STREAM->Connecting = TRUE; + + // If Stream 0, Convert C CALL to PACTOR CALL + + if (Stream == 0) + { + if (TNC->HFPacket) + datalen = sprintf(TXMsg, "C2AC %s", TNC->Streams[0].RemoteCall); + else + datalen = sprintf(TXMsg, "C20PACTOR %s", TNC->Streams[0].RemoteCall); + + // If Pactor, check busy detecters on any interlocked ports + + if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) + { + // Channel Busy. Wait + + TNC->ConnectCmd = _strdup(TXMsg); + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; + + return; + } + + TNC->OverrideBusy = FALSE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", + TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); + + EncodeAndSend(TNC, TXMsg, datalen); + TNC->Timeout = 50; + TNC->InternalCmd = 'C'; // So we dont send the reply to the user. + ReleaseBuffer(buffptr); + STREAM->Connecting = TRUE; + + return; + } + + if (memcmp(MsgPtr, "GTOR ", 5) == 0) // GTOR Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[5], 9); + STREAM->Connecting = TRUE; + + // If Stream 0, Convert C CALL to PACTOR CALL + + if (Stream == 0) + { + datalen = sprintf(TXMsg, "C20GTOR %s", TNC->Streams[0].RemoteCall); + + // If Pactor, check busy detecters on any interlocked ports + + if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) + { + // Channel Busy. Wait + + TNC->ConnectCmd = _strdup(TXMsg); + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; + + return; + } + + TNC->OverrideBusy = FALSE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", + TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); + + EncodeAndSend(TNC, TXMsg, datalen); + TNC->Timeout = 50; + TNC->InternalCmd = 'C'; // So we dont send the reply to the user. + ReleaseBuffer(buffptr); + STREAM->Connecting = TRUE; + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + if (Stream == 0) + { + if (TNC->HFPacket) + EncodeAndSend(TNC, "C2AD", 4); // ??Return to packet mode?? + else + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? + + TNC->NeedPACTOR = 50; + } + else + { + sprintf(TXMsg, "C1%cD", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 4); + TNC->CmdStream = Stream; + TNC->Timeout = 50; + } + + TNC->Timeout = 0; // Don't expect a response + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? + + if (Stream > 0) + datalen = sprintf(TXMsg, "C1%c%s", Stream + '@', MsgPtr); + else + datalen = sprintf(TXMsg, "C20%s", MsgPtr); + EncodeAndSend(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->Timeout = 50; + TNC->InternalCmd = 0; + TNC->CmdStream = Stream; + + } + } + } + +Poll: + + // Need to poll data and control channel (for responses to commands) + + // Also check status if we have data buffered (for flow control) + + if (TNC->TNCOK) + { + if (TNC->IntCmdDelay == 50) + { + EncodeAndSend(TNC, "C10S", 4); + TNC->InternalCmd = 'S'; + TNC->Timeout = 50; + TNC->IntCmdDelay--; + return; + } + + if (TNC->IntCmdDelay <=0) + { + if (TNC->VeryOldMode == FALSE) + { + EncodeAndSend(TNC, "?", 1); + TNC->InternalCmd = '?'; + TNC->Timeout = 50; + } + TNC->IntCmdDelay = 100; // Every 30 + return; + } + else + TNC->IntCmdDelay--; + } + + return; + +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; // Got Response, so must be back in term mode + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + Poll[0] = 13; + TNC->TXLen = 1; + + WriteCommBlock(TNC); + TNC->Timeout = 50; + + return; + } + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + char * start, * end; + int len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + // Put into Host Mode + + memcpy(Poll, "INTFACE HOST\r", 13); + + TNC->TXLen = 13; + WriteCommBlock(TNC); + TNC->Timeout = 50; + + TNC->ReinitState = 4; // Need Reset + + return; + } + + end = strchr(start, 13); + len = (int)(++end - start); + TNC->InitPtr = end; + memcpy(Poll, start, len); + + TNC->TXLen = len; + WriteCommBlock(TNC); + TNC->Timeout = 50; + + return; + + } +} + +static VOID DoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + Poll[0] = 3; + Poll[1] = 0x58; // ?? Back to cmd: mode ?? + TNC->TXLen = 2; + + Poll[0] = 0xc0; + Poll[1] = 'Q'; // ?? Back to cmd: mode ?? + Poll[2] = 0xc0; + TNC->TXLen = 3; + + WriteCommBlock(TNC); + + return; + } + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + DoTNCReinit(TNC); // See if worked + return; + } + + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + return; + } +} + + + +static VOID ProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0 || TNC->ReinitState == 1) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + DoTNCReinit(TNC); // Send First Command + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } + + if (TNC->ReinitState == 4) // Send INTFACE, Need RESET + { + TNC->ReinitState = 5; + + memcpy(Poll, "RESET\r", 6); + + TNC->TXLen = 6; + WriteCommBlock(TNC); + TNC->Timeout = 50; + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs + + return; + } + + if (TNC->ReinitState == 5) // RESET sent + { + TNC->ReinitState = 5; + + return; + } + + + +} + +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + PMSGWITHLEN buffptr; + char * Buffer = &Msg[3]; // Data portion of frame + char * Call; + char Status[80]; + int Stream = 0; + struct STREAMINFO * STREAM; + + // Any valid frame is an ACK + + TNC->TNCOK = TRUE; + + Len = KissDecode(Msg, Msg, Len); // Remove KISS transparency + + if (Msg[1] == '0' && Msg[2] == '0') + Stream = 0; + else + if (Msg[1] == '2') Stream = 0; else Stream = Msg[2] - '@'; + + STREAM = &TNC->Streams[Stream]; + + // See if Poll Reply or Data + + Msg[Len] = 0; // Terminate + + if (Msg[0] == 'M') // Monitor + { + DoMonitor(TNC, Msg, Len); + return; + } + + + if (Msg[0] == 'E') // Data Echo + { + if (Msg[1] == '2') // HF Port + { + if (TNC->Streams[0].bytesTXed) + TNC->Streams[0].BytesAcked += Len - 3; // We get an ack before the first send + + sprintf(Status, "RX %d TX %d ACKED %d ", + TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); + SetWindowText(TNC->xIDC_TRAFFIC, Status); + + if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked < 500) + TNC->Streams[0].FramesOutstanding = 0; + } + return; + } + + if (Msg[0] == 'D') // Data + { + // Pass to Appl + + buffptr = GetBuff(); + if (buffptr == NULL) return; // No buffers, so ignore + + Len-=3; // Remove Header + + buffptr->Len = Len; // Length + STREAM->bytesRXed += Len; + memcpy(buffptr->Data, Buffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + if (Stream == 0) + { + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", + TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + + WritetoTrace(TNC, Buffer, Len); + + return; + } + + + if (Msg[0] == 'C') // Command Reponse + { + TNC->Timeout = 0; + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + // See if a response to internal command + + if (TNC->InternalCmd) + { + // Process it + + if (TNC->InternalCmd == 'S') // Status + { + char * Line; + char * ptr; + + // Message is line giving free bytes, followed by a line for each active (packet) stream + + // FREE BYTES 1366/5094 + // A/2 #1145(12) CONNECTED to KE7XO-3 + // S/2 CONNECTED to NLV + + // each line is teminated by CR, and by the time it gets here it is null terminated + + //FREE BYTES 2628 + //A/H #80(1) CONNECTED to DK0MNL.. + + if (TNC->HFPacket) + TNC->Streams[0].FramesOutstanding = 0; + + Line = strchr(&Msg[3], 13); + if (Line == 0) return; + + *(Line) = 0; + + ptr = strchr(&Msg[13], '/'); + TNC->Mem1 = atoi(&Msg[13]); + if (ptr) + TNC->Mem2 = atoi(++ptr); + else + TNC->Mem2 = 0; + + SetWindowText(TNC->xIDC_BUFFERS, &Msg[14]); + strcpy(TNC->WEB_BUFFERS, &Msg[14]); + + while (Line[1] != 0) // End of stream + { + Stream = Line[1] - '@'; + STREAM = &TNC->Streams[Stream]; + + if (Line[5] == '#') + { + STREAM->BytesOutstanding = atoi(&Line[6]); + ptr = strchr(&Line[6], '('); + if (ptr) + STREAM->FramesOutstanding = atoi(++ptr); + } + else + { + STREAM->BytesOutstanding = 0; + STREAM->FramesOutstanding = 0; + } + + Line = strchr(&Line[1], 13); + } + return; + } + return; + } + + + WritetoTrace(TNC, Buffer, Len); + + + // Pass to Appl + + Stream = TNC->CmdStream; + + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"KAM} %s", Buffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (Msg[0] == 'I') // ISS/IRS State + { + if (Msg[2] == '1') + { + strcpy(TNC->WEB_TXRX, "Sender"); + SetWindowText(TNC->xIDC_TXRX, "Sender"); + TNC->TXRXState = 'S'; + } + else + { + strcpy(TNC->WEB_TXRX, "Receiver"); + SetWindowText(TNC->xIDC_TXRX, "Receiver"); + TNC->TXRXState = 'R'; + } + return; + } + + if (Msg[0] == '?') // Status + { + TNC->Timeout = 0; + return; + } + + if (Msg[0] == 'S') // Status + { + if (Len < 4) + { + // Reset Response FEND FEND S00 FEND + + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + // Pass to Appl + + if (strstr(Buffer, "STANDBY>") || strstr(Buffer, "*** DISCONNECTED")) + { + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesOutstanding = 0; + + return; + } + + // Must Have been connected or disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesOutstanding = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + if (Stream == 0) + { + // Need to reset Pactor Call in case it was changed + + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? + TNC->NeedPACTOR = 20; + } + + return; + } + + if (Msg[2] == '0') + Call = strstr(Buffer, "HFPacket == 0) + { + Buffer[Len-4] = 0; + } + + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + if (Stream == 0) + { + // Stop Scanner + + char Msg[80]; + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", + TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); + + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + + TRANSPORTENTRY * SESS; + + if (Msg[1] == '2' && Msg[2] == 'A') + TNC->HFPacket = TRUE; + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, Call, Stream, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + if (Stream == 0) + { + struct WL2KInfo * WL2K = TNC->WL2K; + char FreqAppl[10] = ""; // Frequecy-specific application + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->NodeCall, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + + // If Scan Entry has a Appl, save it + + if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0]) + strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]); + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall); + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + + SESS->Mode = 11; // P1 + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + EncodeAndSend(TNC, "T", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (FreqAppl[0]) // Frequency spcific APPL overrides TNC APPL + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + return; + } + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + char CTBuff[300] = "D20"; + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + if (Stream > 0) + sprintf(CTBuff, "D1%c", Stream + '@'); + + while (Len > CTPaclen) // CTEXT Paclen + { + memcpy(&CTBuff[3], &CTEXTMSG[Next], CTPaclen); + EncodeAndSend(TNC, CTBuff, CTPaclen + 3); + Next += CTPaclen; + Len -= CTPaclen; + } + + memcpy(&CTBuff[3], &CTEXTMSG[Next], Len); + EncodeAndSend(TNC, CTBuff, Len + 3); + EncodeAndSend(TNC, "E", 1); // Changeover when all sent + TNC->Streams[0].bytesTXed += CTEXTLEN; + } + return; + + } + else + { + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + + if (Stream == 0) + { + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + + } + + return; + } + } + } +} + + +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + for (i=0;iTXLen = KissEncode(txbuffer, TNC->TXBuffer, Len); + WriteCommBlock(TNC); +} + +static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + outbuff[0]=FEND; + txptr=1; + + for (i=0;i'); + + if (ptr) *(ptr) = 0; + + UpdateMH(TNC, &Msg[3], ' ', 0); + +} +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) // Pactor Stream + { + TNC->TimeInRX = 0; + if (TNC->HFPacket) + EncodeAndSend(TNC, "C2AD", 4); // Disconnect + else + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? +// EncodeAndSend(TNC, "C20TOR", 6); // Disconnect + + TNC->HFPacket = FALSE; + } + else + { + UCHAR TXMsg[10]; + + sprintf(TXMsg, "C1%cD", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 4); + TNC->Timeout = 50; + } +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) // Pactor Stream + { + TNC->TimeInRX = 0; + + if (TNC->HFPacket) + EncodeAndSend(TNC, "C2AD", 4); // Disconnect + else + EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? + + TNC->HFPacket = FALSE; + } + else + { + UCHAR TXMsg[10]; + + sprintf(TXMsg, "C1%cD", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 4); // Send twice - must force a disconnect + TNC->Timeout = 50; + }} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + sprintf(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseOtherPorts(TNC); + TNC->NeedPACTOR = 50; +} + + + +/* + + +MARK 1400 +SPACE 1600 +SHIFT MODEM +INV ON +MAXUSERS 1/10 +PTERRS 30 + + // Others go on end so they can't be overriden + +ECHO OFF +XMITECHO ON +TXFLOW OFF +XFLOW OFF +TRFLOW OFF +AUTOCR 0 +AUTOLF OFF +CRADD OFF +CRSUP OFF +CRSUP OFF/OFF +LFADD OFF/OFF +LFADD OFF +LFSUP OFF/OFF +LFSUP OFF +RING OFF +ARQBBS OFF + +MYCALL + +*/ diff --git a/.svn/pristine/1a/1aa9d4e92ce6b8878c3ec022bb10c4789a6bed84.svn-base b/.svn/pristine/1a/1aa9d4e92ce6b8878c3ec022bb10c4789a6bed84.svn-base index 239598b..6510618 100644 --- a/.svn/pristine/1a/1aa9d4e92ce6b8878c3ec022bb10c4789a6bed84.svn-base +++ b/.svn/pristine/1a/1aa9d4e92ce6b8878c3ec022bb10c4789a6bed84.svn-base @@ -1,6472 +1,6472 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -#define _CRT_SECURE_NO_DEPRECATE - -#include "cheaders.h" -#include "bpqmail.h" - -#define MAIL -#include "httpconnectioninfo.h" - -#ifdef WIN32 -//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" -#else -#include -#include -#endif - -static struct HTTPConnectionInfo * FindSession(char * Key); -int APIENTRY SessionControl(int stream, int command, int param); -int SetupNodeMenu(char * Buff); -VOID SetMultiStringValue(char ** values, char * Multi); -char * GetTemplateFromFile(int Version, char * FN); -VOID FormatTime(char * Time, time_t cTime); -struct MsgInfo * GetMsgFromNumber(int msgno); -BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); -BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); -int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen); -struct HTTPConnectionInfo * AllocateWebMailSession(); -VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen); -void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len); -char *stristr (char *ch1, char *ch2); -char * ReadTemplate(char * FormSet, char * DirName, char * FileName); -VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile); -BOOL CheckifPacket(char * Via); -int GetHTMLFormSet(char * FormSet); -void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen); -char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End); -struct HTTPConnectionInfo * FindWMSession(char * Key); -int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert); -char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys); -char * FindXMLVariable(WebMailInfo * WebMail, char * Var); -int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter); -BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply); -VOID UpdateFormAction(char * Template, char * Key); -BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); -BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); -void FreeWebMailFields(WebMailInfo * WebMail); -VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys); -VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); -VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); -VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); -char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls); -VOID FormatTime2(char * Time, time_t cTime); -VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams); -VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams); -char * CheckFile(struct HtmlFormDir * Dir, char * FN); -VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL); -VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen); -BOOL isAMPRMsg(char * Addr); -char * doXMLTransparency(char * string); -Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); -void SendMessageReadEvent(char * Call, struct MsgInfo * Msg); -void SendNewMessageEvent(char * call, struct MsgInfo * Msg); -void MQTTMessageEvent(void* message); - -extern char NodeTail[]; -extern char BBSName[10]; - -extern char LTFROMString[2048]; -extern char LTTOString[2048]; -extern char LTATString[2048]; - - UCHAR BPQDirectory[260]; - -int LineCount = 35; // Lines per page on message list - -// Forms - -struct HtmlFormDir ** HtmlFormDirs = NULL; -int FormDirCount = 0; - -struct HtmlForm -{ - char * FileName; - BOOL HasInitial; - BOOL HasViewer; - BOOL HasReply; - BOOL HasReplyViewer; -}; - -struct HtmlFormDir -{ - char * FormSet; - char * DirName; - struct HtmlForm ** Forms; - int FormCount; - struct HtmlFormDir ** Dirs; // Nested Directories - int DirCount; -}; - - -char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"}; - -static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; -static char BusyError[] = "

Sorry, No sessions available - please try later

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

BPQ32 Mail Server %s Access

" - "

Please enter Callsign and Password to access WebMail

" - "
" - "" - "" - "
User
Password
" - "

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

Webmail Interface - Message Input Form

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

Webmail Forms Interface - Check Message

" - "
" - - "
" - "To      
" - "CC      
" - "Subject " -//"" - "
Type    " - "" - " BID

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

%s Webmail Interface - User %s - Message List

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

" - "Select Required Template from %s

" - "

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

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

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

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