From e3ea58d23aae3aca6f382c4b95ec31a1f7875cae Mon Sep 17 00:00:00 2001 From: John Wiseman Date: Sun, 28 Aug 2022 09:35:46 +0100 Subject: [PATCH] 23.18 --- AEAPactor.c | 1634 ++ AFXRES.H | 750 + AGWAPI.c | 1672 ++ AGWMon.asm | 1646 ++ AGWMoncode.c | 652 + AISCommon.c | 3291 ++++ APRSCode.c | 8750 ++++++++++ APRSIconData.c | 428 + APRSStdPages.c | 3719 ++++ ARDOP.c | 6370 +++++++ ARDOPAX25.c | 2377 +++ Alloc.c | 129 + Alloc.h | 32 + BBSHTMLConfig.c | 3007 ++++ BBSUtilities.c | 14057 ++++++++++++++++ BPQChat.rc | 416 + BPQChat.vcproj | 386 + BPQChat.vcproj.DESKTOP-TGEL8RC.John.user | 65 + BPQChat.vcproj.SKIGACER.johnw.user | 65 + BPQINP3.c | 1375 ++ BPQMail.aps | Bin 0 -> 68844 bytes BPQMail.c | 3633 ++++ BPQMail.rc | 1475 ++ BPQMail.vcproj | 489 + BPQMail.vcproj.DESKTOP-TGEL8RC.John.user | 65 + BPQMail.vcproj.HPLAPTOP.johnw.user | 65 + BPQMail.vcproj.SKIGACER.johnw.user | 65 + BPQMailConfig.c | 3821 +++++ BPQMailrc.h | 330 + BPQMailrc.hm | 6 + BPQNRR.c | 193 + BPQRemotePTT.c | 988 ++ BPQRemotePTT.cfg | 12 + BPQRemotePTT.rc | 173 + BPQRemotePTT.vcproj | 228 + BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user | 65 + BPQRemotePTTRes.h | 32 + BPQTermMDI.c | 4830 ++++++ BPQWinAPP.c | 246 + BPQWinAPP.rc | 71 + BPQWinAPP.vcproj | 230 + BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user | 65 + BPQWinAPP.vcproj.SKIGACER.johnw.user | 65 + BPQtoAGW.c | 763 + Bpq32.c | 6557 +++++++ BpqTermMDI.h | 146 + CBPQ32.vcproj | 597 + CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user | 65 + CBPQ32.vcproj.HPLAPTOP.johnw.user | 65 + CBPQ32.vcproj.SKIGACER.johnw.user | 65 + CBPQ32.vcxproj | 232 + CBPQ32.vcxproj.filters | 228 + CHeaders.h | 409 + CMSAuth.c | 73 + ChatDebug.c | 432 + ChatHTMLConfig.c | 815 + ChatMonitor.c | 455 + ChatMultiConsole.c | 1731 ++ ChatUsers.txt | 16 + ChatUtilities.c | 363 + ChatUtils.c | 830 + Cmd.c | 5504 ++++++ CmdLineAuth.c | 62 + CmdLineAuth.cpp | 68 + CommonCode.c | 4667 +++++ ConfigDirewolf.c | 131 + ConfigDirewolf.vcxproj | 164 + ConfigDirewolf.vcxproj.user | 11 + ConfigSDR.c | 121 + ConfigWinRPR.c | 137 + ConfigWinRPR.vcxproj | 164 + ConfigWinRPR.vcxproj.user | 4 + DOSAPI.c | 406 + DRATS.c | 673 + Dragon.c | 2257 +++ FBBRoutines.c | 2008 +++ FLDigi.c | 3861 +++++ FLDigi64.c | 3860 +++++ FreeDATA-HPLaptop.c | 3490 ++++ FreeDATA.c | 3492 ++++ GetVersion.h | 28 + HALDriver.c | 1906 +++ HALDriver64.c | 1907 +++ HFCommon.c | 2145 +++ HSMODEM.c | 1880 +++ HTMLCommonCode.c | 124 + HTTPcode.c | 4331 +++++ HanksRT.c | 4404 +++++ Housekeeping.c | 1184 ++ IPCode.c | 5362 ++++++ KAMPactor.c | 2083 +++ KISSHF.c | 1543 ++ KernelScript1.rc | 339 + L2Code.c | 3865 +++++ L3Code.c | 1470 ++ L4Code.c | 2395 +++ LinBPQ.c | 1766 ++ LzFind.c | 751 + LzFind.h | 107 + LzHash.h | 54 + LzmaDec.c | 1025 ++ LzmaDec.h | 223 + LzmaEnc.c | 2281 +++ LzmaEnc.h | 72 + LzmaLib.c | 46 + LzmaLib.h | 135 + MBLRoutines.c | 411 + MHSave.txt | 0 MULTIPSK.c | 1543 ++ MULTIPSK64.c | 1543 ++ MailCommands.c | 696 + MailDataDefs.c | 221 + MailNode.vcproj | 623 + MailNode.vcproj.DESKTOP-TGEL8RC.John.user | 65 + MailNode.vcproj.HPLAPTOP.johnw.user | 65 + MailNode.vcproj.SKIGACER.johnw.user | 65 + MailNode.vcxproj | 271 + MailRouting.c | 2010 +++ MailTCP.c | 4128 +++++ Moncode.c | 937 + Monitor.c | 424 + MultiConsole.c | 1663 ++ Multicast.c | 1676 ++ NNTPRoutines.c | 1208 ++ NodeMapTest.c | 2381 +++ NodeMapTest.vcproj | 209 + NodeMapTest.vcproj.SKIGACER.johnw.user | 65 + PortMapper.c | 1858 ++ RTKnown.txt | 25 + RigControl.c | 9143 ++++++++++ Ring.wav | Bin 0 -> 63760 bytes SCSPactor.c | 4179 +++++ SCSTrackeMulti.c | 1713 ++ SCSTrackeMulti64.c | 1714 ++ SCSTracker.c | 2631 +++ SerialPort.c | 1142 ++ StdVer.inc | 59 + Strucs.inc | 572 + TNCCode.c | 362 + TNCEmulators.c | 6261 +++++++ TelnetV6.c | 6897 ++++++++ TermTCP.c | 1604 ++ UDPtoTCP/UDPtoTCP.sln | 20 + UDPtoTCP/UDPtoTCP/UDPtoTCP.c | 391 + UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj | 207 + .../UDPtoTCP.vcproj.SKIGACER.johnw.user | 65 + UIARQ.c | 1850 ++ UIRoutines.c | 636 + UZ7HODrv.c | 3011 ++++ V4-HPLaptop.c | 1821 ++ V4.c | 1766 ++ VARA.c | 2539 +++ Versions.h | 125 + WINMOR.c | 3125 ++++ WPRoutines.c | 1636 ++ WebMail.c | 5963 +++++++ WinRPR.c | 1671 ++ WinmorControl.c | 841 + WinmorControl.rc | 71 + WinmorControl.vcproj | 229 + WinmorControl.vcproj.SKIGACER.johnw.user | 65 + XAprs | Bin 0 -> 174928 bytes XAprs.c | 4583 +++++ XAprsCopy.c | 4463 +++++ adif-HPLaptop.h | 36 + adif.c | 617 + adif.h | 37 + asmDOSAPI.asm | 136 + asmstrucs.h | 1379 ++ base64.c | 177 + bpq32.def | 23 + bpq32.h | 600 + bpqaprs.h | 154 + bpqaxip.c | 3333 ++++ bpqchat.c | 1601 ++ bpqchat.h | 770 + bpqether.c | 691 + bpqhdlc.c | 1287 ++ bpqicon.ico | Bin 0 -> 766 bytes bpqmail.h | 1594 ++ bpqvkiss.c | 578 + bpqvkiss.h | 42 + cMain.c | 2640 +++ chatconfig.cfg | 22 + chatrc.h | 208 + compatbits.c | 167 + compatbits.h | 240 + config.c | 3048 ++++ configstructs.h | 179 + configure.scan | 53 + datadefs.c | 70 + hid.c | 913 + hidapi.h | 386 + httpconnectioninfo.h | 43 + i2c-dev.h | 335 + i2cdummy.c | 48 + igd_desc_parse.h | 49 + ipcode.h | 226 + kernelresource.h | 135 + kiss.c | 2026 +++ kiss.h | 64 + libconfig.h | 321 + linether.c | 498 + linether.c~ | 498 + lzhuf32.c | 1810 ++ macdummy.c | 52 + makeaprs | 1 + makefile | 39 + md5.c | 332 + md5.h | 30 + miniupnpc.h | 153 + miniupnpc.lib | Bin 0 -> 107890 bytes miniupnpc_declspec.h | 21 + miniupnpctypes.h | 19 + mypng.h | 3455 ++++ nodelog.txt | 0 nodestatus.txt | 2 + pcap-bpf.h | 697 + pcap-stdinc.h | 64 + pcap.h | 257 + pibits.c | 41 + png.c | 828 + png.h | 3455 ++++ pngconf.h | 1441 ++ pngerror.c | 295 + pngfile.c | 409 + pngfile.h | 23 + pnggccrd.c | 5408 ++++++ pngget.c | 934 + pngmem.c | 598 + pngpread.c | 1573 ++ pngread.c | 1456 ++ pngrio.c | 161 + pngrtran.c | 4177 +++++ pngrutil.c | 3124 ++++ pngset.c | 1219 ++ pngtest.c | 1554 ++ pngtrans.c | 650 + pngvcrd.c | 3903 +++++ pngwio.c | 228 + pngwrite.c | 1464 ++ pngwtran.c | 563 + pngwutil.c | 2730 +++ resource.h | 32 + rigcontrol.h | 261 + rigresource.h | 34 + savelinks.txt | 0 savenodes.txt | 0 stdexcept.c | 82 + telnetserver.h | 106 + templatedefs.c | 1593 ++ tncinfo.h | 901 + types.h | 205 + upnp.c | 182 + upnpcommands.h | 348 + upnpdev.h | 44 + upnperrors.h | 26 + utf8Routines.c | 560 + winstdint.h | 235 + xbpqether.c | 706 + xbpqvkiss.c | 578 + zconf.h | 333 + zlib.h | 1358 ++ zlib.lib | Bin 0 -> 14778 bytes zlibstat.lib | Bin 0 -> 674920 bytes zlibstat64.lib | Bin 0 -> 1231334 bytes 266 files changed, 323732 insertions(+) create mode 100644 AEAPactor.c create mode 100644 AFXRES.H create mode 100644 AGWAPI.c create mode 100644 AGWMon.asm create mode 100644 AGWMoncode.c create mode 100644 AISCommon.c create mode 100644 APRSCode.c create mode 100644 APRSIconData.c create mode 100644 APRSStdPages.c create mode 100644 ARDOP.c create mode 100644 ARDOPAX25.c create mode 100644 Alloc.c create mode 100644 Alloc.h create mode 100644 BBSHTMLConfig.c create mode 100644 BBSUtilities.c create mode 100644 BPQChat.rc create mode 100644 BPQChat.vcproj create mode 100644 BPQChat.vcproj.DESKTOP-TGEL8RC.John.user create mode 100644 BPQChat.vcproj.SKIGACER.johnw.user create mode 100644 BPQINP3.c create mode 100644 BPQMail.aps create mode 100644 BPQMail.c create mode 100644 BPQMail.rc create mode 100644 BPQMail.vcproj create mode 100644 BPQMail.vcproj.DESKTOP-TGEL8RC.John.user create mode 100644 BPQMail.vcproj.HPLAPTOP.johnw.user create mode 100644 BPQMail.vcproj.SKIGACER.johnw.user create mode 100644 BPQMailConfig.c create mode 100644 BPQMailrc.h create mode 100644 BPQMailrc.hm create mode 100644 BPQNRR.c create mode 100644 BPQRemotePTT.c create mode 100644 BPQRemotePTT.cfg create mode 100644 BPQRemotePTT.rc create mode 100644 BPQRemotePTT.vcproj create mode 100644 BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user create mode 100644 BPQRemotePTTRes.h create mode 100644 BPQTermMDI.c create mode 100644 BPQWinAPP.c create mode 100644 BPQWinAPP.rc create mode 100644 BPQWinAPP.vcproj create mode 100644 BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user create mode 100644 BPQWinAPP.vcproj.SKIGACER.johnw.user create mode 100644 BPQtoAGW.c create mode 100644 Bpq32.c create mode 100644 BpqTermMDI.h create mode 100644 CBPQ32.vcproj create mode 100644 CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user create mode 100644 CBPQ32.vcproj.HPLAPTOP.johnw.user create mode 100644 CBPQ32.vcproj.SKIGACER.johnw.user create mode 100644 CBPQ32.vcxproj create mode 100644 CBPQ32.vcxproj.filters create mode 100644 CHeaders.h create mode 100644 CMSAuth.c create mode 100644 ChatDebug.c create mode 100644 ChatHTMLConfig.c create mode 100644 ChatMonitor.c create mode 100644 ChatMultiConsole.c create mode 100644 ChatUsers.txt create mode 100644 ChatUtilities.c create mode 100644 ChatUtils.c create mode 100644 Cmd.c create mode 100644 CmdLineAuth.c create mode 100644 CmdLineAuth.cpp create mode 100644 CommonCode.c create mode 100644 ConfigDirewolf.c create mode 100644 ConfigDirewolf.vcxproj create mode 100644 ConfigDirewolf.vcxproj.user create mode 100644 ConfigSDR.c create mode 100644 ConfigWinRPR.c create mode 100644 ConfigWinRPR.vcxproj create mode 100644 ConfigWinRPR.vcxproj.user create mode 100644 DOSAPI.c create mode 100644 DRATS.c create mode 100644 Dragon.c create mode 100644 FBBRoutines.c create mode 100644 FLDigi.c create mode 100644 FLDigi64.c create mode 100644 FreeDATA-HPLaptop.c create mode 100644 FreeDATA.c create mode 100644 GetVersion.h create mode 100644 HALDriver.c create mode 100644 HALDriver64.c create mode 100644 HFCommon.c create mode 100644 HSMODEM.c create mode 100644 HTMLCommonCode.c create mode 100644 HTTPcode.c create mode 100644 HanksRT.c create mode 100644 Housekeeping.c create mode 100644 IPCode.c create mode 100644 KAMPactor.c create mode 100644 KISSHF.c create mode 100644 KernelScript1.rc create mode 100644 L2Code.c create mode 100644 L3Code.c create mode 100644 L4Code.c create mode 100644 LinBPQ.c create mode 100644 LzFind.c create mode 100644 LzFind.h create mode 100644 LzHash.h create mode 100644 LzmaDec.c create mode 100644 LzmaDec.h create mode 100644 LzmaEnc.c create mode 100644 LzmaEnc.h create mode 100644 LzmaLib.c create mode 100644 LzmaLib.h create mode 100644 MBLRoutines.c create mode 100644 MHSave.txt create mode 100644 MULTIPSK.c create mode 100644 MULTIPSK64.c create mode 100644 MailCommands.c create mode 100644 MailDataDefs.c create mode 100644 MailNode.vcproj create mode 100644 MailNode.vcproj.DESKTOP-TGEL8RC.John.user create mode 100644 MailNode.vcproj.HPLAPTOP.johnw.user create mode 100644 MailNode.vcproj.SKIGACER.johnw.user create mode 100644 MailNode.vcxproj create mode 100644 MailRouting.c create mode 100644 MailTCP.c create mode 100644 Moncode.c create mode 100644 Monitor.c create mode 100644 MultiConsole.c create mode 100644 Multicast.c create mode 100644 NNTPRoutines.c create mode 100644 NodeMapTest.c create mode 100644 NodeMapTest.vcproj create mode 100644 NodeMapTest.vcproj.SKIGACER.johnw.user create mode 100644 PortMapper.c create mode 100644 RTKnown.txt create mode 100644 RigControl.c create mode 100644 Ring.wav create mode 100644 SCSPactor.c create mode 100644 SCSTrackeMulti.c create mode 100644 SCSTrackeMulti64.c create mode 100644 SCSTracker.c create mode 100644 SerialPort.c create mode 100644 StdVer.inc create mode 100644 Strucs.inc create mode 100644 TNCCode.c create mode 100644 TNCEmulators.c create mode 100644 TelnetV6.c create mode 100644 TermTCP.c create mode 100644 UDPtoTCP/UDPtoTCP.sln create mode 100644 UDPtoTCP/UDPtoTCP/UDPtoTCP.c create mode 100644 UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj create mode 100644 UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj.SKIGACER.johnw.user create mode 100644 UIARQ.c create mode 100644 UIRoutines.c create mode 100644 UZ7HODrv.c create mode 100644 V4-HPLaptop.c create mode 100644 V4.c create mode 100644 VARA.c create mode 100644 Versions.h create mode 100644 WINMOR.c create mode 100644 WPRoutines.c create mode 100644 WebMail.c create mode 100644 WinRPR.c create mode 100644 WinmorControl.c create mode 100644 WinmorControl.rc create mode 100644 WinmorControl.vcproj create mode 100644 WinmorControl.vcproj.SKIGACER.johnw.user create mode 100644 XAprs create mode 100644 XAprs.c create mode 100644 XAprsCopy.c create mode 100644 adif-HPLaptop.h create mode 100644 adif.c create mode 100644 adif.h create mode 100644 asmDOSAPI.asm create mode 100644 asmstrucs.h create mode 100644 base64.c create mode 100644 bpq32.def create mode 100644 bpq32.h create mode 100644 bpqaprs.h create mode 100644 bpqaxip.c create mode 100644 bpqchat.c create mode 100644 bpqchat.h create mode 100644 bpqether.c create mode 100644 bpqhdlc.c create mode 100644 bpqicon.ico create mode 100644 bpqmail.h create mode 100644 bpqvkiss.c create mode 100644 bpqvkiss.h create mode 100644 cMain.c create mode 100644 chatconfig.cfg create mode 100644 chatrc.h create mode 100644 compatbits.c create mode 100644 compatbits.h create mode 100644 config.c create mode 100644 configstructs.h create mode 100644 configure.scan create mode 100644 datadefs.c create mode 100644 hid.c create mode 100644 hidapi.h create mode 100644 httpconnectioninfo.h create mode 100644 i2c-dev.h create mode 100644 i2cdummy.c create mode 100644 igd_desc_parse.h create mode 100644 ipcode.h create mode 100644 kernelresource.h create mode 100644 kiss.c create mode 100644 kiss.h create mode 100644 libconfig.h create mode 100644 linether.c create mode 100644 linether.c~ create mode 100644 lzhuf32.c create mode 100644 macdummy.c create mode 100644 makeaprs create mode 100644 makefile create mode 100644 md5.c create mode 100644 md5.h create mode 100644 miniupnpc.h create mode 100644 miniupnpc.lib create mode 100644 miniupnpc_declspec.h create mode 100644 miniupnpctypes.h create mode 100644 mypng.h create mode 100644 nodelog.txt create mode 100644 nodestatus.txt create mode 100644 pcap-bpf.h create mode 100644 pcap-stdinc.h create mode 100644 pcap.h create mode 100644 pibits.c create mode 100644 png.c create mode 100644 png.h create mode 100644 pngconf.h create mode 100644 pngerror.c create mode 100644 pngfile.c create mode 100644 pngfile.h create mode 100644 pnggccrd.c create mode 100644 pngget.c create mode 100644 pngmem.c create mode 100644 pngpread.c create mode 100644 pngread.c create mode 100644 pngrio.c create mode 100644 pngrtran.c create mode 100644 pngrutil.c create mode 100644 pngset.c create mode 100644 pngtest.c create mode 100644 pngtrans.c create mode 100644 pngvcrd.c create mode 100644 pngwio.c create mode 100644 pngwrite.c create mode 100644 pngwtran.c create mode 100644 pngwutil.c create mode 100644 resource.h create mode 100644 rigcontrol.h create mode 100644 rigresource.h create mode 100644 savelinks.txt create mode 100644 savenodes.txt create mode 100644 stdexcept.c create mode 100644 telnetserver.h create mode 100644 templatedefs.c create mode 100644 tncinfo.h create mode 100644 types.h create mode 100644 upnp.c create mode 100644 upnpcommands.h create mode 100644 upnpdev.h create mode 100644 upnperrors.h create mode 100644 utf8Routines.c create mode 100644 winstdint.h create mode 100644 xbpqether.c create mode 100644 xbpqvkiss.c create mode 100644 zconf.h create mode 100644 zlib.h create mode 100644 zlib.lib create mode 100644 zlibstat.lib create mode 100644 zlibstat64.lib diff --git a/AEAPactor.c b/AEAPactor.c new file mode 100644 index 0000000..6327548 --- /dev/null +++ b/AEAPactor.c @@ -0,0 +1,1634 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface AEA/Timewave TNC in Pactor Mode to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +// Version 1.1.1.2 Sept 2010 + +// Fix CTEXT +// Turn round link when all acked (not all sent) + +// Version 1.1.1.3 Sept 2010 + +// Turn round link if too long in receive + +// Version 1.1.1.4 September 2010 + +// Fix Freq Display after Node reconfig +// Only use AutoConnect APPL for Pactor Connects + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +//#include +//#include + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + + +static char ClassName[]="AEAPACTORSTATUS"; + +static char WindowTitle[] = "AEA Pactor"; +static int RigControlRow = 165; + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +#define MaxStreams 26 + +#define PTOVER 0x1a // Pactor Turnround Char + +char OverMsg[3] = " \x1a"; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + + + +static char status[8][8] = {"STANDBY", "PHASING", "CHGOVER", "IDLE", "TRAFFIC", "ERROR", "RQ", "XXXX"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +static void CheckRX(struct TNCINFO * TNC); +VOID AEAPoll(int Port); +static VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +static VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); + +VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +static VOID ProcessAEAPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, size_t Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +static VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, size_t Len); + +// Note that AEA host Mode uses SOH/ETB delimiters, with DLE stuffing + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +static int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, size_t len); +static int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, size_t len); + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "ADDR") == 0) // Winmor Using BPQ32 COnfig + { + BPQport = Port; + p_ipad = ptr; + } + else + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else + strcat (TNC->InitScript, buf); + } + } + + return (TRUE); +} + + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL || TNC->hDevice == 0) + return 0; // Port not open + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + CheckRX(TNC); + AEAPoll(port); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) + { + size_t datalen; + + buffptr=Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, (int)datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = 36; + memcpy(buffptr->Data, "No Connection to PACTOR TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + if(TNC->Streams[Stream].Connected) + { + TNC->Streams[Stream].FramesQueued++; + } + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesQueued > 4) + return 1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15; // Busy + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting; // OK + + case 4: // reinit + + return (0); + + case 5: // Close + + EncodeAndSend(TNC, "OHON", 4); // HOST N + Sleep(50); + + SaveWindowPos(port); + + CloseCOMPort(TNC->hDevice); + + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + + +VOID * AEAExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * TempScript; + char * ptr; + + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"AEA Pactor %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->Hardware = H_AEA; + + TNC->TEXTMODE = FALSE; + + PortEntry->MAXHOSTMODESESSIONS = 11; // Default + + TNC->InitScript = _strupr(TNC->InitScript); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // No Scan Interlock + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + TempScript = malloc(4000); + + strcpy(TempScript, "RESTART\r"); + strcat(TempScript, "EXPERT ON\r"); + strcat(TempScript, "PTHUFF 0\r"); + strcat(TempScript, "PT200 ON\r"); + strcat(TempScript, "WIDESHFT OFF\r"); + strcat(TempScript, "ARQT 30\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "XMITOK ON\r"); + strcat(TNC->InitScript, "XFLOW OFF\r"); + strcat(TNC->InitScript, "RXREV OFF\r"); + strcat(TNC->InitScript, "FLOW OFF\r"); + strcat(TNC->InitScript, "AWLEN 8\r"); + strcat(TNC->InitScript, "AUTOBAUD OFF\r"); + strcat(TNC->InitScript, "8BITCONV ON\r"); + strcat(TNC->InitScript, "ALFPAC OFF\r"); + strcat(TNC->InitScript, "ALFDISP OFF\r"); + strcat(TNC->InitScript, "ACRRTTY 0\r"); + strcat(TNC->InitScript, "HPOLL ON\r"); + strcat(TNC->InitScript, "EAS ON\r\r"); + strcat(TNC->InitScript, "CONMODE TRANS\r"); + strcat(TNC->InitScript, "PTOVER $1A\r\r"); + + // Set the ax.25 MYCALL + + sprintf(msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, msg); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 0, 0, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Buffers", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 213; + TNC->ClientWidth = 500; + + TNC->hMenu = CreateMenu(); + TNC->hWndMenu = CreatePopupMenu(); + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + WritetoConsole("\n"); + + return ExtProc; +} + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // AEA uses SOH ETX Framing + + if (TNC->RXBuffer[0] != SOH) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + if (TNC->ReinitState == 2) + { + // Just entered host mode, so could be text command on front + + unsigned char * ptr = strchr(TNC->RXBuffer, SOH); + + if (ptr) + { + int textlen = ptr - TNC->RXBuffer; + + TNC->RXLen -= textlen; + Length = TNC->RXLen; + + Debugprintf("%s", TNC->RXBuffer); + Debugprintf("AEA Entered Host Mode"); + TNC->ReinitState =0; + + memmove(TNC->RXBuffer, ptr, Length); + goto hostFrame; + } + } + + Debugprintf("AEA Bad Host Frame"); + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->RXBuffer[TNC->RXLen-2] != ':') + if (strstr(TNC->RXBuffer, "cmd:") == 0) + { + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; // NULL in text mode message + + return; // Wait for rest of frame + } + + // Complete Char Mode Frame + + TNC->RXLen = 0; // Ready for next frame + + ProcessTermModeResponse(TNC); + return; + } + + // Receiving a Host Mode frame + + if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery + { + TNC->RXLen = 0; // Ready for next frame + return; + } + +hostFrame: + + if (Length < 3) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[Length-1] != ETB) + return; // Wait till we have a full frame + + if (TNC->RXBuffer[Length-2] == DLE && TNC->RXBuffer[Length-3] != DLE) + return; // ??? DLE ETB isn't end of frame, but DLE DLE ETB is + + ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer + + TNC->RXLen = 0; // Ready for next frame + + + return; + +} + +static VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, size_t Len) +{ + UCHAR * FendPtr; + size_t NewLen; + + // Split into Packets. By far the most likely is a single packet, so treat as special case + // Beware of DLE ETB and DLE DLE ETB! + + FendPtr = memchr(&rxbuffer[1], ETB, Len-1); + +FENDLoop: + + if (*(FendPtr - 1) == DLE && *(FendPtr - 2) != DLE) + { + FendPtr++; + FendPtr = memchr(FendPtr, ETB, Len-1); + goto FENDLoop; + } + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessAEAPacket(TNC, &rxbuffer[1], Len - 2); + return; + } + + // Process the first Packet in the buffer + + NewLen = FendPtr - rxbuffer -1; + + ProcessAEAPacket(TNC, &rxbuffer[1], NewLen); + + // Loop Back + + ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2); + return; + +} + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + TNC->Timeout = 50; + return TRUE; +} + +VOID AEAPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM; + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + int Stream; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + if (TNC->HostMode == 0) + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + TNC->ReinitState = 0; + + sprintf(Status,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, Status); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Nod + } + } + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->CommandBusy) + goto Poll; + + // We don't check for a new attach unless Timeout and CommandBusy are both zero, as we need to send a command. + + // If Pactor Session has just been attached, set Pactor Call to the connecting user's callsign + + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + UCHAR TXMsg[1000]; + int datalen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + TNC->Streams[0].TimeInRX = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "OMf%s", TNC->Streams[0].MyCall); + EncodeAndSend(TNC, TXMsg, datalen); + TNC->InternalCmd = 'M'; + TNC->CommandBusy = TRUE; + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + // Shouldn't we also take out of standby mode?? PN is Pactor Listen, for monitoring + + return; + + } + + //If sending internal command list, send next element + + if (TNC->CmdSet) + { + char * start, * end; + size_t len; + + start = TNC->CmdSet; + + if (*(start) == 0) // End of Script + { + free(TNC->CmdSave); + TNC->CmdSet = NULL; + } + else + { + end = strchr(start, 13); + len = ++end - start -1; // exclude cr + TNC->CmdSet = end; + + EncodeAndSend(TNC, start, (int)len); + TNC->InternalCmd = 'X'; + TNC->CommandBusy = TRUE; + + return; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + } + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + EncodeAndSend(TNC, "OPA", 3); // ??Return to packet mode?? + TNC->CommandBusy = TRUE; + + TNC->CmdSet = TNC->CmdSave = malloc(100); + + sprintf(TNC->CmdSet, "OMf%s\rOPt\rOCETRANS\r", TNC->NodeCall); // Queue Back to Pactor Standby + TNC->InternalCmd = 'T'; + TNC->TEXTMODE = FALSE; + TNC->IntCmdDelay--; + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, Status); + + return; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q && TNC->DataBusy == FALSE) + { + int datalen; + UCHAR TXMsg[1000]; + PMSGWITHLEN buffptr; + UCHAR * MsgPtr; + char Status[80]; + + if (TNC->Streams[Stream].Connected) + { + if (Stream == 0) + { + // Limit amount in TX + + if (TNC->Streams[0].BytesTXed - TNC->Streams[0].BytesAcked > 200) + continue; + + // If in IRS state for too long, force turnround + + if (TNC->TXRXState == 'R') + { + if (TNC->Streams[0].TimeInRX++ > 15) + { + EncodeAndSend(TNC, "OAG", 3); + TNC->InternalCmd = 'A'; + TNC->CommandBusy = TRUE; + } + else + goto GetStatus; + } + TNC->Streams[0].TimeInRX = 0; + } + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + TNC->Streams[Stream].FramesQueued--; + datalen=buffptr->Len; + MsgPtr = buffptr->Data; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + // If in CONV, and data looks binary, switch to TRAN + + TNC->NeedTurnRound = TRUE; // Sending data, so need turnround at end + + if (TNC->TEXTMODE) + { + int i; + UCHAR j; + + for (i = 0; i < datalen; i++) + { + j = MsgPtr[i]; + + if (j > 127 || j == 26 || j < 10) + { + TNC->TEXTMODE = FALSE; + EncodeAndSend(TNC, "OCETRANS", 8); + Debugprintf("Switching to TRANS"); + TNC->CommandBusy = TRUE; + TNC->InternalCmd = 'A'; + break; + } + } + } + + sprintf(TXMsg, "%c", Stream + ' '); + + memcpy(&TXMsg[1], buffptr->Data, datalen); + + EncodeAndSend(TNC, TXMsg, datalen + 1); + ReleaseBuffer(buffptr); + TNC->Streams[Stream].BytesTXed += datalen; + Debugprintf("Stream %d Sending %d, BytesTXED now %d", Stream, datalen, TNC->Streams[Stream].BytesTXed); + TNC->Timeout = 0; + TNC->DataBusy = TRUE; + + if (Stream == 0) + ShowTraffic(TNC); + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + else + { + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + datalen=buffptr->Len; + MsgPtr = buffptr->Data; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CONV", 9) == 0) + { + TNC->TEXTMODE = TRUE; + buffptr->Len = sprintf((UCHAR *)buffptr->Data,"AEA} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + EncodeAndSend(TNC, "OCECONV", 7); + TNC->CommandBusy = TRUE; + + return; + } + + if (memcmp(MsgPtr, "MODE TRANS", 9) == 0) + { + TNC->TEXTMODE = FALSE; + buffptr->Len = sprintf(buffptr->Data,"AEA} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + EncodeAndSend(TNC, "OCETRANS", 8); + TNC->CommandBusy = TRUE; + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(TNC->Streams[Stream].RemoteCall, &MsgPtr[2], 9); + TNC->Streams[Stream].Connecting = TRUE; + + // If Stream 0, Convert C CALL to PACTOR CALL + + if (Stream == 0) + { + datalen = sprintf(TXMsg, "%cPG%s", Stream + '@', TNC->Streams[0].RemoteCall); + sprintf(Status, "%s Connecting to %s", + TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + } + else + datalen = sprintf(TXMsg, "%cCO %s", Stream + '@', TNC->Streams[Stream].RemoteCall); + + EncodeAndSend(TNC, TXMsg, datalen); + TNC->InternalCmd = 'C'; // So we dont send the reply to the user. + ReleaseBuffer(buffptr); + TNC->Streams[Stream].Connecting = TRUE; + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + if (Stream == 0) + { + EncodeAndSend(TNC, "ODI", 3); // ??Return to packet mode?? + TNC->NeedPACTOR = 50; + TNC->CommandBusy = TRUE; + } + else + { + sprintf(TXMsg, "%cDI", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 3); + TNC->CmdStream = Stream; + } + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? + + if (Stream > 0) + datalen = sprintf(TXMsg, "C20%s", MsgPtr); + else + datalen = sprintf(TXMsg, "O%s", MsgPtr); + + EncodeAndSend(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + TNC->CommandBusy = TRUE; + TNC->CmdStream = Stream; + } + } + } + +GetStatus: + + // Need to poll data and control channel (for responses to commands) + + // Also check status if we have data buffered (for flow control) + + if (TNC->TNCOK) + { + if (TNC->IntCmdDelay <= 0) + { + EncodeAndSend(TNC, "OOP", 3); + TNC->InternalCmd = 'S'; + TNC->CommandBusy = TRUE; + TNC->IntCmdDelay = 9; // Every second + return; + } + else + TNC->IntCmdDelay--; + } + +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 3) + return; + + TNC->PollDelay = 0; + + EncodeAndSend(TNC, "OGG", 3); // Poll + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; // Got Response, so must be back in term mode + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a CR to see if in Terminal or Host Mode + + char Status[80]; + + sprintf(Status,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, Status); + + Poll[0] = 13; + TNC->TXLen = 1; + TNC->RXLen = 0; + + WriteCommBlock(TNC); + + return; + } + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + char * start, * end; + size_t len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + // Put into Host Mode + + + Poll[0] = 0x11; // XON + Poll[1] = 0x18; // CAN + Poll[2] = 0x03; // COM + + memcpy(&Poll[3], "HOST Y\r", 7); + + TNC->TXLen = 10; + WriteCommBlock(TNC); + TNC->Timeout = 0; + TNC->CommandBusy = FALSE; + TNC->DataBusy = FALSE; + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs + + return; + } + + end = strchr(start, 13); + len = ++end - start; + TNC->InitPtr = end; + memcpy(Poll, start, len); + + TNC->TXLen = (int)len; + WriteCommBlock(TNC); + + + return; + + } +} + +static VOID DoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + + EncodeAndSend(TNC, "OHON", 4); // HOST N + TNC->Timeout = 20; + return; + } + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + DoTNCReinit(TNC); // See if worked + return; + } +} + +static VOID ProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + Debugprintf("AEA Initstate %d Response %s", TNC->ReinitState, TNC->RXBuffer); + + if (TNC->ReinitState == 0 || TNC->ReinitState == 1) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + DoTNCReinit(TNC); // Send First Command + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } +} + +static VOID ProcessAEAPacket(struct TNCINFO * TNC, UCHAR * Msg, size_t Len) +{ + PMSGWITHLEN buffptr; + char * Buffer = &Msg[1]; // Data portion of frame + char * Call; + char Status[80]; + int Stream = 0; + int Opcode; + struct STREAMINFO * STREAM; + + // Any valid frame is an ACK + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + Len = DLEDecode(Msg, Msg, Len); // Remove KISS transparency + + Stream = Msg[0] & 15; + Opcode = Msg[0] >> 4; + + STREAM = &TNC->Streams[Stream]; + + if (Msg[0] == 'O' && Msg[1] == 'G' && Msg[2] == 'G') + { + if (Msg[3] == 0) + { + // OK Response + + sprintf(Status,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, Status); + + return; + } + } + + if (Msg[0] == '/') // 2F + { + char * Eptr; + + // Echoed Data + + Len--; + + Eptr = memchr(Buffer, 0x1c, Len); + + if (Eptr) + Debugprintf("Echoed 1c followed by %x", *(++Eptr)); + + TNC->Streams[0].BytesAcked += (int)Len; + + Debugprintf("Ack for %d, BytesAcked now %d", Len, TNC->Streams[0].BytesAcked); + ShowTraffic(TNC); + + // If nothing more to send, turn round link + + if ((TNC->Streams[0].BPQtoPACTOR_Q == 0) && TNC->NeedTurnRound && + (TNC->Streams[0].BytesAcked >= TNC->Streams[0].BytesTXed)) // Nothing following and all acked + { + Debugprintf("AEA Sent = Acked - sending Turnround"); + + if (TNC->TEXTMODE == 0) + { + // In Trans - switch back + + TNC->TEXTMODE = TRUE; + EncodeAndSend(TNC, "OCECONV", 7); + Debugprintf("Switching to CONV"); + TNC->CommandBusy = TRUE; + TNC->InternalCmd = 'A'; + TNC->NeedTRANS = TRUE; + } + + TNC->NeedTurnRound = FALSE; + EncodeAndSend(TNC, OverMsg, 2); + TNC->Timeout = 0; // No response to data + } + return; + } + + if (Opcode == 3) + { + // Received Data + + // Pass to Appl + + buffptr = GetBuff(); + if (buffptr == NULL) return; // No buffers, so ignore + + Len--; // Remove Header + + buffptr->Len = Len; // Length + TNC->Streams[Stream].BytesRXed += (int)Len; + memcpy(&buffptr->Data[0], Buffer, Len); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + if (Stream == 0) + ShowTraffic(TNC); + + return; + } + + + if (Opcode == 4) + { + // Link Status or Command Response + + TNC->CommandBusy = FALSE; + + if (TNC->InternalCmd) + { + // Process it + + if (TNC->InternalCmd == 'S') // Status + { +// if (Msg[3] == 'P' && Msg[4] == 'G') + { + SetWindowText(TNC->xIDC_STATE, status[Msg[5] - 0x30]); + + TNC->TXRXState = Msg[6]; + + if (Msg[6] == 'S') + SetWindowText(TNC->xIDC_TXRX, "Sender"); + else + SetWindowText(TNC->xIDC_TXRX, "Receiver"); + + Msg[12] = 0; + SetWindowText(TNC->xIDC_MODE, Msg); + + // Testing.. I think ZF returns buffers + + EncodeAndSend(TNC, "OZF", 3); + TNC->InternalCmd = 'Z'; + TNC->CommandBusy = TRUE; + } + + return; + } + + if (TNC->InternalCmd == 'Z') // Buffers? + { + Msg[Len] = 0; + SetWindowText(TNC->xIDC_BUFFERS, &Msg[3]); + return; + } + + return; + } + + // Reply to Manual command - Pass to Appl + + Stream = TNC->CmdStream; + STREAM = &TNC->Streams[Stream]; + + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + Buffer[Len - 1] = 13; + Buffer[Len] = 0; + + buffptr->Len = sprintf(&buffptr->Data[0],"AEA} %s", Buffer); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + + } + + if (Opcode == 5) + { + // Link Messages (Connect, etc) + + if (Stream == 15) + { + // SOH $5F X X $00 ETB data acknowledgement + + if (Msg[1] == 'X' && Msg[2] == 'X' && Msg[3] == 0) + { + TNC->DataBusy = FALSE; + + if (TNC->NeedTRANS) // Sent CTRL/Z in conv mode - switch back to trans + { + TNC->NeedTRANS = FALSE; + TNC->TEXTMODE = FALSE; + EncodeAndSend(TNC, "OCETRANS", 8); + Debugprintf("Switching to TRANS"); + TNC->CommandBusy = TRUE; + TNC->InternalCmd = 'A'; + } + else + EncodeAndSend(TNC, "OGG", 3); // Send another Poll + } + return; + } + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "Timeout")) + { + if ((TNC->Streams[Stream].Connecting | TNC->Streams[Stream].Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Failure with %s\r", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = FALSE; // In case! + TNC->Streams[Stream].FramesQueued = 0; + + return; + } + + // Connected, or Disconnecting - Release Session + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; + + if (Stream == 0) + { + // Claar any data from buffers + + EncodeAndSend(TNC, "OTC", 3); // Clear buffers + TNC->NeedPACTOR = 20; + TNC->InternalCmd = 'T'; + TNC->CommandBusy = TRUE; + } + + return; + } + + Call = strstr(Buffer, "NNECTED to"); + + if (Call) + { + Call+=11; // To Callsign + + if (Stream == 0) + { + Buffer[Len-2] = 0; + } + + TNC->Streams[Stream].BytesRXed = TNC->Streams[Stream].BytesTXed = TNC->Streams[Stream].BytesAcked = 0; + TNC->Streams[Stream].ConnectTime = time(NULL); + + if (Stream == 0) + { + // Stop Scanner + + char Msg[80]; + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + ShowTraffic(TNC); + + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, Call, Stream, TRUE); + + if (Stream == 0) + { + struct WL2KInfo * WL2K = TNC->WL2K; + char FreqAppl[10] = ""; // Frequecy-specific application + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig) + { + // If Scan Entry has a Appl, save it + + if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0]) + strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]); + } + + // We are going to Send something, so turn link round + + EncodeAndSend(TNC, "OAG", 3); + TNC->InternalCmd = 'A'; + TNC->CommandBusy = TRUE; + + sprintf(Status, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + + // If an autoconnect APPL is defined, send it + + if (FreqAppl[0]) // Frequency specific APPL overrides TNC APPL + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "%s\r", FreqAppl); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "%s\r", TNC->ApplCmd); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + return; + } + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + char CTBuff[300]; + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + CTBuff[0] = Stream + ' '; + + while (Len > CTPaclen) // CTEXT Paclen + { + memcpy(&CTBuff[1], &CTEXTMSG[Next], CTPaclen); + EncodeAndSend(TNC, CTBuff, CTPaclen + 1); + Next += CTPaclen; + Len -= CTPaclen; + } + + memcpy(&CTBuff[1], &CTEXTMSG[Next], Len); + EncodeAndSend(TNC, CTBuff, Len + 1); + } + return; + + } + else + { + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to %s\r", Call);; + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = TRUE; // Subsequent data to data channel + + if (Stream == 0) + { + sprintf(Status, "%s Connected to %s Outbound", TNC->NodeCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + UpdateMH(TNC, Call, '+', 'O'); + } + + return; + } + } + } +} + + +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, size_t len) +{ + unsigned int i, txptr = 0; + UCHAR c; + + for (i=0; iTXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +static int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, size_t len) +{ + unsigned int i, txptr = 0; + UCHAR c; + + outbuff[0] = SOH; + txptr=1; + + for (i=0; iInternalCmd = 'P'; + TNC->CommandBusy = TRUE; + } + else + { + UCHAR TXMsg[10]; + + sprintf(TXMsg, "%cDI", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 4); // Send twice - must force a disconnect + } +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // Send DI again - can't do much else +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + TNC->NeedPACTOR = 50; +} diff --git a/AFXRES.H b/AFXRES.H new file mode 100644 index 0000000..7c8fe5f --- /dev/null +++ b/AFXRES.H @@ -0,0 +1,750 @@ +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) 1992-1995 Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#ifndef __AFXRES_H__ +#define __AFXRES_H__ + +#ifdef REZ // Mac resource compiler (mrc) defines REZ +#define RC_INVOKED +#endif + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS +#define _INC_WINDOWS + #include "..\include\winres.h" // extract from windows header +#endif +#endif + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, off) +#endif + +#ifdef APSTUDIO_INVOKED +#define APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// MFC resource types (see Technical note TN024 for implementation details) + +#ifndef RC_INVOKED +#define RT_DLGINIT MAKEINTRESOURCE(240) +#define RT_TOOLBAR MAKEINTRESOURCE(241) +#endif + +///////////////////////////////////////////////////////////////////////////// + +#ifdef APSTUDIO_INVOKED +#undef APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// General style bits etc + +// Tab Control styles +#ifndef TCS_MULTILINE // new in later versions of Win32 +#define TCS_MULTILINE 0x0200 +#endif + +// ControlBar styles +#define CBRS_ALIGN_LEFT 0x1000L +#define CBRS_ALIGN_TOP 0x2000L +#define CBRS_ALIGN_RIGHT 0x4000L +#define CBRS_ALIGN_BOTTOM 0x8000L +#define CBRS_ALIGN_ANY 0xF000L + +#define CBRS_BORDER_LEFT 0x0100L +#define CBRS_BORDER_TOP 0x0200L +#define CBRS_BORDER_RIGHT 0x0400L +#define CBRS_BORDER_BOTTOM 0x0800L +#define CBRS_BORDER_ANY 0x0F00L + +#define CBRS_TOOLTIPS 0x0010L +#define CBRS_FLYBY 0x0020L +#define CBRS_FLOAT_MULTI 0x0040L +#define CBRS_BORDER_3D 0x0080L +#define CBRS_HIDE_INPLACE 0x0008L +#define CBRS_SIZE_DYNAMIC 0x0004L +#define CBRS_SIZE_FIXED 0x0002L +#define CBRS_FLOATING 0x0001L + +#define CBRS_ORIENT_HORZ (CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM) +#define CBRS_ORIENT_VERT (CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT) +#define CBRS_ORIENT_ANY (CBRS_ORIENT_HORZ|CBRS_ORIENT_VERT) + +#define CBRS_ALL 0xFFFFL + + +// the CBRS_ style is made up of an alignment style and a draw border style +// the alignment styles are mutually exclusive +// the draw border styles may be combined +#define CBRS_NOALIGN 0x00000000L +#define CBRS_LEFT (CBRS_ALIGN_LEFT|CBRS_BORDER_RIGHT) +#define CBRS_TOP (CBRS_ALIGN_TOP|CBRS_BORDER_BOTTOM) +#define CBRS_RIGHT (CBRS_ALIGN_RIGHT|CBRS_BORDER_LEFT) +#define CBRS_BOTTOM (CBRS_ALIGN_BOTTOM|CBRS_BORDER_TOP) + +///////////////////////////////////////////////////////////////////////////// +// Standard window components + +// Mode indicators in status bar - these are routed like commands +#define ID_INDICATOR_EXT 0xE700 // extended selection indicator +#define ID_INDICATOR_CAPS 0xE701 // cap lock indicator +#define ID_INDICATOR_NUM 0xE702 // num lock indicator +#define ID_INDICATOR_SCRL 0xE703 // scroll lock indicator +#define ID_INDICATOR_OVR 0xE704 // overtype mode indicator +#define ID_INDICATOR_REC 0xE705 // record mode indicator +#define ID_INDICATOR_KANA 0xE706 // kana lock indicator + +#define ID_SEPARATOR 0 // special separator value + +#ifndef RC_INVOKED // code only +// Standard control bars (IDW = window ID) +#define AFX_IDW_CONTROLBAR_FIRST 0xE800 +#define AFX_IDW_CONTROLBAR_LAST 0xE8FF + +#define AFX_IDW_TOOLBAR 0xE800 // main Toolbar for window +#define AFX_IDW_STATUS_BAR 0xE801 // Status bar window +#define AFX_IDW_PREVIEW_BAR 0xE802 // PrintPreview Dialog Bar +#define AFX_IDW_RESIZE_BAR 0xE803 // OLE in-place resize bar + +// Note: If your application supports docking toolbars, you should +// not use the following IDs for your own toolbars. The IDs chosen +// are at the top of the first 32 such that the bars will be hidden +// while in print preview mode, and are not likely to conflict with +// IDs your application may have used succesfully in the past. + +#define AFX_IDW_DOCKBAR_TOP 0xE81B +#define AFX_IDW_DOCKBAR_LEFT 0xE81C +#define AFX_IDW_DOCKBAR_RIGHT 0xE81D +#define AFX_IDW_DOCKBAR_BOTTOM 0xE81E +#define AFX_IDW_DOCKBAR_FLOAT 0xE81F + +// Macro for mapping standard control bars to bitmask (limit of 32) +#define AFX_CONTROLBAR_MASK(nIDC) (1L << (nIDC - AFX_IDW_CONTROLBAR_FIRST)) + +// parts of Main Frame +#define AFX_IDW_PANE_FIRST 0xE900 // first pane (256 max) +#define AFX_IDW_PANE_LAST 0xE9ff +#define AFX_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) +#define AFX_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) + +#define AFX_IDW_SIZE_BOX 0xEA20 // size box for splitters +#define AFX_IDW_PANE_SAVE 0xEA21 // to shift AFX_IDW_PANE_FIRST +#endif //!RC_INVOKED + +#ifndef APSTUDIO_INVOKED + +// common style for form views +#define AFX_WS_DEFAULT_VIEW (WS_CHILD | WS_VISIBLE | WS_BORDER) + +#endif //!APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// Standard app configurable strings + +// for application title (defaults to EXE name or name in constructor) +#define AFX_IDS_APP_TITLE 0xE000 +// idle message bar line +#define AFX_IDS_IDLEMESSAGE 0xE001 +// message bar line when in shift-F1 help mode +#define AFX_IDS_HELPMODEMESSAGE 0xE002 +// document title when editing OLE embedding +#define AFX_IDS_APP_TITLE_EMBEDDING 0xE003 +// company name +#define AFX_IDS_COMPANY_NAME 0xE004 +// object name when server is inplace +#define AFX_IDS_OBJ_TITLE_INPLACE 0xE005 + +///////////////////////////////////////////////////////////////////////////// +// Standard Commands + +// File commands +#define ID_FILE_NEW 0xE100 +#define ID_FILE_OPEN 0xE101 +#define ID_FILE_CLOSE 0xE102 +#define ID_FILE_SAVE 0xE103 +#define ID_FILE_SAVE_AS 0xE104 +#define ID_FILE_PAGE_SETUP 0xE105 +#define ID_FILE_PRINT_SETUP 0xE106 +#define ID_FILE_PRINT 0xE107 +#define ID_FILE_PRINT_DIRECT 0xE108 +#define ID_FILE_PRINT_PREVIEW 0xE109 +#define ID_FILE_UPDATE 0xE10A +#define ID_FILE_SAVE_COPY_AS 0xE10B +#define ID_FILE_SEND_MAIL 0xE10C + +#define ID_FILE_MRU_FIRST 0xE110 +#define ID_FILE_MRU_FILE1 0xE110 // range - 16 max +#define ID_FILE_MRU_FILE2 0xE111 +#define ID_FILE_MRU_FILE3 0xE112 +#define ID_FILE_MRU_FILE4 0xE113 +#define ID_FILE_MRU_FILE5 0xE114 +#define ID_FILE_MRU_FILE6 0xE115 +#define ID_FILE_MRU_FILE7 0xE116 +#define ID_FILE_MRU_FILE8 0xE117 +#define ID_FILE_MRU_FILE9 0xE118 +#define ID_FILE_MRU_FILE10 0xE119 +#define ID_FILE_MRU_FILE11 0xE11A +#define ID_FILE_MRU_FILE12 0xE11B +#define ID_FILE_MRU_FILE13 0xE11C +#define ID_FILE_MRU_FILE14 0xE11D +#define ID_FILE_MRU_FILE15 0xE11E +#define ID_FILE_MRU_FILE16 0xE11F +#define ID_FILE_MRU_LAST 0xE11F + +// Edit commands +#define ID_EDIT_CLEAR 0xE120 +#define ID_EDIT_CLEAR_ALL 0xE121 +#define ID_EDIT_COPY 0xE122 +#define ID_EDIT_CUT 0xE123 +#define ID_EDIT_FIND 0xE124 +#define ID_EDIT_PASTE 0xE125 +#define ID_EDIT_PASTE_LINK 0xE126 +#define ID_EDIT_PASTE_SPECIAL 0xE127 +#define ID_EDIT_REPEAT 0xE128 +#define ID_EDIT_REPLACE 0xE129 +#define ID_EDIT_SELECT_ALL 0xE12A +#define ID_EDIT_UNDO 0xE12B +#define ID_EDIT_REDO 0xE12C + +// Window commands +#define ID_WINDOW_NEW 0xE130 +#define ID_WINDOW_ARRANGE 0xE131 +#define ID_WINDOW_CASCADE 0xE132 +#define ID_WINDOW_TILE_HORZ 0xE133 +#define ID_WINDOW_TILE_VERT 0xE134 +#define ID_WINDOW_SPLIT 0xE135 +#ifndef RC_INVOKED // code only +#define AFX_IDM_WINDOW_FIRST 0xE130 +#define AFX_IDM_WINDOW_LAST 0xE13F +#define AFX_IDM_FIRST_MDICHILD 0xFF00 // window list starts here +#endif //!RC_INVOKED + +// Help and App commands +#define ID_APP_ABOUT 0xE140 +#define ID_APP_EXIT 0xE141 +#define ID_HELP_INDEX 0xE142 +#define ID_HELP_FINDER 0xE143 +#define ID_HELP_USING 0xE144 +#define ID_CONTEXT_HELP 0xE145 // shift-F1 +// special commands for processing help +#define ID_HELP 0xE146 // first attempt for F1 +#define ID_DEFAULT_HELP 0xE147 // last attempt + +// Misc +#define ID_NEXT_PANE 0xE150 +#define ID_PREV_PANE 0xE151 + +// Format +#define ID_FORMAT_FONT 0xE160 + +// OLE commands +#define ID_OLE_INSERT_NEW 0xE200 +#define ID_OLE_EDIT_LINKS 0xE201 +#define ID_OLE_EDIT_CONVERT 0xE202 +#define ID_OLE_EDIT_CHANGE_ICON 0xE203 +#define ID_OLE_EDIT_PROPERTIES 0xE204 +#define ID_OLE_VERB_FIRST 0xE210 // range - 16 max +#ifndef RC_INVOKED // code only +#define ID_OLE_VERB_LAST 0xE21F +#endif //!RC_INVOKED + +// for print preview dialog bar +#define AFX_ID_PREVIEW_CLOSE 0xE300 +#define AFX_ID_PREVIEW_NUMPAGE 0xE301 // One/Two Page button +#define AFX_ID_PREVIEW_NEXT 0xE302 +#define AFX_ID_PREVIEW_PREV 0xE303 +#define AFX_ID_PREVIEW_PRINT 0xE304 +#define AFX_ID_PREVIEW_ZOOMIN 0xE305 +#define AFX_ID_PREVIEW_ZOOMOUT 0xE306 + +// View commands (same number used as IDW used for control bar) +#define ID_VIEW_TOOLBAR 0xE800 +#define ID_VIEW_STATUS_BAR 0xE801 + // -> E8FF reserved for other control bar commands + +// RecordForm commands +#define ID_RECORD_FIRST 0xE900 +#define ID_RECORD_LAST 0xE901 +#define ID_RECORD_NEXT 0xE902 +#define ID_RECORD_PREV 0xE903 + +///////////////////////////////////////////////////////////////////////////// +// Standard control IDs + +#ifdef IDC_STATIC +#undef IDC_STATIC +#endif +#define IDC_STATIC (-1) // all static controls + +///////////////////////////////////////////////////////////////////////////// +// Standard string error/warnings + +#ifndef RC_INVOKED // code only +#define AFX_IDS_SCFIRST 0xEF00 +#endif //!RC_INVOKED + +#define AFX_IDS_SCSIZE 0xEF00 +#define AFX_IDS_SCMOVE 0xEF01 +#define AFX_IDS_SCMINIMIZE 0xEF02 +#define AFX_IDS_SCMAXIMIZE 0xEF03 +#define AFX_IDS_SCNEXTWINDOW 0xEF04 +#define AFX_IDS_SCPREVWINDOW 0xEF05 +#define AFX_IDS_SCCLOSE 0xEF06 +#define AFX_IDS_SCRESTORE 0xEF12 +#define AFX_IDS_SCTASKLIST 0xEF13 + +#define AFX_IDS_MDICHILD 0xEF1F + +#define AFX_IDS_DESKACCESSORY 0xEFDA + +// General strings +#define AFX_IDS_OPENFILE 0xF000 +#define AFX_IDS_SAVEFILE 0xF001 +#define AFX_IDS_ALLFILTER 0xF002 +#define AFX_IDS_UNTITLED 0xF003 +#define AFX_IDS_SAVEFILECOPY 0xF004 +#define AFX_IDS_PREVIEW_CLOSE 0xF005 +#define AFX_IDS_UNNAMED_FILE 0xF006 +#ifdef _MAC +#define AFX_IDS_ABOUT 0xF010 +#endif +#define AFX_IDS_HIDE 0xF011 + +// MFC Standard Exception Error messages +#define AFX_IDP_NO_ERROR_AVAILABLE 0xF020 +#define AFX_IDS_NOT_SUPPORTED_EXCEPTION 0xF021 +#define AFX_IDS_RESOURCE_EXCEPTION 0xF022 +#define AFX_IDS_MEMORY_EXCEPTION 0xF023 +#define AFX_IDS_USER_EXCEPTION 0xF024 + +// Printing and print preview strings +#define AFX_IDS_PRINTONPORT 0xF040 +#define AFX_IDS_ONEPAGE 0xF041 +#define AFX_IDS_TWOPAGE 0xF042 +#define AFX_IDS_PRINTPAGENUM 0xF043 +#define AFX_IDS_PREVIEWPAGEDESC 0xF044 +#define AFX_IDS_PRINTDEFAULTEXT 0xF045 +#define AFX_IDS_PRINTDEFAULT 0xF046 +#define AFX_IDS_PRINTFILTER 0xF047 +#define AFX_IDS_PRINTCAPTION 0xF048 +#define AFX_IDS_PRINTTOFILE 0xF049 + + +// OLE strings +#define AFX_IDS_OBJECT_MENUITEM 0xF080 +#define AFX_IDS_EDIT_VERB 0xF081 +#define AFX_IDS_ACTIVATE_VERB 0xF082 +#define AFX_IDS_CHANGE_LINK 0xF083 +#define AFX_IDS_AUTO 0xF084 +#define AFX_IDS_MANUAL 0xF085 +#define AFX_IDS_FROZEN 0xF086 +#define AFX_IDS_ALL_FILES 0xF087 +// dynamically changing menu items +#define AFX_IDS_SAVE_MENU 0xF088 +#define AFX_IDS_UPDATE_MENU 0xF089 +#define AFX_IDS_SAVE_AS_MENU 0xF08A +#define AFX_IDS_SAVE_COPY_AS_MENU 0xF08B +#define AFX_IDS_EXIT_MENU 0xF08C +#define AFX_IDS_UPDATING_ITEMS 0xF08D +// COlePasteSpecialDialog defines +#define AFX_IDS_METAFILE_FORMAT 0xF08E +#define AFX_IDS_DIB_FORMAT 0xF08F +#define AFX_IDS_BITMAP_FORMAT 0xF090 +#define AFX_IDS_LINKSOURCE_FORMAT 0xF091 +#define AFX_IDS_EMBED_FORMAT 0xF092 +// other OLE utility strings +#define AFX_IDS_PASTELINKEDTYPE 0xF094 +#define AFX_IDS_UNKNOWNTYPE 0xF095 +#define AFX_IDS_RTF_FORMAT 0xF096 +#define AFX_IDS_TEXT_FORMAT 0xF097 +// OLE datatype format error strings +#define AFX_IDS_INVALID_CURRENCY 0xF098 +#define AFX_IDS_INVALID_DATETIME 0xF099 +#define AFX_IDS_INVALID_DATETIMESPAN 0xF09A + +// General error / prompt strings +#define AFX_IDP_INVALID_FILENAME 0xF100 +#define AFX_IDP_FAILED_TO_OPEN_DOC 0xF101 +#define AFX_IDP_FAILED_TO_SAVE_DOC 0xF102 +#define AFX_IDP_ASK_TO_SAVE 0xF103 +#define AFX_IDP_FAILED_TO_CREATE_DOC 0xF104 +#define AFX_IDP_FILE_TOO_LARGE 0xF105 +#define AFX_IDP_FAILED_TO_START_PRINT 0xF106 +#define AFX_IDP_FAILED_TO_LAUNCH_HELP 0xF107 +#define AFX_IDP_INTERNAL_FAILURE 0xF108 // general failure +#define AFX_IDP_COMMAND_FAILURE 0xF109 // command failure +#define AFX_IDP_FAILED_MEMORY_ALLOC 0xF10A + +// DDV parse errors +#define AFX_IDP_PARSE_INT 0xF110 +#define AFX_IDP_PARSE_REAL 0xF111 +#define AFX_IDP_PARSE_INT_RANGE 0xF112 +#define AFX_IDP_PARSE_REAL_RANGE 0xF113 +#define AFX_IDP_PARSE_STRING_SIZE 0xF114 +#define AFX_IDP_PARSE_RADIO_BUTTON 0xF115 +#define AFX_IDP_PARSE_BYTE 0xF116 +#define AFX_IDP_PARSE_UINT 0xF117 +#define AFX_IDP_PARSE_DATETIME 0xF118 +#define AFX_IDP_PARSE_CURRENCY 0xF119 + +// CFile/CArchive error strings for user failure +#define AFX_IDP_FAILED_INVALID_FORMAT 0xF120 +#define AFX_IDP_FAILED_INVALID_PATH 0xF121 +#define AFX_IDP_FAILED_DISK_FULL 0xF122 +#define AFX_IDP_FAILED_ACCESS_READ 0xF123 +#define AFX_IDP_FAILED_ACCESS_WRITE 0xF124 +#define AFX_IDP_FAILED_IO_ERROR_READ 0xF125 +#define AFX_IDP_FAILED_IO_ERROR_WRITE 0xF126 + +// OLE errors / prompt strings +#define AFX_IDP_STATIC_OBJECT 0xF180 +#define AFX_IDP_FAILED_TO_CONNECT 0xF181 +#define AFX_IDP_SERVER_BUSY 0xF182 +#define AFX_IDP_BAD_VERB 0xF183 +#define AFX_IDP_FAILED_TO_NOTIFY 0xF185 +#define AFX_IDP_FAILED_TO_LAUNCH 0xF186 +#define AFX_IDP_ASK_TO_UPDATE 0xF187 +#define AFX_IDP_FAILED_TO_UPDATE 0xF188 +#define AFX_IDP_FAILED_TO_REGISTER 0xF189 +#define AFX_IDP_FAILED_TO_AUTO_REGISTER 0xF18A +#define AFX_IDP_FAILED_TO_CONVERT 0xF18B +#define AFX_IDP_GET_NOT_SUPPORTED 0xF18C +#define AFX_IDP_SET_NOT_SUPPORTED 0xF18D +#define AFX_IDP_ASK_TO_DISCARD 0xF18E +#define AFX_IDP_FAILED_TO_CREATE 0xF18F + +// MAPI errors / prompt strings +#define AFX_IDP_FAILED_MAPI_LOAD 0xF190 +#define AFX_IDP_INVALID_MAPI_DLL 0xF191 +#define AFX_IDP_FAILED_MAPI_SEND 0xF192 + +#define AFX_IDP_FILE_NONE 0xF1A0 +#define AFX_IDP_FILE_GENERIC 0xF1A1 +#define AFX_IDP_FILE_NOT_FOUND 0xF1A2 +#define AFX_IDP_FILE_BAD_PATH 0xF1A3 +#define AFX_IDP_FILE_TOO_MANY_OPEN 0xF1A4 +#define AFX_IDP_FILE_ACCESS_DENIED 0xF1A5 +#define AFX_IDP_FILE_INVALID_FILE 0xF1A6 +#define AFX_IDP_FILE_REMOVE_CURRENT 0xF1A7 +#define AFX_IDP_FILE_DIR_FULL 0xF1A8 +#define AFX_IDP_FILE_BAD_SEEK 0xF1A9 +#define AFX_IDP_FILE_HARD_IO 0xF1AA +#define AFX_IDP_FILE_SHARING 0xF1AB +#define AFX_IDP_FILE_LOCKING 0xF1AC +#define AFX_IDP_FILE_DISKFULL 0xF1AD +#define AFX_IDP_FILE_EOF 0xF1AE + +#define AFX_IDP_ARCH_NONE 0xF1B0 +#define AFX_IDP_ARCH_GENERIC 0xF1B1 +#define AFX_IDP_ARCH_READONLY 0xF1B2 +#define AFX_IDP_ARCH_ENDOFFILE 0xF1B3 +#define AFX_IDP_ARCH_WRITEONLY 0xF1B4 +#define AFX_IDP_ARCH_BADINDEX 0xF1B5 +#define AFX_IDP_ARCH_BADCLASS 0xF1B6 +#define AFX_IDP_ARCH_BADSCHEMA 0xF1B7 + +#define AFX_IDS_OCC_SCALEUNITS_PIXELS 0xF1C0 + +// 0xf200-0xf20f reserved + +// font names and point sizes +#define AFX_IDS_STATUS_FONT 0xF230 +#define AFX_IDS_TOOLTIP_FONT 0xF231 +#define AFX_IDS_UNICODE_FONT 0xF232 +#define AFX_IDS_MINI_FONT 0xF233 + +// ODBC Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_SQL_FIRST 0xF280 +#endif //!RC_INVOKED +#define AFX_IDP_SQL_CONNECT_FAIL 0xF281 +#define AFX_IDP_SQL_RECORDSET_FORWARD_ONLY 0xF282 +#define AFX_IDP_SQL_EMPTY_COLUMN_LIST 0xF283 +#define AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH 0xF284 +#define AFX_IDP_SQL_ILLEGAL_MODE 0xF285 +#define AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED 0xF286 +#define AFX_IDP_SQL_NO_CURRENT_RECORD 0xF287 +#define AFX_IDP_SQL_NO_ROWS_AFFECTED 0xF288 +#define AFX_IDP_SQL_RECORDSET_READONLY 0xF289 +#define AFX_IDP_SQL_SQL_NO_TOTAL 0xF28A +#define AFX_IDP_SQL_ODBC_LOAD_FAILED 0xF28B +#define AFX_IDP_SQL_DYNASET_NOT_SUPPORTED 0xF28C +#define AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED 0xF28D +#define AFX_IDP_SQL_API_CONFORMANCE 0xF28E +#define AFX_IDP_SQL_SQL_CONFORMANCE 0xF28F +#define AFX_IDP_SQL_NO_DATA_FOUND 0xF290 +#define AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED 0xF291 +#define AFX_IDP_SQL_ODBC_V2_REQUIRED 0xF292 +#define AFX_IDP_SQL_NO_POSITIONED_UPDATES 0xF293 +#define AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED 0xF294 +#define AFX_IDP_SQL_DATA_TRUNCATED 0xF295 +#define AFX_IDP_SQL_ROW_FETCH 0xF296 +#define AFX_IDP_SQL_INCORRECT_ODBC 0xF297 +#define AFX_IDP_SQL_UPDATE_DELETE_FAILED 0xF298 +#define AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED 0xF299 + +// DAO Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_DAO_FIRST 0xF2A0 +#endif //!RC_INVOKED +#define AFX_IDP_DAO_ENGINE_INITIALIZATION 0xF2A0 +#define AFX_IDP_DAO_DFX_BIND 0xF2A1 +#define AFX_IDP_DAO_OBJECT_NOT_OPEN 0xF2A2 + +// ICDAORecordset::GetRows Errors +// These are not placed in DAO Errors collection +// and must be handled directly by MFC. +#define AFX_IDP_DAO_ROWTOOSHORT 0xF2A3 +#define AFX_IDP_DAO_BADBINDINFO 0xF2A4 +#define AFX_IDP_DAO_COLUMNUNAVAILABLE 0xF2A5 + +///////////////////////////////////////////////////////////////////////////// +// AFX implementation - control IDs (AFX_IDC) + +// Parts of dialogs +#define AFX_IDC_LISTBOX 100 +#define AFX_IDC_CHANGE 101 + +// for print dialog +#define AFX_IDC_PRINT_DOCNAME 201 +#define AFX_IDC_PRINT_PRINTERNAME 202 +#define AFX_IDC_PRINT_PORTNAME 203 +#define AFX_IDC_PRINT_PAGENUM 204 + +// Property Sheet control id's (determined with Spy++) +#define ID_APPLY_NOW 0x3021 +#define ID_WIZBACK 0x3023 +#define ID_WIZNEXT 0x3024 +#define ID_WIZFINISH 0x3025 +#define AFX_IDC_TAB_CONTROL 0x3020 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for standard components + +#ifndef RC_INVOKED // code only +// These are really COMMDLG dialogs, so there usually isn't a resource +// for them, but these IDs are used as help IDs. +#define AFX_IDD_FILEOPEN 28676 +#define AFX_IDD_FILESAVE 28677 +#define AFX_IDD_FONT 28678 +#define AFX_IDD_COLOR 28679 +#define AFX_IDD_PRINT 28680 +#define AFX_IDD_PRINTSETUP 28681 +#define AFX_IDD_FIND 28682 +#define AFX_IDD_REPLACE 28683 +#endif //!RC_INVOKED + +// Standard dialogs app should leave alone (0x7801->) +#define AFX_IDD_NEWTYPEDLG 30721 +#define AFX_IDD_PRINTDLG 30722 +#define AFX_IDD_PREVIEW_TOOLBAR 30723 +#ifdef _MAC +#define AFX_IDD_PREVIEW_SHORTTOOLBAR 30731 +#endif + +// Dialogs defined for OLE2UI library +#define AFX_IDD_INSERTOBJECT 30724 +#define AFX_IDD_CHANGEICON 30725 +#define AFX_IDD_CONVERT 30726 +#define AFX_IDD_PASTESPECIAL 30727 +#define AFX_IDD_EDITLINKS 30728 +#define AFX_IDD_FILEBROWSE 30729 +#define AFX_IDD_BUSY 30730 + +#define AFX_IDD_OBJECTPROPERTIES 30732 +#define AFX_IDD_CHANGESOURCE 30733 + +// Standard cursors (0x7901->) + // AFX_IDC = Cursor resources +#define AFX_IDC_CONTEXTHELP 30977 // context sensitive help +#define AFX_IDC_MAGNIFY 30978 // print preview zoom +#define AFX_IDC_SMALLARROWS 30979 // splitter +#define AFX_IDC_HSPLITBAR 30980 // splitter +#define AFX_IDC_VSPLITBAR 30981 // splitter +#define AFX_IDC_NODROPCRSR 30982 // No Drop Cursor +#define AFX_IDC_TRACKNWSE 30983 // tracker +#define AFX_IDC_TRACKNESW 30984 // tracker +#define AFX_IDC_TRACKNS 30985 // tracker +#define AFX_IDC_TRACKWE 30986 // tracker +#define AFX_IDC_TRACK4WAY 30987 // tracker +#define AFX_IDC_MOVE4WAY 30988 // resize bar (server only) + +// Mini frame window bitmap ID +#define AFX_IDB_MINIFRAME_MENU 30994 + +// CheckListBox checks bitmap ID +#define AFX_IDB_CHECKLISTBOX_NT 30995 +#define AFX_IDB_CHECKLISTBOX_95 30996 + +// AFX standard accelerator resources +#define AFX_IDR_PREVIEW_ACCEL 30997 + +// AFX standard ICON IDs (for MFC V1 apps) (0x7A01->) +#define AFX_IDI_STD_MDIFRAME 31233 +#define AFX_IDI_STD_FRAME 31234 + +///////////////////////////////////////////////////////////////////////////// +// AFX OLE control implementation - control IDs (AFX_IDC) + +// Font property page +#define AFX_IDC_FONTPROP 1000 +#define AFX_IDC_FONTNAMES 1001 +#define AFX_IDC_FONTSTYLES 1002 +#define AFX_IDC_FONTSIZES 1003 +#define AFX_IDC_STRIKEOUT 1004 +#define AFX_IDC_UNDERLINE 1005 +#define AFX_IDC_SAMPLEBOX 1006 + +// Color property page +#define AFX_IDC_COLOR_BLACK 1100 +#define AFX_IDC_COLOR_WHITE 1101 +#define AFX_IDC_COLOR_RED 1102 +#define AFX_IDC_COLOR_GREEN 1103 +#define AFX_IDC_COLOR_BLUE 1104 +#define AFX_IDC_COLOR_YELLOW 1105 +#define AFX_IDC_COLOR_MAGENTA 1106 +#define AFX_IDC_COLOR_CYAN 1107 +#define AFX_IDC_COLOR_GRAY 1108 +#define AFX_IDC_COLOR_LIGHTGRAY 1109 +#define AFX_IDC_COLOR_DARKRED 1110 +#define AFX_IDC_COLOR_DARKGREEN 1111 +#define AFX_IDC_COLOR_DARKBLUE 1112 +#define AFX_IDC_COLOR_LIGHTBROWN 1113 +#define AFX_IDC_COLOR_DARKMAGENTA 1114 +#define AFX_IDC_COLOR_DARKCYAN 1115 +#define AFX_IDC_COLORPROP 1116 +#define AFX_IDC_SYSTEMCOLORS 1117 + +// Picture porperty page +#define AFX_IDC_PROPNAME 1201 +#define AFX_IDC_PICTURE 1202 +#define AFX_IDC_BROWSE 1203 +#define AFX_IDC_CLEAR 1204 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for OLE control standard components + +// Standard propery page dialogs app should leave alone (0x7E01->) +#define AFX_IDD_PROPPAGE_COLOR 32257 +#define AFX_IDD_PROPPAGE_FONT 32258 +#define AFX_IDD_PROPPAGE_PICTURE 32259 + +#define AFX_IDB_TRUETYPE 32384 + +///////////////////////////////////////////////////////////////////////////// +// Standard OLE control strings + +// OLE Control page strings +#define AFX_IDS_PROPPAGE_UNKNOWN 0xFE01 +#define AFX_IDS_COLOR_DESKTOP 0xFE04 +#define AFX_IDS_COLOR_APPWORKSPACE 0xFE05 +#define AFX_IDS_COLOR_WNDBACKGND 0xFE06 +#define AFX_IDS_COLOR_WNDTEXT 0xFE07 +#define AFX_IDS_COLOR_MENUBAR 0xFE08 +#define AFX_IDS_COLOR_MENUTEXT 0xFE09 +#define AFX_IDS_COLOR_ACTIVEBAR 0xFE0A +#define AFX_IDS_COLOR_INACTIVEBAR 0xFE0B +#define AFX_IDS_COLOR_ACTIVETEXT 0xFE0C +#define AFX_IDS_COLOR_INACTIVETEXT 0xFE0D +#define AFX_IDS_COLOR_ACTIVEBORDER 0xFE0E +#define AFX_IDS_COLOR_INACTIVEBORDER 0xFE0F +#define AFX_IDS_COLOR_WNDFRAME 0xFE10 +#define AFX_IDS_COLOR_SCROLLBARS 0xFE11 +#define AFX_IDS_COLOR_BTNFACE 0xFE12 +#define AFX_IDS_COLOR_BTNSHADOW 0xFE13 +#define AFX_IDS_COLOR_BTNTEXT 0xFE14 +#define AFX_IDS_COLOR_BTNHIGHLIGHT 0xFE15 +#define AFX_IDS_COLOR_DISABLEDTEXT 0xFE16 +#define AFX_IDS_COLOR_HIGHLIGHT 0xFE17 +#define AFX_IDS_COLOR_HIGHLIGHTTEXT 0xFE18 +#define AFX_IDS_REGULAR 0xFE19 +#define AFX_IDS_BOLD 0xFE1A +#define AFX_IDS_ITALIC 0xFE1B +#define AFX_IDS_BOLDITALIC 0xFE1C +#define AFX_IDS_SAMPLETEXT 0xFE1D +#define AFX_IDS_DISPLAYSTRING_FONT 0xFE1E +#define AFX_IDS_DISPLAYSTRING_COLOR 0xFE1F +#define AFX_IDS_DISPLAYSTRING_PICTURE 0xFE20 +#define AFX_IDS_PICTUREFILTER 0xFE21 +#define AFX_IDS_PICTYPE_UNKNOWN 0xFE22 +#define AFX_IDS_PICTYPE_NONE 0xFE23 +#define AFX_IDS_PICTYPE_BITMAP 0xFE24 +#define AFX_IDS_PICTYPE_METAFILE 0xFE25 +#define AFX_IDS_PICTYPE_ICON 0xFE26 +#define AFX_IDS_COLOR_PPG 0xFE28 +#define AFX_IDS_COLOR_PPG_CAPTION 0xFE29 +#define AFX_IDS_FONT_PPG 0xFE2A +#define AFX_IDS_FONT_PPG_CAPTION 0xFE2B +#define AFX_IDS_PICTURE_PPG 0xFE2C +#define AFX_IDS_PICTURE_PPG_CAPTION 0xFE2D +#define AFX_IDS_PICTUREBROWSETITLE 0xFE30 +#define AFX_IDS_BORDERSTYLE_0 0xFE31 +#define AFX_IDS_BORDERSTYLE_1 0xFE32 + +// OLE Control verb names +#define AFX_IDS_VERB_EDIT 0xFE40 +#define AFX_IDS_VERB_PROPERTIES 0xFE41 + +// OLE Control internal error messages +#define AFX_IDP_PICTURECANTOPEN 0xFE83 +#define AFX_IDP_PICTURECANTLOAD 0xFE84 +#define AFX_IDP_PICTURETOOLARGE 0xFE85 +#define AFX_IDP_PICTUREREADFAILED 0xFE86 + +// Standard OLE Control error strings +#define AFX_IDP_E_ILLEGALFUNCTIONCALL 0xFEA0 +#define AFX_IDP_E_OVERFLOW 0xFEA1 +#define AFX_IDP_E_OUTOFMEMORY 0xFEA2 +#define AFX_IDP_E_DIVISIONBYZERO 0xFEA3 +#define AFX_IDP_E_OUTOFSTRINGSPACE 0xFEA4 +#define AFX_IDP_E_OUTOFSTACKSPACE 0xFEA5 +#define AFX_IDP_E_BADFILENAMEORNUMBER 0xFEA6 +#define AFX_IDP_E_FILENOTFOUND 0xFEA7 +#define AFX_IDP_E_BADFILEMODE 0xFEA8 +#define AFX_IDP_E_FILEALREADYOPEN 0xFEA9 +#define AFX_IDP_E_DEVICEIOERROR 0xFEAA +#define AFX_IDP_E_FILEALREADYEXISTS 0xFEAB +#define AFX_IDP_E_BADRECORDLENGTH 0xFEAC +#define AFX_IDP_E_DISKFULL 0xFEAD +#define AFX_IDP_E_BADRECORDNUMBER 0xFEAE +#define AFX_IDP_E_BADFILENAME 0xFEAF +#define AFX_IDP_E_TOOMANYFILES 0xFEB0 +#define AFX_IDP_E_DEVICEUNAVAILABLE 0xFEB1 +#define AFX_IDP_E_PERMISSIONDENIED 0xFEB2 +#define AFX_IDP_E_DISKNOTREADY 0xFEB3 +#define AFX_IDP_E_PATHFILEACCESSERROR 0xFEB4 +#define AFX_IDP_E_PATHNOTFOUND 0xFEB5 +#define AFX_IDP_E_INVALIDPATTERNSTRING 0xFEB6 +#define AFX_IDP_E_INVALIDUSEOFNULL 0xFEB7 +#define AFX_IDP_E_INVALIDFILEFORMAT 0xFEB8 +#define AFX_IDP_E_INVALIDPROPERTYVALUE 0xFEB9 +#define AFX_IDP_E_INVALIDPROPERTYARRAYINDEX 0xFEBA +#define AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME 0xFEBB +#define AFX_IDP_E_SETNOTSUPPORTED 0xFEBC +#define AFX_IDP_E_NEEDPROPERTYARRAYINDEX 0xFEBD +#define AFX_IDP_E_SETNOTPERMITTED 0xFEBE +#define AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME 0xFEBF +#define AFX_IDP_E_GETNOTSUPPORTED 0xFEC0 +#define AFX_IDP_E_PROPERTYNOTFOUND 0xFEC1 +#define AFX_IDP_E_INVALIDCLIPBOARDFORMAT 0xFEC2 +#define AFX_IDP_E_INVALIDPICTURE 0xFEC3 +#define AFX_IDP_E_PRINTERERROR 0xFEC4 +#define AFX_IDP_E_CANTSAVEFILETOTEMP 0xFEC5 +#define AFX_IDP_E_SEARCHTEXTNOTFOUND 0xFEC6 +#define AFX_IDP_E_REPLACEMENTSTOOLONG 0xFEC7 + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, on) +#endif + +#endif //__AFXRES_H__ + +///////////////////////////////////////////////////////////////////////////// diff --git a/AGWAPI.c b/AGWAPI.c new file mode 100644 index 0000000..61cb95b --- /dev/null +++ b/AGWAPI.c @@ -0,0 +1,1672 @@ +/* +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 +*/ + +/* + + AGWPE emulation Interface for BPQ32 + + Based on AGWtoBPQ + +*/ +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +#include "bpq32.h" + +// Internal AGW Interface + +//#define VVICON 400 + +struct AGWHeader +{ + int Port; + unsigned char DataKind; + unsigned char filler2; + unsigned char PID; + unsigned char filler3; + unsigned char callfrom[10]; + unsigned char callto[10]; + int DataLength; + int reserved; +}; + +struct AGWSocketConnectionInfo +{ + int Number; // Number of record - for AGWConnections display + SOCKET socket; + SOCKADDR_IN sin; + BOOL SocketActive; + BOOL RawFlag; + BOOL MonFlag; + unsigned char CallSign1[10]; + unsigned char CallSign2[10]; + BOOL GotHeader; + int MsgDataLength; + struct AGWHeader AGWRXHeader; +}; + +struct BPQConnectionInfo +{ + struct AGWSocketConnectionInfo * SocketIndex; + int BPQStream; + unsigned char CallKey[21]; // Port + two calls + BOOL Connecting; // Set while waiting for connection to complete + BOOL Listening; + int ApplMask; +} ConInfoRec; + + +char AGWPorts[1000]; + +byte AGWMessage[1000]; + +struct AGWHeader AGWTXHeader; + +char SessionList[100]; + +struct BPQConnectionInfo AGWConnections[65]; + +#define MaxSockets 64 + +static struct AGWSocketConnectionInfo Sockets[MaxSockets+1]; + +int CurrentConnections; + +static int CurrentSockets=0; + +int AGWPort = 0; +int AGWSessions = 0; +int AGWMask = 0; + +BOOL LoopMonFlag = FALSE; +BOOL Loopflag = FALSE; + +extern char pgm[256]; + +SOCKET agwsock; + +extern BPQVECSTRUC * AGWMONVECPTR; + +extern int SemHeldByAPI; + +char szBuff[80]; + +static int VisiblePortToRealPort[32]; + +int SetUpHostSessions(); +int DisplaySessions(); +int AGWDoStateChange(int Stream); +int AGWDoReceivedData(int Stream); +int AGWDoMonitorData(); +int AGWConnected(struct BPQConnectionInfo * Con, int Stream); +int AGWDisconnected(struct BPQConnectionInfo * Con, int Stream); +int DeleteConnection(struct BPQConnectionInfo * Con); +int SendConMsgtoAppl(BOOL Incomming, struct BPQConnectionInfo * Con, char * CallSign); +int SendDisMsgtoAppl(char * Msg, struct AGWSocketConnectionInfo * sockptr); +int AGWSocket_Accept(SOCKET SocketId); +int Socket_Data(int SocketId,int error, int eventcode); +int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock); +int DataSocket_Write(struct AGWSocketConnectionInfo * sockptr, SOCKET sock); +int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr); +int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr); +int SendDataToAppl(int Stream, byte * Buffer, int Length); +int InternalAGWDecodeFrame(char * msg, char * buffer, int Stamp, int * FrameType); +int AGWDataSocket_Disconnect( struct AGWSocketConnectionInfo * sockptr); +int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length); +int ShowApps(); +int Terminate(); +int SendtoSocket(SOCKET sock,char * Msg); +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); + +VOID Poll_AGW() +{ + int state, change, i; + int Stream; + struct BPQConnectionInfo * Con; + struct AGWSocketConnectionInfo * sockptr; + + // Look for incoming connects + + fd_set readfd, writefd, exceptfd; + struct timeval timeout; + int retval; + int n; + int Active = 0; + SOCKET maxsock; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + FD_ZERO(&readfd); + + FD_SET(agwsock, &readfd); + + retval = select((int)agwsock + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + retval = WSAGetLastError(); + perror("listen select"); + } + + if (retval) + if (FD_ISSET((int)agwsock, &readfd)) + AGWSocket_Accept(agwsock); + + // look for data on any active sockets + + maxsock = 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + + // Check for data on active streams + + for (i = 0; i < CurrentConnections; i++) + { + Con = &AGWConnections[i]; + Stream = Con->BPQStream; + + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + AGWConnected(Con, Stream); + else + AGWDisconnected(Con, Stream); + } + + + if (Con->SocketIndex) // Active Session + { + AGWDoReceivedData(Stream); + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + AGWConnected(Con, Stream); + else + AGWDisconnected(Con, Stream); + } + } + } + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + FD_SET(sock, &readfd); + FD_SET(sock, &exceptfd); + + Active++; + if (sock > maxsock) + maxsock = sock; + } + } + + if (Active) + { + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("data select yy"); + Debugprintf("Select Error %d", WSAGetLastError()); + } + else + { + if (retval) + { + // see who has data + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + if (FD_ISSET(sock, &exceptfd)) + AGWDataSocket_Disconnect(sockptr); + + if (FD_ISSET(sock, &readfd)) + AGWDataSocket_Read(sockptr, sock); + + } + } + } + } + } + + AGWDoMonitorData(); +} + + +SOCKADDR_IN local_sin; /* Local socket - internet style */ + +PSOCKADDR_IN psin; + + +BOOL AGWAPIInit() +{ + struct PORTCONTROL * PORT = PORTTABLE; + int i = 1; + int v = 0; + char * ptr; + BOOL opt=TRUE; + + if (AGWPort == 0) + return FALSE; + +// Create listening socket + + agwsock = socket( AF_INET, SOCK_STREAM, 0); + + if (agwsock == INVALID_SOCKET) + { + sprintf(szBuff, "socket() failed error %d", WSAGetLastError()); + WritetoConsole(szBuff); + return FALSE; + } + + setsockopt (agwsock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&opt,4); + + psin=&local_sin; + + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + psin->sin_port = htons(AGWPort); /* Convert to network ordering */ + + if (bind(agwsock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed Port %d Error %d", AGWPort, WSAGetLastError()); + WritetoConsole(szBuff); + closesocket(agwsock); + + return FALSE; + } + + if (listen(agwsock, 5) < 0) + { + sprintf(szBuff, "listen(sock) failed Error %d", WSAGetLastError()); + WritetoConsole(szBuff); + + return FALSE; + } + + SetUpHostSessions(); + + // Set up port List + + ptr = &AGWPorts[0]; + + ptr += sprintf(ptr, "%d", NUMBEROFPORTS); + + *(ptr)=';'; + *(++ptr)=0; + + while (PORT) + { + if (PORT->Hide == 0) + { + VisiblePortToRealPort[v++] = i - 1; + memcpy(ptr,"Port",4); + ptr += sprintf(ptr, "%d", i); + memcpy(ptr, " with ", 6); + ptr+=6; + memcpy(ptr, PORT->PORTDESCRIPTION, 29); // ";" + ptr+=29; + + while (*(--ptr) == ' ') {} + + ptr++; + + *(ptr++)=';'; + } + i++; + PORT=PORT->PORTPOINTER; + } + + *(ptr)=0; + + AGWMONVECPTR->HOSTAPPLFLAGS = 0x80; // Requext Monitoring + + return TRUE; +} + +int SetUpHostSessions() +{ + int Stream, i; + + if (AGWMask == 0) return 0; + + for (i = 1; i <= AGWSessions; i++) + { + strcpy(pgm, "AGW"); + + Stream = FindFreeStream(); + + if (Stream == 255) break; + + SetAppl(Stream, 2, AGWMask); + + strcpy(pgm, "bpq32.exe"); + + AGWConnections[CurrentConnections].CallKey[0] = 0; + AGWConnections[CurrentConnections].BPQStream = Stream; + AGWConnections[CurrentConnections].SocketIndex = 0; + AGWConnections[CurrentConnections].Connecting = FALSE; + AGWConnections[CurrentConnections].Listening = TRUE; + AGWConnections[CurrentConnections].ApplMask = AGWMask; + + CurrentConnections++; + } + + return 0; +} + +extern struct DATAMESSAGE * REPLYBUFFER; +extern BOOL AGWActive; + +VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY AGW Session Status + + int i, con; + struct BPQConnectionInfo * Con; + byte key[21]; + + struct AGWSocketConnectionInfo * sockptr; + char IPAddr[20]; + + if (AGWActive == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "\rAGW Interface is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\rSockets\r"); + for (i = 1; i <= CurrentSockets; i++) + { + sockptr=&Sockets[i]; + + if (sockptr->SocketActive) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + + sprintf(IPAddr, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%2d %-16s %5d %-10s %-10s\r", i, IPAddr, htons(sockptr->sin.sin_port), &sockptr->CallSign1, &sockptr->CallSign2); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%2d Idle\r", 1); + } + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\rPort Calls Stream Socket Connecting Listening Mask\r"); + + for (con = 0; con < CurrentConnections; con++) + { + Con = &AGWConnections[con]; + + memcpy(key,Con->CallKey,21); + + if (key[0] == 0) key[0] = 32; + + key[10]=0; + key[20]=0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%2c ", key[0]); + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-10s ",&key[1],&key[11]); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%2d %2d %s %s %X\r", + Con->BPQStream, + (Con->SocketIndex == 0) ? 0 : AGWConnections[con].SocketIndex->Number, + (Con->Connecting == 0) ? "FALSE" : "TRUE ", + (Con->Listening == 0) ? "FALSE" : "TRUE ", + Con->ApplMask); + + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + + +int DisplaySessions() +{ +/* char * ptr; + int i, con; + + byte key[21]; // As String, char As String + + strcpy (SessionList," Port Calls Stream Socket Connecting Listening Mask"); + + SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_RESETCONTENT,0,0); + SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_ADDSTRING,0,(LPARAM) SessionList); + + for (con = 0; con < CurrentConnections; con++) + { + memcpy(key,AGWConnections[con].CallKey,21); + + if (key[0] == 0) key[0] = 32; + + + key[10]=0; + key[20]=0; + + ptr=&SessionList[0]; + + i=sprintf(ptr,"%2d %2c ",con,key[0]); + + ptr+=i; + + i=sprintf(ptr,"%-10s%-10s ",&key[1],&key[11]); + + ptr+=i; + + i=sprintf(ptr,"%2d %2d %s %s %x", + AGWConnections[con].BPQStream, + (AGWConnections[con].SocketIndex == 0) ? 0 : AGWConnections[con].SocketIndex->Number, + (AGWConnections[con].Connecting == 0) ? "FALSE" : "TRUE ", + (AGWConnections[con].Listening == 0) ? "FALSE" : "TRUE ", + AGWConnections[con].ApplMask); + + SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_ADDSTRING,0,(LPARAM) SessionList); + } +*/ + return (0); + +} + +int AGWConnected(struct BPQConnectionInfo * Con, int Stream) +{ + byte ConnectingCall[10]; + byte * ApplCallPtr; + byte * keyptr; + byte ApplCall[10]=""; + int i; + + int ApplNum; + struct AGWSocketConnectionInfo * sockptr; + + GetCallsign(Stream, ConnectingCall); + + for (i=9;i>0;i--) + if (ConnectingCall[i]==32) + ConnectingCall[i]=0; + + ApplNum = GetApplNum(Stream); + + if (ApplNum == 0) + { + return 0; // Cant be an incomming connect + } + ApplCallPtr = GetApplCall(ApplNum); + + if (ApplCallPtr != 0) memcpy(ApplCall,ApplCallPtr,10); + + // Convert trailing spaces to nulls + + for (i=9;i>0;i--) + if (ApplCall[i]==32) + ApplCall[i]=0; + +// See if incomming connection + + if (Con->Listening) + { + // Allocate Session and send "c" Message + // + // Find an AGW session + + for (sockptr=&Sockets[1]; sockptr <= &Sockets[CurrentSockets]; sockptr++) + { + if (sockptr->SocketActive && + (memcmp(sockptr->CallSign1, ApplCall, 10) == 0) || (memcmp(sockptr->CallSign2, ApplCall, 10) == 0)) + { + // Create Key + + keyptr=(byte *)&Con->CallKey; + + *(keyptr++)='1'; + memcpy(keyptr, ApplCall, 10); + keyptr+=10; + memcpy(keyptr,ConnectingCall, 10); + + // Make sure key is not already in use + + for (i = 0; i < CurrentConnections; i++) + { + if (Con->BPQStream == Stream) + continue; // Dont Check ourself! + + if (AGWConnections[i].SocketIndex == sockptr && + memcmp(&Con->CallKey, &AGWConnections[i].CallKey,21) == 0) + { + SendMsg(Stream, "AGWtoBPQ - Callsign is already connected\r", 43); + Sleep (500); + Disconnect(Stream); + Con->CallKey[0]=0; + + return 0; + } + } + + Con->Listening = FALSE; + Con->SocketIndex = sockptr; + + DisplaySessions(); + + SendConMsgtoAppl(TRUE, Con, ConnectingCall); + + return 0; + } + } + + SendMsg(Stream, "No AGWPE Host Sessions available\r", 33); + Sleep (500); + Disconnect(Stream); // disconnect + return (0); + } + + // Not listening ?? + + OutputDebugString("Inbound Connection on Outgoing Stream"); + + SendMsg(Stream, "AGWtoBPQ - Inbound Connection on Outgoing Stream\r", 49); + Sleep (500); + Disconnect(Stream); // disconnect + return (0); +} + +int AGWDisconnected(struct BPQConnectionInfo * Con, int Stream) +{ + struct AGWSocketConnectionInfo * sockptr; + char key[21]; + + memcpy(key,Con->CallKey,21); + + sockptr = Con->SocketIndex; + + if (sockptr != 0) + { + AGWTXHeader.Port = key[0] - 49; + + memcpy(&AGWTXHeader.callfrom,&key[11],10); + memcpy(&AGWTXHeader.callto,&key[1],10); + + // Send a "d" Message + + // DisMsg = "*** DISCONNECTED From Station " + + SendDisMsgtoAppl("*** DISCONNECTED From Station ", sockptr); + + } + + if (Con->ApplMask != 0) + { + Con->Listening = TRUE; + Con->SocketIndex = 0; + memset(&Con->CallKey ,0 ,21); + + DisplaySessions(); + } + else + { + DeleteConnection(Con); + } + + return 0; + +} +int AGWDoReceivedData(int Stream) +{ + byte Buffer[400]; + int len,count; + +//Dim n As Integer, i As Integer, j As Integer, portcount As Integer +//Dim start As Integer + + do + { + GetMsg(Stream, Buffer,&len, &count); + + if (len > 0) + SendDataToAppl(Stream, Buffer,len); + + } + while (count > 0); + + return 0; +} + +int AGWDoMonitorData() +{ + byte Buffer[500]; + int RawLen, Length; + byte Port; + struct AGWSocketConnectionInfo * sockptr; + byte AGWBuffer[1000]; + int n; + int Stamp, Frametype; + BOOL RXFlag, NeedAGW; + + // Look for Monitor Data + + while (AGWMONVECPTR->HOSTTRACEQ) + { + MESSAGE * monbuff; + + GetSemaphore(&Semaphore, 99); + + monbuff = Q_REM((void *)&AGWMONVECPTR->HOSTTRACEQ); + + RawLen = monbuff->LENGTH; + + if (RawLen < 7 || RawLen > 350) + { + ReleaseBuffer(monbuff); + FreeSemaphore(&Semaphore); + return 0; + } + + Stamp = (UINT)monbuff->Timestamp; + + memcpy(Buffer, monbuff, RawLen); + + ReleaseBuffer(monbuff); + + FreeSemaphore(&Semaphore); + +//' 4 byte chain +//' 1 byte port - top bit = transmit +//' 2 byte length (LO-HI) + + Port = Buffer[4]; + + if (Port > 127) + { + RXFlag = FALSE; + Port = Port - 128; + } + else + { + RXFlag = TRUE; + } + + NeedAGW = FALSE; + + for (n = 1; n<= CurrentSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive && sockptr->MonFlag) NeedAGW = TRUE; + } + + if (NeedAGW) + { + if (RXFlag || LoopMonFlag) // only send txed frames if requested + { + Length = InternalAGWDecodeFrame(Buffer, AGWBuffer,Stamp, &Frametype); + + // + // Decode frame and send to applications which have requested monitoring + // + if (Length > 0) + { + AGWTXHeader.Port = Port - 1; // AGW Ports start from 0 + + /* + * Patch by D. van der Locht 09-10-2020 + * Added support for sending 'T' frames to AGW clients if LoopMonFlag is set. + */ + + if (RXFlag) + { + if (Frametype == 3) + { + AGWTXHeader.DataKind = 'U'; + } + else + { + if (Frametype && 1 == 0) + { + AGWTXHeader.DataKind = 'I'; + } + else + { + AGWTXHeader.DataKind = 'S'; + } + } + } + else + { + AGWTXHeader.DataKind = 'T'; + } + + AGWTXHeader.DataLength = Length; + + memset(AGWTXHeader.callfrom, 0,10); + ConvFromAX25(monbuff->ORIGIN, AGWTXHeader.callfrom); + + for (n = 1; n<= CurrentSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive && sockptr->MonFlag) + SendRawPacket(sockptr, AGWBuffer, Length); + } + } + } + } + + RawLen = RawLen - 6; + + if (RXFlag || Loopflag) // Send transmitted frames if requested + { + + // + // Send raw data to any sockets that have requested Raw frames + // + + Buffer[6]=0; + + AGWTXHeader.Port = Port - 1; // AGW Ports start from 0 + AGWTXHeader.DataKind = 'K'; + + AGWTXHeader.DataLength = RawLen; + + for (n = 1; n<= CurrentSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive && sockptr->RawFlag) + SendRawPacket(sockptr, &Buffer[6], RawLen); + + } + } + } + + return 0; + +} + +int DeleteConnection(struct BPQConnectionInfo * Con) +{ + int i; + int con; + + // + // remove specified session + // + + SetAppl(Con->BPQStream, 0, 0); + + Disconnect(Con->BPQStream); + + DeallocateStream(Con->BPQStream); + +// move all down one + + con = (int)(Con - &AGWConnections[0]); + + for (i = con; i <= CurrentConnections - 1; i++) + { + memcpy(&AGWConnections[i],&AGWConnections[i + 1],sizeof ConInfoRec); + } + + CurrentConnections--; + + return 0; +} + +int SendConMsgtoAppl(BOOL Incomming, struct BPQConnectionInfo * Con, char * CallSign) +{ + char key[21]; + char ConMsg[80]="*** CONNECTED "; + struct AGWSocketConnectionInfo * sockptr; + + + memcpy(key,&Con->CallKey,21); + + sockptr = Con->SocketIndex; + + AGWTXHeader.Port = key[0] - 49; + + memcpy(AGWTXHeader.callfrom, &key[11],10); + + memcpy(AGWTXHeader.callto, &key[1],10); + +/* ' + ' Send a "C" Message + ' +'01 00 00 00 43 00 00 00 47 4D 38 42 50 51 2D 34  C GM8BPQ-4 +'00 EA 47 4D 38 42 50 51 2D 34 00 FF 25 00 00 00 êGM8BPQ-4 ÿ% +'00 00 00 00 2A 2A 2A 20 43 4F 4E 4E 45 43 54 45 *** CONNECTE +'44 20 57 69 74 68 20 53 74 61 74 69 6F 6E 20 47 D With Station G +'4D 38 42 50 51 2D 34 0D 00 M8BPQ-4 +*/ + + AGWTXHeader.DataKind = 'C'; + + AGWTXHeader.PID = 0; + + if (Incomming) + strcat(ConMsg,"To"); + else + strcat(ConMsg,"With"); + + strcat(ConMsg," Station "); + strcat(ConMsg,CallSign); + + AGWTXHeader.DataLength = (int)strlen(ConMsg)+1; + + SendtoSocket(sockptr->socket, ConMsg); + + return 0; + +} + + + + +int SendDisMsgtoAppl(char * Msg, struct AGWSocketConnectionInfo * sockptr) +{ + byte DisMsg[100]; + + strcpy(DisMsg,Msg); + strcat(DisMsg,(const char *)&AGWTXHeader.callfrom); + + AGWTXHeader.Port = sockptr->AGWRXHeader.Port; + AGWTXHeader.DataKind = 'd'; + + strcat(DisMsg,"\r"); + + AGWTXHeader.DataLength = (int)strlen(DisMsg)+1; + + SendtoSocket(sockptr->socket, DisMsg); + + return 0; + +} + + + +int AGWSocket_Accept(SOCKET SocketId) +{ + int n,addrlen; + struct AGWSocketConnectionInfo * sockptr; + SOCKET sock; + +// Find a free Socket + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive == FALSE) + { + addrlen=sizeof(struct sockaddr); + + memset(sockptr, 0, sizeof(struct AGWSocketConnectionInfo)); + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "AGW accept() failed Error %d\r", WSAGetLastError()); + WritetoConsole(szBuff); + return FALSE; + } + + sockptr->socket = sock; + sockptr->SocketActive = TRUE; + sockptr->GotHeader = FALSE; + sockptr->MsgDataLength = 0; + sockptr->Number = n; + + if (CurrentSockets < n) CurrentSockets=n; //Record max used to save searching all entries + + ShowApps(); + + return 0; + } + } + + // Should accept, then immediately close + + return 0; +} + +int SendtoSocket(SOCKET sock, char * Msg) +{ + int len; + int n; + char * ptr; + + len = AGWTXHeader.DataLength; + + // Make sure calls are null terminated + + n = 10; + ptr = &AGWTXHeader.callfrom[9]; + while (n-- && *(ptr) == ' ') + { + *(ptr) = 0; + ptr--; + } + + n = 10; + ptr = &AGWTXHeader.callto[9]; + while (n-- && *(ptr) == ' ') + { + *(ptr) = 0; + n--; + } + + send(sock,(char *)&AGWTXHeader, 36,0); + if (len > 0) send(sock, Msg, len,0); + + return 0; +} + +int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock) +{ + int i; + int DataLength; + + ioctlsocket(sock,FIONREAD,&DataLength); + + if (DataLength == SOCKET_ERROR || DataLength == 0) + { + // Failed or closed - clear connection + + AGWDataSocket_Disconnect(sockptr); + return 0; + } + + + if (sockptr->GotHeader) + { + // Received a header, without sufficient data bytes + + if (DataLength < sockptr->MsgDataLength) + { + // Fiddle - seem to be problems somtimes with un-Neagled hosts + + Sleep(500); + + ioctlsocket(sock,FIONREAD,&DataLength); + } + + if (DataLength >= sockptr->MsgDataLength) + { + // Read Data and Process Command + + i=recv(sock, AGWMessage, sockptr->MsgDataLength, 0); + + ProcessAGWCommand (sockptr); + + sockptr->GotHeader = FALSE; + } + + // Not Enough Data - wait + + } + else // Not got header + { + if (DataLength > 35)// A header + { + i=recv(sock,(char *)&sockptr->AGWRXHeader, 36, 0); + + if (i == SOCKET_ERROR) + { + i=WSAGetLastError(); + + AGWDataSocket_Disconnect(sockptr); + } + + + sockptr->MsgDataLength = sockptr->AGWRXHeader.DataLength; + + if (sockptr->MsgDataLength > 500) + OutputDebugString("Corrupt AGW message"); + + + if (sockptr->MsgDataLength == 0) + { + ProcessAGWCommand (sockptr); + } + else + { + sockptr->GotHeader = TRUE; // Wait for data + } + + } + + // not got 36 bytes + + } + + return 0; +} + + +int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) +{ + int AGWVersion[2]={2003,999}; + char AGWRegReply[1]; + struct BPQConnectionInfo * Connection; + int Stream; + char AXCall[10]; + char RegCall[10]; + unsigned char TXMessage[500]; + int Digis,MsgStart,j; + byte * TXMessageptr; + char key[21]; + char ToCall[10]; + char ConnectMsg[128]; + int con,conport; + int AGWYReply = 0; + int state, change; + + // if we have hidden some ports then the port in the AGW packet will be an index into the visible ports, + // not the real port number + + switch (sockptr->AGWRXHeader.DataKind) + { + case 'C': + case 'v': + + // Connect or Connect with Digis + + // Create Session Key from port and callsign pair + + AGWGetSessionKey(key, sockptr); + + memcpy(ToCall, &key[11],10); + + strcpy(pgm, "AGW"); + + Stream = FindFreeStream(); + + strcpy(pgm, "bpq32.exe"); + + if (Stream == 255) return 0; + + Connection=&AGWConnections[CurrentConnections]; + + memcpy(&Connection->CallKey,key,21); + Connection->BPQStream = Stream; + Connection->SocketIndex = sockptr; + Connection->Connecting = TRUE; + + Connect(Stream); // Connect + SessionState(Stream, &state, &change); + + ConvToAX25(sockptr->CallSign1, AXCall); + ChangeSessionCallsign(Stream, AXCall); // Prevent triggering incoming connect code + + DisplaySessions(); + + if (memcmp(ToCall,"SWITCH",6) == 0) + { + // Just connect to command level on switch + + SendConMsgtoAppl(FALSE, Connection, ToCall); + Connection->Connecting = FALSE; + } + else + { + + // Need to convert port index (used by AGW) to port number + + conport=GetPortNumber(VisiblePortToRealPort[key[0]-48]); + + sprintf(ConnectMsg,"C %d %s",conport,ToCall); + + // if 'v' command add digis + + if (sockptr->AGWRXHeader.DataLength) + { + // Have digis + + char * Digis = AGWMessage; + int nDigis = Digis[0]; + + Digis ++; + + while(nDigis--) + { + sprintf(ConnectMsg, "%s, %s", ConnectMsg, Digis); + Digis += 10; + } + } + + strcat(ConnectMsg, "\r"); + + // Send C command to Node + + SendMsg(Stream, ConnectMsg, (int)strlen(ConnectMsg)); + } + + CurrentConnections++; + + DisplaySessions(); + + return 0; + + case 'D': + + // Send Data + // + // Create Session Key from port and callsign pair + + AGWGetSessionKey(key, sockptr); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + SendMsg(AGWConnections[con].BPQStream, AGWMessage, sockptr->MsgDataLength); + return 0; + } + } + + return 0; + + case 'd': + + // Disconnect + + memcpy(AGWTXHeader.callto,sockptr->AGWRXHeader.callfrom,10); + memcpy(AGWTXHeader.callfrom,sockptr->AGWRXHeader.callto,10); + + SendDisMsgtoAppl("*** DISCONNECTED RETRYOUT With ", sockptr); + + AGWGetSessionKey(key, sockptr); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + Disconnect(AGWConnections[con].BPQStream); + return 0; + } + } + + // There is confusion about the correct ordring of calls in the "d" packet. AGW appears to accept either, + // so I will too. + + memset(&key[1],0,20); + strcpy(&key[1],sockptr->AGWRXHeader.callto); + strcpy(&key[11],sockptr->AGWRXHeader.callfrom); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + Disconnect(AGWConnections[con].BPQStream); + return 0; + } + } + + return 0; + + + case 'R': + + // Version + + memset(&AGWTXHeader,0,36); + + AGWTXHeader.DataKind = 'R'; + + AGWTXHeader.DataLength = 8; // Length + + SendtoSocket(sockptr->socket, (char *)&AGWVersion[0]); + + return 0; + + + case 'G': + + // Port info. String is in AGWPorts + + + memset(&AGWTXHeader,0,36); + + AGWTXHeader.DataKind = 'G'; + + AGWTXHeader.DataLength =(int)strlen(AGWPorts)+1; // Length + + SendtoSocket(sockptr->socket, AGWPorts); + + return 0; + + + case 'k': + + // Toggle Raw receive + + sockptr->RawFlag = !sockptr->RawFlag; + + return 0; + + case 'K': + + // Send Raw Frame + + SendRaw(sockptr->AGWRXHeader.Port+1,&AGWMessage[1], sockptr->MsgDataLength - 1); + + return 0; + + case 'm': + + // Toggle Monitor receive + + sockptr->MonFlag = !sockptr->MonFlag; + return 0; + + + case 'M': + case 'V': // Send UNProto Frame "V" includes Via string + + + ConvToAX25(sockptr->AGWRXHeader.callto,TXMessage); + ConvToAX25(sockptr->AGWRXHeader.callfrom,&TXMessage[7]); + + Digis=0; + MsgStart = 0; + + if (sockptr->AGWRXHeader.DataKind == 'V') // Unproto with VIA string + { + Digis = AGWMessage[0]; // Number of digis + + for (j = 1; j<= Digis; j++) + { + ConvToAX25(&AGWMessage[(j - 1) * 10 + 1],&TXMessage[7+(j*7)]); // No "last" bit + } + + // set end of call + + MsgStart = Digis * 10 + 1; // UI Data follows digis in message + } + + TXMessageptr=&TXMessage[13+(Digis*7)]; + + *(TXMessageptr++) |= 1; // set last bit + + *(TXMessageptr++) = 3; // UI + + if (sockptr->AGWRXHeader.PID == 0) + + *(TXMessageptr++) = 240; // PID + else + *(TXMessageptr++) = sockptr->AGWRXHeader.PID; + + memcpy(TXMessageptr,&AGWMessage[MsgStart], sockptr->MsgDataLength - MsgStart); + + TXMessageptr += (sockptr->MsgDataLength - MsgStart); + + SendRaw(sockptr->AGWRXHeader.Port + 1, TXMessage, (int)(TXMessageptr-&TXMessage[0])); + + return 0; + + case 'X': + + // Register Callsign + + memset(&AGWTXHeader,0,36); + + memset(RegCall,0,10); + memcpy(RegCall, sockptr->AGWRXHeader.callfrom, strlen(sockptr->AGWRXHeader.callfrom)); + + if (sockptr->CallSign1[0] == 0) + memcpy(sockptr->CallSign1, RegCall, 10); + else + memcpy(sockptr->CallSign2, RegCall, 10); + + AGWTXHeader.DataKind = 'X'; + + AGWTXHeader.DataLength = 1; // Length + + AGWRegReply[0] = 1; + + SendtoSocket(sockptr->socket, AGWRegReply); + + ShowApps(); + + + return 0; + + + case 'Y': + + // Session Status + + // Create Session Key from port and callsign pair + + AGWGetSessionKey(key, sockptr); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + memcpy(&AGWTXHeader,&sockptr->AGWRXHeader,36); + + AGWYReply = CountFramesQueuedOnStream(AGWConnections[con].BPQStream); + AGWTXHeader.DataLength = 4; // Length + SendtoSocket(sockptr->socket, (char *)&AGWYReply); + + return 0; + } + } + + return 0; + + + default: + + //If Debugging Then Print #10, "Unknown Message "; Chr$(Sockets(Index).AGWRXHeader(4)) + // Debug.Print "Unknown Message "; Chr$(Sockets(Index).AGWRXHeader(4)) + + return 0; + + } + + return 0; + +} + +int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr) +{ + +// Create Session Key from port and callsign pair + + + + key[0] = sockptr->AGWRXHeader.Port + '1'; + + memset(&key[1],0,20); + strcpy(&key[1],sockptr->AGWRXHeader.callfrom); + strcpy(&key[11],sockptr->AGWRXHeader.callto); + + return 0; + +} +int SendDataToAppl(int Stream, byte * Buffer, int Length) +{ + int con; + char * i; + char ConMsg[80]; + char DisMsg[80]; + char key[21]; + struct AGWSocketConnectionInfo * sockptr; + +//Dim i As Long, Length As Long, con As Long, key As String, hilen As Long, lolen As Long +//Dim Index As Integer, ConMsg As String, DisMsg As String +//Dim BytesSent As Long + + + //' Find Connection number and call pair + + for (con = 0; con < CurrentConnections; con++) + { + if (AGWConnections[con].BPQStream == Stream) + { + memcpy(key,&AGWConnections[con].CallKey,21); + + if (key[0] == 32) + { + //Debug.Print "Data on Unconnected Session" + + Disconnect(Stream); + return (0); + } + + sockptr = AGWConnections[con].SocketIndex; + + if (sockptr == 0) + { + // No connection, but have a key - wot's going on!! + // Probably best to clear out connection + + Disconnect(Stream); + + return (0); + } + + AGWTXHeader.Port = key[0] - 49; + + memcpy(AGWTXHeader.callfrom,&key[11],10); + memcpy(AGWTXHeader.callto,&key[1],10); + + if (AGWConnections[con].Connecting) + { + + // See if *** Connected message + + i = strstr(Buffer, "Connected to"); + + if (i != 0) + { + AGWConnections[con].Connecting = FALSE; + + DisplaySessions(); + + AGWTXHeader.DataKind = 'C'; + AGWTXHeader.PID = 0; + + strcpy(ConMsg,"*** CONNECTED With Station "); + strcat(ConMsg, AGWTXHeader.callfrom); + strcat(ConMsg,"\r"); + + AGWTXHeader.DataLength = (int)strlen(ConMsg)+1; + + SendtoSocket(sockptr->socket, ConMsg); + + return (0); + + } + + i = strstr(Buffer, "Failure with"); + + if (i != 0) + { + AGWConnections[con].Connecting = FALSE; + + strcpy(DisMsg,"*** DISCONNECTED RETRYOUT With "); + + SendDisMsgtoAppl(DisMsg, sockptr); + + DeleteConnection(&AGWConnections[con]); + + return 0; + + } + + i = strstr(Buffer, "Busy from"); + + if (i != 0) + { + AGWConnections[con].Connecting = FALSE; + + strcpy(DisMsg,"*** DISCONNECTED RETRYOUT With "); + + SendDisMsgtoAppl(DisMsg, sockptr); + + DeleteConnection(&AGWConnections[con]); + + return 0; + + } + } + + AGWTXHeader.DataKind = 'D'; + AGWTXHeader.PID = 0xF0; + AGWTXHeader.DataLength = Length; + + SendtoSocket(sockptr->socket, Buffer); + } + } + + return 0; + } + + +int AGWDataSocket_Disconnect(struct AGWSocketConnectionInfo * sockptr) +{ + int con; + + closesocket(sockptr->socket); + + for (con = 0; con < CurrentConnections; con++) + { + if (AGWConnections[con].SocketIndex == sockptr) + Disconnect(AGWConnections[con].BPQStream); + } + + sockptr->SocketActive = FALSE; + sockptr->RawFlag = FALSE; + sockptr->MonFlag = FALSE; + + ShowApps(); + + + return 0; +} + +int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length) +{ + SendtoSocket(sockptr->socket, txmsg); + + return 0; +} + +int ShowApps() +{ +/* + struct AGWSocketConnectionInfo * sockptr; + int i; + char Msg[80]; + char IPAddr[20]; + + if (ConnWnd == 0) return 0; // Not on display + + SendDlgItemMessage(ConnWnd,IDC_CONNECTIONS_LIST,LB_RESETCONTENT,0,0); + + for (i = 1; i <= CurrentSockets; i++) + { + sockptr=&Sockets[i]; + + if (sockptr->SocketActive) + { + sprintf(IPAddr,"%d.%d.%d.%d", + sockptr->sin.sin_addr.S_un.S_un_b.s_b1, + sockptr->sin.sin_addr.S_un.S_un_b.s_b2, + sockptr->sin.sin_addr.S_un.S_un_b.s_b3, + sockptr->sin.sin_addr.S_un.S_un_b.s_b4); + + sprintf(Msg,"%2d %-16s %5d %-10s",i,IPAddr,htons(sockptr->sin.sin_port),&sockptr->CallSign); + } + else + { + sprintf(Msg,"%2d Idle",i); + } + + SendDlgItemMessage(ConnWnd,IDC_CONNECTIONS_LIST,LB_ADDSTRING,0,(LPARAM) Msg); + } +*/ + return 0; +} + + +int LocalSessionState(int stream, int * state, int * change, BOOL ACK); + +int AGWAPITerminate() +{ + int con, State, Change, n; + struct BPQConnectionInfo * Connection; + struct AGWSocketConnectionInfo * sockptr; +// +// Release all streams +// + for (con = 0; con < CurrentConnections; con++) + { + Connection=&AGWConnections[con]; + + SetAppl(Connection->BPQStream, 0, 0); + + Disconnect(Connection->BPQStream); + + DeallocateStream(Connection->BPQStream); + + LocalSessionState(Connection->BPQStream, &State, &Change, TRUE); + + memset(Connection, 0, sizeof(struct AGWSocketConnectionInfo)); + + } + + CurrentConnections = 0; + + // Close Listening socket and any connections + + shutdown(agwsock, 2); + closesocket(agwsock); + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + shutdown(sock, 2); + closesocket(sock); + } + memset(sockptr, 0, sizeof(struct BPQConnectionInfo)); + } + + return 0; +} + diff --git a/AGWMon.asm b/AGWMon.asm new file mode 100644 index 0000000..8d51a3c --- /dev/null +++ b/AGWMon.asm @@ -0,0 +1,1646 @@ + PAGE 56,132 +; + +.386 +; +; SEGMENT definitions and order +; + + +;* 32 Bit code +_TEXT SEGMENT DWORD USE32 PUBLIC 'CODE' +_TEXT ENDS + + + +;* Contains 32 Bit data +_DATA SEGMENT DWORD PUBLIC 'DATA' +_DATA ENDS + + + ASSUME CS:FLAT, DS:FLAT, ES:FLAT, SS:FLAT + + +OFFSET32 EQU + +_DATA SEGMENT + + +CR EQU 0DH +LF EQU 0AH + + +D10 DW 10 +D60 DD 60 +D3600 DD 3600 +D86400 DD 86400 + +TIMESTAMP DD 0 + +NEWVALUE DW 0 +WORD10 DW 10 +WORD16 DW 16 + + + +; +; BASIC LINK LEVEL MESSAGE BUFFER LAYOUT +; +MESSAGE STRUC + +MSGCHAIN DD ? ; CHAIN WORD +MSGPORT DB ? ; PORT +MSGLENGTH DW ? ; LENGTH + +MSGDEST DB 7 DUP (?) ; DESTINATION +MSGORIGIN DB 7 DUP (?) ; ORIGIN +; +; MAY BE UP TO 56 BYTES OF DIGIS +; +MSGCONTROL DB ? ; CONTROL BYTE +MSGPID DB ? ; PROTOCOL IDENTIFIER +MSGDATA DB ? ; START OF LEVEL 2 MESSAGE +; +MESSAGE ENDS +; +; +; +; L4FLAGS DEFINITION +; +L4BUSY EQU 80H ; BNA - DONT SEND ANY MORE +L4NAK EQU 40H ; NEGATIVE RESPONSE FLAG +L4MORE EQU 20H ; MORE DATA FOLLOWS - FRAGMENTATION FLAG + +L4CREQ EQU 1 ; CONNECT REQUEST +L4CACK EQU 2 ; CONNECT ACK +L4DREQ EQU 3 ; DISCONNECT REQUEST +L4DACK EQU 4 ; DISCONNECT ACK +L4INFO EQU 5 ; INFORMATION +L4IACK EQU 6 ; INFORMATION ACK +; + + +NULL EQU 00H +CR EQU 0DH +LF EQU 0AH +NETROM_PID EQU 0CFH +NODES_SIG EQU 0FFH +; +PORT_MSG DB ' Port=',NULL +NODES_MSG DB ' (NetRom Routing)',CR,NULL +NETROM_MSG DB ' NET/ROM',CR,' ',NULL +TO_MSG DB ' To ',NULL +TTL_MSG DB ' ttl=',NULL +AT_MSG DB ' at ',NULL +VIA_MSG DB ' via ',NULL +QUALITY_MSG DB ' qlty=',NULL +LEN_MSG DB ' Len=',0 + +PID_MSG DB ' pid=',NULL + +MYCCT_MSG DB ' my' +CCT_MSG DB ' cct=',0 +TIM_MSG DB ' t/o=',0 + +WINDOW DB ' w=',0 +CONN_REQ_MSG DB ' ',NULL +CONN_ACK_MSG DB ' ',NULL +CONN_NAK_MSG DB ' - BUSY',NULL +DISC_REQ_MSG DB ' ',NULL +DISC_ACK_MSG DB ' ',NULL +INFO_MSG DB ' ',NULL +IP_MSG DB ' ',NULL + +SABM_MSG DB 'SABM ',NULL +DISC_MSG DB 'DISC ',NULL +UA_MSG DB 'UA ',NULL +DM_MSG DB 'DM ',NULL +RR_MSG DB 'RR ',NULL +RNR_MSG DB 'RNR ',NULL +UI_MSG DB 'UI',NULL +FRMR_MSG DB 'FRMR ',NULL +REJ_MSG DB 'REJ ',NULL +; +; IP AND TCP BITS +; +IPMSG STRUC +; +; FORMAT OF IP HEADER +; +; NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) +; + +VERLEN DB 0 ; 4 BITS VERSION, 4 BITS LENGTH +TOS DB 0 ; TYPE OF SERVICE +IPLENGTH DW 0 ; DATAGRAM LENGTH +IPID DW 0 ; IDENTIFICATION +FRAGWORD DW 0 ; 3 BITS FLAGS, 13 BITS OFFSET +IPTTL DB 0 +IPPROTOCOL DB 0 ; HIGHER LEVEL PROTOCOL +IPCHECKSUM DW 0 ; HEADER CHECKSUM +IPSOURCE DD 0 +IPDEST DD 0 +; +; FORMAT OF TCP HEADER WITHIN AN IP DATAGRAM +; +; NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) +; + +SOURCEPORT DW 0 +DESTPORT DW 0 + +SEQNUM DD 0 +ACKNUM DD 0 + +TCPCONTROL DB 0 ; 4 BITS DATA OFFSET 4 RESERVED +TCPFLAGS DB 0 ; (2 RESERVED) URG ACK PSH RST SYN FIN + +TCPWINDOW DW 0 +CHECKSUM DW 0 +URGPTR DW 0 +; +; OPTIONS AND/OR DATA MAY FOLLOW +; +TCPOPTIONS DB 4 DUP (0) + +IPMSG ENDS +; +; TCPFLAGS BITS +; +FIN EQU 1B +SYN EQU 10B +RST EQU 100B +PSH EQU 1000B +ACK EQU 10000B +URG EQU 100000B + + +TCP DB 'TCP: ',0 +ICMP DB 'ICMP: ',0 +LEN DB ' LEN: ',0 + +SEQTEXT DB ' SEQ: ',0 +ACKTEXT DB 'ACK: ',0 +WINDTEXT DB 'WIN: ',0 +SYNTEXT DB 'SYN ',0 +FINTEXT DB 'FIN ',0 +RSTTEXT DB 'RST ',0 +PSHTEXT DB 'PSH ',0 +ACKTEXT1 DB 'ACK ',0 +URGTEXT DB 'URG ',0 + +BADSUM DB ' CHECKSUM ERROR ',0 + +;-----------------------------------------------------------------------------; +; Parameter area for received frame ; +;-----------------------------------------------------------------------------; + +PORT_NO DB 0 ; Received port number 0 - 256 +VERSION_NO DB 0 ; Version 1 or 2 1,2 +POLL_FINAL DB 0 ; Poll or Final ? P,F +COMM_RESP DB 0 ; Command or Response C,R +FRAME_TYPE DB 0 ; Frame Type UI etc in Hex +PID DB 0 ; Protocol ID +FRAME_LENGTH DD 0 ; Length of frame 0 - 65... +NR DB 0 ; N(R) Flag +NS DB 0 ; N(S) Flag +INFO_FLAG DB 0 ; Information Packet ? 0 No, 1 Yes +OPCODE DB 0 ; L4 FRAME TYPE +FRMRFLAG DB 0 + +TRACEFLAG DB 1 +MALL DB 1 +HEADERLN DB 1 +MCOM DB 1 +MTX DB 1 +MMASK DW 0FFFFH +; +; HDLC COMMANDS (WITHOUT P/F) +; +UI EQU 3 +SABM EQU 2FH +DISC EQU 43H +DM EQU 0FH +UA EQU 63H +FRMR EQU 87H +RR EQU 1 +RNR EQU 5 +REJ EQU 9 +; +PFBIT EQU 10H ; POLL/FINAL BIT IN CONTROL BYTE + + +CRLF DB 0DH,0AH + +AX25CALL DB 7 DUP (0) ; WORK AREA FOR AX25 <> NORMAL CALL CONVERSION +NORMCALL DB 10 DUP (0) ; CALLSIGN IN NORMAL FORMAT +NORMLEN DD 0 ; LENGTH OF CALL IN NORMCALL +; +TENK DD 10000 + DD 1000 +WORD100 DD 100 +DWORD10 DD 10 +; +ACCUM DB 4 DUP (0) +CONVFLAG DB 0 +SUPPRESS DB 0 ; ZERO SUPPRESS FLAG + +SAVESI DD 0 +SAVEDI DD 0 + +_DATA ENDS + +; 2:Fm GM8BPQ-2 To NODES [18:49:56] +;FF BPQ (NetRom Routing) +; N5IN-9 N9 N5IN-3 152 +; GB7CB GALA N5IN-3 151 +; 1:Fm GM8BPQ To APU25N [18:51:48] +;=5828.55N/00612.62W- {UIV32} + +; 1:Fm GM8BPQ-1 To BPQ [09:09:51] + +_TEXT SEGMENT PUBLIC 'CODE' + + PUBLIC _AGWMONDECODE,_AGWMONOPTIONS + +_AGWMONOPTIONS: + + MOV MMASK,AX + MOV MTX,BL + MOV MCOM,CL + + RET + +_AGWMONDECODE: +; +; esi=message, edi=buffer, return length in ECX amd frame type in EAX +; + + MOV SAVESI,ESI + MOV SAVEDI,EDI + + MOV TIMESTAMP,EAX + + cmp TRACEFLAG,0 + jne TRACEOK + + +TRACERET: + + ret + +TRACEOK: + + MOVZX ECX,MSGLENGTH[ESI] +; +; GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED +; + PUSH ESI + MOV ECX,9 ; MAX DIGIS + 1 +CTRLLOOP: + TEST BYTE PTR MSGCONTROL-1[ESI],1 + JNZ CTRLFOUND + + ADD ESI,7 + LOOP CTRLLOOP +; +; INVALID FRAME +; + POP ESI + MOV ECX,0 + RET + +CTRLFOUND: + + MOV AL,MSGCONTROL[ESI] + POP ESI +; + TEST AL,1 ; I FRAME + JZ IFRAME + + AND AL,NOT PFBIT ; CLEAR P/F + CMP AL,3 ; UI + JE OKTOTRACE ; ALWAYS DO UI + + CMP AL,FRMR + JE OKTOTRACE ; ALWAYS DO FRMR + +; +; USEQ/CONTROL - TRACE IF MCOM ON +; + CMP MCOM,0 + JNE OKTOTRACE + + MOV ECX,0 + RET + +;-----------------------------------------------------------------------------; +; Check for MALL ; +;-----------------------------------------------------------------------------; + +IFRAME: + + cmp MALL,0 + jne OKTOTRACE + + MOV ECX,0 + ret + +OKTOTRACE: +; +;-----------------------------------------------------------------------------; +; Get the port number of the received frame ; +;-----------------------------------------------------------------------------; + + mov CL,MSGPORT[ESI] + mov PORT_NO,CL + + TEST CL,80H + JZ NOT_TX +; +; TRANSMITTED FRAME - SEE IF MTX ON +; + CMP MTX,1 + JE NOT_TX + + MOV ECX,0 + RET + +NOT_TX: + + AND CL,7FH ; MASK T/R BIT + + DEC CL + MOV AX,1 + SHL AX,CL ; SHIFT BIT UP + + TEST MMASK,AX + JNZ TRACEOK1 + + MOV ECX,0 + RET + +TRACEOK1: + + MOV FRMRFLAG,0 + + mov AH,MSGDEST+6[ESI] + mov AL,MSGORIGIN+6[ESI] + + mov COMM_RESP,0 ; Clear Command/Response Flag + +;-----------------------------------------------------------------------------; +; Is it a Poll/Final or Command/Response ; +;-----------------------------------------------------------------------------; + + test AH,80H + mov COMM_RESP,'C' + jnz NOT_RESPONSE + mov COMM_RESP,'R' + +NOT_RESPONSE: + +;-----------------------------------------------------------------------------; +; Is this version 1 or 2 of AX25 ? ; +;-----------------------------------------------------------------------------; + + xor AH,AL + test AH,80H + mov VERSION_NO,1 + je VERSION_1 + mov VERSION_NO,2 + +VERSION_1: + + + mov al,PORT_NO + and al,7fh + + + cmp AL,10 + JAE @F + + PUSH EAX + + MOV AL,' ' + CALL PUTCHAR + + POP EAX +@@: + call DISPLAY_BYTE_1 + + MOV AL,':' + + CALL PUTCHAR + + MOV AL,'F' + CALL PUTCHAR + + MOV AL,'m' + CALL PUTCHAR + + MOV AL,' ' + CALL PUTCHAR + +;-----------------------------------------------------------------------------; +; Display Origin Callsign ; +;-----------------------------------------------------------------------------; + + PUSH ESI + + lea ESI,MSGORIGIN[ESI] + call CONVFROMAX25 + + mov ESI,OFFSET NORMCALL + call DISPADDR + + POP ESI + + PUSH ESI + + mov EBX,OFFSET TO_MSG + call NORMSTR + +;-----------------------------------------------------------------------------; +; Display Destination Callsign ; +;-----------------------------------------------------------------------------; + + lea ESI,MSGDEST[ESI] + call CONVFROMAX25 + + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop ESI + + movzx EAX,MSGLENGTH[ESI] + mov FRAME_LENGTH,EAX + mov CX,8 ; Max number of digi-peaters + +;-----------------------------------------------------------------------------; +; Display any Digi-Peaters ; +;-----------------------------------------------------------------------------; + +NEXT_DIGI: + + test MSGORIGIN+6[ESI],1 + jnz NO_MORE_DIGIS + + add ESI,7 + sub FRAME_LENGTH,7 ; Reduce length + + push ESI + + push ECX + lea ESI,MSGORIGIN[ESI] + call CONVFROMAX25 ; Convert to call + + push EAX ; Last byte is in AH + + mov AL,',' + call PUTCHAR + + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EAX + + test AH,80H + jz NOT_REPEATED + + mov AL,'*' + call PUTCHAR + +NOT_REPEATED: + + pop ECX + pop ESI + loop NEXT_DIGI + +NO_MORE_DIGIS: + + MOV AL,' ' + CALL PUTCHAR + +;-----------------------------------------------------------------------------; +; If this is Version 2 get the Poll/Final Bit ; +;-----------------------------------------------------------------------------; + + mov POLL_FINAL,0 ; Clear Poll/Final Flag + + mov AL,MSGCONTROL[ESI] ; Get control byte + + cmp COMM_RESP,'C' + jne NOT_COMM + + test AL,PFBIT + je NOT_POLL + + mov POLL_FINAL,'P' + +NOT_POLL: +NOT_COMM: + + cmp COMM_RESP,'R' + jne NOT_RESP + + test AL,PFBIT + je NOT_FINAL + + mov POLL_FINAL,'F' + +NOT_FINAL: +NOT_RESP: + +;-----------------------------------------------------------------------------; +; Start displaying the frame information ; +;-----------------------------------------------------------------------------; + + and AL,NOT PFBIT ; Remove P/F bit + mov FRAME_TYPE,AL + + mov AL,'<' ; Print "<" + call PUTCHAR + + mov NR,0 ; Reset all the flags + mov NS,0 + mov INFO_FLAG,0 + + mov AL,FRAME_TYPE + + test AL,1 + jne NOT_I_FRAME + +;-----------------------------------------------------------------------------; +; Information frame ; +;-----------------------------------------------------------------------------; + + mov AL,'I' + call PUTCHAR + mov AL,' ' + call PUTCHAR + + mov INFO_FLAG,1 + mov NR,1 + mov NS,1 + jmp END_OF_TYPE + +NOT_I_FRAME: + +;-----------------------------------------------------------------------------; +; Un-numbered Information Frame ; +;-----------------------------------------------------------------------------; + + cmp AL,UI + jne NOT_UI_FRAME + + mov EBX,OFFSET UI_MSG + call NORMSTR + + mov INFO_FLAG,1 + jmp END_OF_TYPE + +NOT_UI_FRAME: + test AL,10B + jne NOT_R_FRAME + +;-----------------------------------------------------------------------------; +; Process supervisory frames ; +;-----------------------------------------------------------------------------; + + mov NR,1 ; All supervisory frames have N(R) + + and AL,0FH ; Mask the interesting bits + cmp AL,RR + jne NOT_RR_FRAME + + mov EBX,OFFSET RR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RR_FRAME: + cmp AL,RNR + jne NOT_RNR_FRAME + + mov EBX,OFFSET RNR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RNR_FRAME: + cmp AL,REJ + jne NOT_REJ_FRAME + + mov EBX,OFFSET REJ_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_REJ_FRAME: + mov NR,0 ; Don't display sequence number + mov AL,'?' ; Print "?" + call PUTCHAR + jmp END_OF_TYPE + +;-----------------------------------------------------------------------------; +; Process all other frame types ; +;-----------------------------------------------------------------------------; + +NOT_R_FRAME: + cmp AL,UA + jne NOT_UA_FRAME + + mov EBX,OFFSET UA_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_UA_FRAME: + cmp AL,DM + jne NOT_DM_FRAME + + mov EBX,OFFSET DM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DM_FRAME: + cmp AL,SABM + jne NOT_SABM_FRAME + + mov EBX,OFFSET SABM_MSG + call NORMSTR + + jmp SHORT END_OF_TYPE + +NOT_SABM_FRAME: + cmp AL,DISC + jne NOT_DISC_FRAME + + mov EBX,OFFSET DISC_MSG + call NORMSTR + + jmp SHORT END_OF_TYPE + +NOT_DISC_FRAME: + cmp AL,FRMR + jne NOT_FRMR_FRAME + + mov EBX,OFFSET FRMR_MSG + call NORMSTR + MOV FRMRFLAG,1 + jmp SHORT END_OF_TYPE + +NOT_FRMR_FRAME: + mov AL,'?' + call PUTCHAR + +END_OF_TYPE: + +;----------------------------------------------------------------------------; +; If Version 2 Then display P/F C/R Information ; +;----------------------------------------------------------------------------; + + IF 0 + + cmp VERSION_NO,2 + jne NOT_VERSION_2 + + mov AL,' ' + call PUTCHAR + + mov AL,COMM_RESP ; Print Command/Response Flag + call PUTCHAR + + ENDIF + + cmp POLL_FINAL,0 + je NO_POLL_FINAL + + + mov AL,POLL_FINAL ; Print Poll/Final Flag if Set + call PUTCHAR + + mov AL,' ' + call PUTCHAR + +NO_POLL_FINAL: +NOT_VERSION_2: + + +;----------------------------------------------------------------------------; +; Display sequence numbers if applicable ; +;----------------------------------------------------------------------------; + + cmp NR,1 + jne NOT_NR_DATA + + + mov AL,'R' + call PUTCHAR + + mov AL,FRAME_TYPE + rol AL,1 + rol AL,1 + rol AL,1 + + call DISPLAYSEQ + + mov AL,' ' + call PUTCHAR + +NOT_NR_DATA: + + cmp NS,1 + jne NOT_NS_DATA + + mov AL,'S' + call PUTCHAR + + mov AL,FRAME_TYPE + ror AL,1 + + call DISPLAYSEQ + +NOT_NS_DATA: + + CMP INFO_FLAG,1 + JNE NO_PID_LEN + + + mov EBX,OFFSET PID_MSG + call NORMSTR + + PUSH ESI + lea ESI,MSGPID[ESI] + lodsb + POP ESI + + CALL HEXOUT + + mov EBX,OFFSET LEN_MSG + call NORMSTR + + MOV EAX,FRAME_LENGTH + SUB EAX,23 + + CALL DISPLAY_BYTE_1 + + mov AL,' ' + call PUTCHAR + +NO_PID_LEN: + + mov AL,'>' + call PUTCHAR + + mov AL,'[' + call PUTCHAR + +; +; DISPLAY TIMESTAMP +; + + MOV EAX,TIMESTAMP + mov EDX,0 + DIV D86400 + + MOV EAX,EDX + MOV edx,0 + DIV D3600 + + CALL DISPLAY_BYTE_2 + + MOV AL,':' + CALL PUTCHAR + + MOV EAX,EDX + MOV EDX,0 + DIV D60 ; MINS IN AX, SECS IN DX + + PUSH DX + + CALL DISPLAY_BYTE_2 + + MOV AL,':' + CALL PUTCHAR + + POP AX ; SECS + CALL DISPLAY_BYTE_2 + + + mov AL,']' + call PUTCHAR + + + CMP FRMRFLAG,0 + JE NOTFRMR +; +; DISPLAY FRMR BYTES +; + lea ESI,MSGPID[ESI] + MOV ECX,3 ; TESTING +FRMRLOOP: + lodsb + CALL BYTE_TO_HEX + + LOOP FRMRLOOP + + JMP NO_INFO + +NOTFRMR: +;----------------------------------------------------------------------------; +; Find the PID if an information frame ; +;----------------------------------------------------------------------------; + + mov AL,0 + + cmp INFO_FLAG,1 + jne NO_PID + + lea ESI,MSGPID[ESI] + lodsb + +NO_PID: + mov PID,AL + +;----------------------------------------------------------------------------; +; Is this a NET/ROM message of any sort ? ; +;----------------------------------------------------------------------------; + + MOV ECX,FRAME_LENGTH + + cmp PID,NETROM_PID + je DISPLAY_NETROM + +;----------------------------------------------------------------------------; +; Display the rest of the frame (If Any) ; +;----------------------------------------------------------------------------; + +DISPLAY_INFO: + + cmp INFO_FLAG,1 ; Is it an information packet ? + jne NO_INFO + + + XOR AL,AL ; IN CASE EMPTY + + sub ECX,23 + JCXZ NO_INFO ; EMPTY I FRAME +; +; PUT TEXT ON A NEW LINE +; + PUSH ECX + MOV AL,0DH + PUSH ESI + CALL PUTCHAR + POP ESI + POP ECX + +SAMELN: + + cmp ECX,257 + jb LENGTH_OK + + mov ECX,256 + +LENGTH_OK: + + push ECX + lodsb + + cmp AL,0AH + JE MONOK + CMP AL,0DH + JE MONOK + + CMP AL,20H + JB SKIP_MON ; IGNORE OTHER CONTROLS + +MONOK: + call PUTCHAR + +SKIP_MON: + + pop ECX + loop LENGTH_OK + +NO_INFO: +; +; ADD CR UNLESS DATA ALREADY HAS ONE +; + CMP AL,CR + JE NOTANOTHER +ADD_CR: + mov AL,CR + call PUTCHAR + +NOTANOTHER: +; + MOV ECX,EDI + SUB ECX,SAVEDI + + MOVZX EAX,FRAME_TYPE + + RET + + +;----------------------------------------------------------------------------; +; Display NET/ROM data ; +;----------------------------------------------------------------------------; + +DISPLAY_NETROM: + + MOV AL,CR + CALL PUTCHAR + + lodsb + PUSH EAX + CALL HEXOUT + + mov AL,' ' + call PUTCHAR + + POP EAX + + cmp AL,NODES_SIG ; Check NODES message + + JNE DISPLAY_NETROM_DATA + + cmp FRAME_TYPE, 3 + JNE DISPLAY_NETROM_DATA ; Not UI, so INP3 + +;----------------------------------------------------------------------------; +; Display NODES broadcast ; +;----------------------------------------------------------------------------; + + push ECX + + mov ECX,6 + REP MOVSB ; ALIAS + + mov EBX,OFFSET NODES_MSG + call NORMSTR + + pop ECX + + sub ECX,30 ; Header, mnemonic and signature length + +NODES_LOOP: + + cmp ECX,0 + jbe NO_INFO + + push ECX + push ESI ; Global push for each node + + mov AL,' ' + call PUTCHAR + mov AL,' ' + call PUTCHAR + + push ESI + + add ESI,7 ; Display destination mnemonic + + cmp BYTE PTR [ESI],' ' + je NO_MNEMONIC + + mov ECX,6 ; Max length + +MNEMONIC_LOOP: + + lodsb ; Get character + + cmp AL,' ' ; Short mnemonic ? + je END_MNEMONIC + + call PUTCHAR + + loop MNEMONIC_LOOP + +END_MNEMONIC: + + mov AL,':' + call PUTCHAR + +NO_MNEMONIC: + + pop ESI + push ESI + + call CONVFROMAX25 ; Display dest callsign + mov ESI,OFFSET NORMCALL + call DISPADDR + + mov EBX,OFFSET VIA_MSG + call NORMSTR + + pop ESI + add ESI,13 ; Point to neighbour callsign + push ESI + + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + mov EBX,OFFSET QUALITY_MSG + call NORMSTR + + pop ESI + add ESI,7 ; Point to quality byte + + mov AL,[ESI] + call DISPLAY_BYTE_1 + + mov AL,CR + call PUTCHAR + + pop ESI + pop ECX + add ESI,21 ; Point to next destination + sub ECX,21 ; Remove length of each + + jmp NODES_LOOP + +;----------------------------------------------------------------------------; +; Display normal NET/ROM transmissions ; +;----------------------------------------------------------------------------; + +DISPLAY_NETROM_DATA: + + DEC ESI ; BACK TO DATA + + mov EBX,OFFSET NETROM_MSG + call NORMSTR + + PUSH ESI + + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + mov EBX,OFFSET TO_MSG + call NORMSTR + + pop ESI + add ESI,7 + + push ESI + + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR +; +; Display Time To Live number +; + mov EBX,OFFSET TTL_MSG + call NORMSTR + + pop ESI + add ESI,7 ; Point to TTL counter + + lodsb + call DISPLAY_BYTE_1 + +; +; DISPLAY CIRCUIT ID +; + MOV EBX,OFFSET CCT_MSG + CALL NORMSTR + + LODSB + CALL BYTE_TO_HEX + + LODSB + CALL BYTE_TO_HEX + + INC ESI + INC ESI ; TO OPCODE + +;-----------------------------------------------------------------------------; +; Determine type of Level 4 frame ; +;-----------------------------------------------------------------------------; + + mov AL,[ESI] + MOV OPCODE,AL ; SAVE + AND AL,0FH ; Strip off flags + + cmp AL,L4CREQ + jne NOT_L4CREQ + + mov EBX,OFFSET CONN_REQ_MSG + call NORMSTR + + MOV EBX,OFFSET WINDOW + CALL NORMSTR + + INC ESI + LODSB ; WINDOW SIZE + + CALL DISPLAY_BYTE_1 + + mov AL,' ' + call PUTCHAR + + PUSH ESI + + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + mov EBX,OFFSET AT_MSG + call NORMSTR + + pop ESI + add ESI,7 + PUSH ESI + + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + + POP ESI + CMP FRAME_LENGTH,58 + JE NOT_BPQ +; +; BPQ EXTENDED CON REQ - DISPLAY TIMEOUT +; + MOV EBX,OFFSET TIM_MSG + CALL NORMSTR + + MOV AX,7[ESI] ; TIMEOUT + + CALL DISPLAY_BYTE_1 +; +NOT_BPQ: + + JMP ADD_CR + +NOT_L4CREQ: + + cmp AL,L4CACK + jne NOT_L4CACK + + TEST OPCODE,L4BUSY + JZ L4CRQ00 +; +; BUSY RETURNED +; + MOV EBX,OFFSET CONN_NAK_MSG + CALL NORMSTR + + JMP END_NETROM + +L4CRQ00: + + MOV EBX,OFFSET CONN_ACK_MSG + CALL NORMSTR + + MOV EBX,OFFSET WINDOW + CALL NORMSTR + + MOV AL,1[ESI] ; WINDOW SIZE + + CALL DISPLAY_BYTE_1 + + MOV EBX,OFFSET MYCCT_MSG + CALL NORMSTR + + MOV AL,-2[ESI] + CALL BYTE_TO_HEX + + MOV AL,-1[ESI] + CALL BYTE_TO_HEX + + JMP ADD_CR + +NOT_L4CACK: + + cmp AL,L4DREQ + jne NOT_L4DREQ + + mov EBX,OFFSET DISC_REQ_MSG + call NORMSTR + + JMP ADD_CR + +NOT_L4DREQ: + + cmp AL,L4DACK + jne NOT_L4DACK + + mov EBX,OFFSET DISC_ACK_MSG + call NORMSTR + + jmp add_cr + +NOT_L4DACK: + + cmp AL,L4INFO + jne NOT_L4INFO + + mov EBX,OFFSET INFO_MSG + call NORMSTR + + mov AL,-2[ESI] ; Get send sequence number + call DISPLAY_BYTE_1 + + mov AL,' ' + call PUTCHAR + mov AL,'R' + call PUTCHAR + + mov AL,-1[ESI] ; Get receive sequence number + call DISPLAY_BYTE_1 + + mov AL,'>' + call PUTCHAR + + INC ESI ; TO DATA + MOV ECX,FRAME_LENGTH + sub ECX,20 + + CALL DOL4FLAGS + + jmp DISPLAY_INFO + + +NOT_L4INFO: + + cmp AL,L4IACK + jne NOT_L4IACK + + mov EBX,OFFSET INFO_ACK_MSG + call NORMSTR + + mov AL,-1[ESI] ; Get receive sequence number + call DISPLAY_BYTE_1 + + mov AL,'>' + call PUTCHAR + + CALL DOL4FLAGS + + JMP SHORT END_NETROM + +NOT_L4IACK: + + OR AL,AL + JNZ NOTIP +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER +; + PUSH ESI + + LEA ESI,IPSOURCE[ESI] + CALL PRINT4 ; PRINT IF ADDR IN 'DOTTED DECIMAL' FORMAT + + POP ESI + + MOV AL,'>' + CALL PUTCHAR + + PUSH ESI + + LEA ESI,IPDEST[ESI] + CALL PRINT4 ; PRINT IF ADDR IN 'DOTTED DECIMAL' FORMAT + + MOV EBX,OFFSET LEN + CALL NORMSTR + + POP ESI + + MOV AL,BYTE PTR IPLENGTH[ESI] + CALL BYTE_TO_HEX + + MOV AL,BYTE PTR IPLENGTH+1[ESI] + CALL BYTE_TO_HEX + + MOV AL,20H + CALL PUTCHAR + + MOV AL,IPPROTOCOL[ESI] + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + +; mov AL,CR +; call PUTCHAR +; +; MOV ECX,39 ; TESTING +;IPLOOP: +; lodsb +; CALL BYTE_TO_HEX +; +; LOOP IPLOOP + + JMP ADD_CR + +NOTIP: + + mov EBX,OFFSET DUFF_NET_MSG + call NORMSTR + + +END_NETROM: + + jmp add_cr + +DOL4FLAGS: +; +; DISPLAY BUSY/NAK/MORE FLAGS +; + TEST OPCODE,L4BUSY + JZ L4F010 + + MOV AL,'B' + CALL PUTCHAR +L4F010: + TEST OPCODE,L4NAK + JZ L4F020 + + MOV AL,'N' + CALL PUTCHAR +L4F020: + TEST OPCODE,L4MORE + JZ L4F030 + + MOV AL,'M' + CALL PUTCHAR +L4F030: + RET + +;----------------------------------------------------------------------------; +; Display ASCIIZ strings ; +;----------------------------------------------------------------------------; + +NORMSTR: + MOV AL,[EBX] + INC EBX + cmp AL,NULL ; End of String ? + je NORMSTR_RET ; Yes + call PUTCHAR + jmp SHORT NORMSTR + +NORMSTR_RET: + ret + +;-----------------------------------------------------------------------------; +; Display sequence numbers ; +;-----------------------------------------------------------------------------; + +DISPLAYSEQ: + and AL,7 + add AL,30H + call PUTCHAR + ret + +;-----------------------------------------------------------------------------; +; Display Callsign pointed to by SI ; +;-----------------------------------------------------------------------------; + +DISPADDR: + + jcxz DISPADDR_RET + + LODS NORMCALL + call PUTCHAR + + loop DISPADDR + +DISPADDR_RET: + ret + + +PRINT4: +; +; DISPLAY IP ADDR IN DOTTED DECIMAL FORMAT +; + + LODSB + CALL DISPLAY_BYTE_1 + MOV AL,'.' + CALL PUTCHAR + + LODSB + CALL DISPLAY_BYTE_1 + MOV AL,'.' + CALL PUTCHAR + + LODSB + CALL DISPLAY_BYTE_1 + MOV AL,'.' + CALL PUTCHAR + + LODSB + CALL DISPLAY_BYTE_1 + + RET + + + +;-----------------------------------------------------------------------------; +; Convert byte in AL to nnn, nn or n format ; +;-----------------------------------------------------------------------------; + +DISPLAY_BYTE_1: + + cmp AL,100 + jb TENS_1 + + mov AH,0 + +HUNDREDS_LOOP_1: + cmp AL,100 + jb HUNDREDS_LOOP_END_1 + + sub AL,100 + inc AH + jmp SHORT HUNDREDS_LOOP_1 + +HUNDREDS_LOOP_END_1: + push AX + mov AL,AH + add AL,30H + call PUTCHAR + pop AX + jmp SHORT TENS_PRINT_1 + +TENS_1: + cmp AL,10 + jb UNITS_1 + +TENS_PRINT_1: + mov AH,0 + +TENS_LOOP_1: + cmp AL,10 + jb TENS_LOOP_END_1 + + sub AL,10 + inc AH + jmp SHORT TENS_LOOP_1 + +TENS_LOOP_END_1: + push AX + mov AL,AH + add AL,30H + call PUTCHAR + pop AX + +UNITS_1: + add AL,30H + call PUTCHAR + + ret + +;-----------------------------------------------------------------------------; +; Convert byte in AL to nn format ; +;-----------------------------------------------------------------------------; + +DISPLAY_BYTE_2: + cmp AL,100 + jb TENS_2 + + sub AL,100 + jmp SHORT DISPLAY_BYTE_2 + +TENS_2: + mov AH,0 + +TENS_LOOP_2: + cmp AL,10 + jb TENS_LOOP_END_2 + + sub AL,10 + inc AH + jmp SHORT TENS_LOOP_2 + +TENS_LOOP_END_2: + push AX + mov AL,AH + add AL,30H + call PUTCHAR + pop AX + +UNITS_2: + add AL,30H + call PUTCHAR + + ret + +;-----------------------------------------------------------------------------; +; Convert byte in AL to Hex display ; +;-----------------------------------------------------------------------------; + +BYTE_TO_HEX: + push AX + shr AL,1 + shr AL,1 + shr AL,1 + shr AL,1 + call NIBBLE_TO_HEX + pop AX + call NIBBLE_TO_HEX + ret + +NIBBLE_TO_HEX: + and AL,0FH + cmp AL,10 + + jb LESS_THAN_10 + add AL,7 + +LESS_THAN_10: + add AL,30H + call PUTCHAR + ret + + + +CONVFROMAX25: +; +; CONVERT AX25 FORMAT CALL IN [SI] TO NORMAL FORMAT IN NORMCALL +; RETURNS LENGTH IN CX AND NZ IF LAST ADDRESS BIT IS SET +; + PUSH EDI ; SAVE BUFFER + + PUSH ESI ; SAVE + MOV EDI,OFFSET NORMCALL + MOV ECX,10 ; MAX ALPHANUMERICS + MOV AL,20H + REP STOSB ; CLEAR IN CASE SHORT CALL + MOV EDI,OFFSET NORMCALL + MOV CL,6 +CONVAX50: + LODSB + CMP AL,40H + JE CONVAX60 ; END IF CALL - DO SSID + + SHR AL,1 + STOSB + LOOP CONVAX50 +CONVAX60: + POP ESI + ADD ESI,6 ; TO SSID + LODSB + MOV AH,AL ; SAVE FOR LAST BIT TEST + SHR AL,1 + AND AL,0FH + JZ CONVAX90 ; NO SSID - FINISHED +; + MOV BYTE PTR [EDI],'-' + INC EDI + CMP AL,10 + JB CONVAX70 + SUB AL,10 + MOV BYTE PTR [EDI],'1' + INC EDI +CONVAX70: + ADD AL,30H ; CONVERT TO DIGIT + STOSB +CONVAX90: + MOV ECX,EDI + SUB ECX,OFFSET NORMCALL + MOV NORMLEN,ECX ; SIGNIFICANT LENGTH + + TEST AH,1 ; LAST BIT SET? + + POP EDI + RET + +HEXOUT: + + PUSH AX + PUSH AX + sar al,1 + sar al,1 + sar al,1 + sar al,1 + call hexout1 + pop ax + call hexout1 + POP AX + ret + +hexout1: + and al,0fh + cmp al,10 + jl hexout5 + add al,7 +hexout5: + add al,30h + STOSB +; + ret + +PUTCHAR: + STOSB + RET + +_TEXT ENDS +; + END diff --git a/AGWMoncode.c b/AGWMoncode.c new file mode 100644 index 0000000..5fa9597 --- /dev/null +++ b/AGWMoncode.c @@ -0,0 +1,652 @@ +/* +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 +*/ + + + +// Monitor Code - from moncode.asm + +// Modified for AGW form monitor + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "CHeaders.h" +#include "tncinfo.h" + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +char * strlop(char * buf, char delim); +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, int msglen); + +static UCHAR * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); +static UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +static UCHAR * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); + + +int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, int Stamp, int * FrameType) +{ + UCHAR * ptr; + int n; + MESSAGE * ADJBUFFER; + ptrdiff_t Work; + UCHAR CTL; + BOOL PF = 0; + char CRCHAR[3] = " "; + char PFCHAR[3] = " "; + int MSGFLAG = 0; //CR and V1 flags + char * Output = buffer; + int HH, MM, SS; + char From[10], To[10]; + BOOL Info = 0; + BOOL FRMRFLAG = 0; + BOOL XIDFLAG = 0; + BOOL TESTFLAG = 0; + size_t MsgLen = msg->LENGTH; + + // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED + + n = 8; // MAX DIGIS + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + n--; + + if (n == 0) + { + return 0; // Corrupt - no end of address bit + } + } + + // Reached End of digis + + Work = ptr - &msg->ORIGIN[6]; // Work is length of digis + + MsgLen -= Work; + + ADJBUFFER = (MESSAGE *)((UCHAR *)msg + Work); // ADJBUFFER points to CTL, etc. allowing for digis + + CTL = ADJBUFFER->CTL; + + if (CTL & PFBIT) + PF = TRUE; + + CTL &= ~PFBIT; + + *FrameType = CTL; + + Stamp = Stamp % 86400; // Secs + HH = Stamp / 3600; + + Stamp -= HH * 3600; + MM = Stamp / 60; + + SS = Stamp - MM * 60; + + Output += sprintf((char *)Output, " %d:Fm ", msg->PORT & 0x7f); // Mask TX bit + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + Output += sprintf((char *)Output, "%s To %s", From, To); + + // Display any Digi-Peaters + + n = 8; // Max number of digi-peaters + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + + if (n == 8) + Output += sprintf((char *)Output, " Via %s", From); // Send via on first + else + Output += sprintf((char *)Output, ",%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + *(Output++) = '*'; + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + *(Output++) = '*'; // No, so need * + } + } + + *(Output++) = ' '; + + // Set up CR and PF + + CRCHAR[0] = 0; + PFCHAR[0] = 0; + + if (msg->DEST[6] & 0x80) + { + if (msg->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + { + MSGFLAG |= CMDBIT; + CRCHAR[0] = ' '; + CRCHAR[1] = 'C'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'P'; + } + } + } + else + { + if (msg->ORIGIN[6] & 0x80) // Only Origin Set + { + MSGFLAG |= RESP; + CRCHAR[0] = ' '; + CRCHAR[1] = 'R'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'F'; + } + } + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + if ((CTL & 1) == 0) // I frame + { + int NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + int NR = (CTL >> 5) & 7; + + Info = 1; + + Output += sprintf((char *)Output, "", CRCHAR, PFCHAR, NS, NR); + } + else if (CTL == 3) + { + // Un-numbered Information Frame + //UI pid=F0 Len=20 > + + Output += sprintf((char *)Output, "", ADJBUFFER->PID, (int)MsgLen - 23); + Info = 1; + } + else if (CTL & 2) + { + // UN Numbered + + char SUP[5] = "??"; + + switch (CTL) + { + case SABM: + + strcpy(SUP, "C"); + break; + + case DISC: + + strcpy(SUP, "D"); + break; + + case DM: + + strcpy(SUP, "DM"); + break; + + case UA: + + strcpy(SUP, "UA"); + break; + + + case FRMR: + + strcpy(SUP, "FRMR"); + FRMRFLAG = 1; + break; + } + + Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); + } + else + { + // Super + + int NR = (CTL >> 5) & 7; + char SUP[4] = "??"; + + switch (CTL & 0x0F) + { + case RR: + + strcpy(SUP, "RR"); + break; + + case RNR: + + strcpy(SUP, "RNR"); + break; + + case REJ: + + strcpy(SUP, "REJ"); + break; + } + + Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); + + } + + Output += sprintf((char *)Output, "[%02d:%02d:%02d]", HH, MM, SS); + + + if (FRMRFLAG) + Output += sprintf((char *)Output, "%02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + + if (Info) + { + // We have an info frame + + switch (ADJBUFFER->PID) + { + case 0xF0: // Normal Data + { + char Infofield[257]; + char * ptr1 = Infofield; + char * ptr2 = ADJBUFFER->L2DATA; + UCHAR C; + size_t len; + + MsgLen = MsgLen - 23; + + if (MsgLen < 0 || MsgLen > 257) + return 0; // Duff + + while (MsgLen--) + { + C = *(ptr2++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + +// Output[0] = ':'; + Output[0] = 13; + memcpy(&Output[1], Infofield, len); + Output += (len + 1); + + break; + } + case NETROM_PID: + + Output = DISPLAY_NETROM(ADJBUFFER, Output,(int) MsgLen); + break; + + case IP_PID: + + Output += sprintf((char *)Output, " \r"); + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, (int)MsgLen); + break; + + case ARP_PID: + + Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output); + break; + + case 8: // Fragmented IP + + Output += sprintf((char *)Output, ""); + break; + } + } + + if (Output[-1] != 13) + Output += sprintf((char *)Output, "\r"); + + return (int)(Output - buffer); + +} +// Display NET/ROM data + +UCHAR * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen) +{ + char Alias[7]= ""; + char Dest[10]; + char Node[10]; + UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window; + UCHAR * ptr = &ADJBUFFER->L2DATA[0]; + + if (ADJBUFFER->L2DATA[0] == NODES_SIG) + { + // Display NODES + + + // If an INP3 RIF (type <> UI) decode as such + + if (ADJBUFFER->CTL != 3) // UI + return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - 24); + + memcpy(Alias, ++ptr, 6); + + ptr += 6; + + Output += sprintf((char *)Output, "\rFF %s (NetRom Routing)\r", Alias); + + MsgLen -= 30; //Header, mnemonic and signature length + + while(MsgLen > 20) // Entries are 21 bytes + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + memcpy(Alias, ptr, 6); + ptr +=6; + strlop(Alias, ' '); + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]); + ptr++; + MsgLen -= 21; + } + return Output; + } + + // Display normal NET/ROM transmissions + + Output += sprintf((char *)Output, " NET/ROM\r "); + + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + TTL = *(ptr++); + Index = *(ptr++); + ID = *(ptr++); + TXNO = *(ptr++); + RXNO = *(ptr++); + OpCode = Flags = *(ptr++); + + OpCode &= 15; // Remove Flags + + Output += sprintf((char *)Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID ); + MsgLen -= 20; + + switch (OpCode) + { + case L4CREQ: + + Window = *(ptr++); + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " w=%d %s at %s", Window, Dest, Node); + + if (MsgLen > 38) // BPQ Extended Params + { + short Timeout = (SHORT)*ptr; + Output += sprintf((char *)Output, " t/o %d", Timeout); + } + + return Output; + + case L4CACK: + + if (Flags & L4BUSY) // BUSY RETURNED + return Output + sprintf((char *)Output, " - BUSY"); + + return Output + sprintf((char *)Output, " w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO); + + case L4DREQ: + + return Output + sprintf((char *)Output, " "); + + case L4DACK: + + return Output + sprintf((char *)Output, " "); + + case L4INFO: + { + char Infofield[257]; + char * ptr1 = Infofield; + UCHAR C; + size_t len; + + Output += sprintf((char *)Output, " ", TXNO, RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + MsgLen = MsgLen - 23; + + if (MsgLen < 0 || MsgLen > 257) + return Output; // Duff + + while (MsgLen--) + { + C = *(ptr++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + } + + return Output; + + case L4IACK: + + Output += sprintf((char *)Output, " ", RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + return Output; + + + case 0: + + // OPcode zero is used for several things + + if (Index == 0x0c && ID == 0x0c) // IP + { +// Output = L3IP(Output); + return Output; + } + + if (Index == 0 && ID == 1) // NRR + { + Output += sprintf((char *)Output, " \r"); + + MsgLen -= 23; + + while (MsgLen > 6) + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + + if (ptr[7] & 0x80) + Output += sprintf((char *)Output, "%s* ", Dest); + else + Output += sprintf((char *)Output, "%s ", Dest); + + ptr +=8; + MsgLen -= 8; + } + + return Output; + } + } + + Output += sprintf((char *)Output, " "); + return Output; +} + +/* + + PUBLIC L3IP +L3IP: +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER + +*/ +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen) +{ + UCHAR * ptr; + + ptr = (UCHAR *)&IP->IPSOURCE; + Output += sprintf((char *)Output, "%d.%d.%d.%d>", ptr[0], ptr[1], ptr[2], ptr[3]); + + ptr = (UCHAR *)&IP->IPDEST; + Output += sprintf((char *)Output, "%d.%d.%d.%d LEN:%d ", ptr[0], ptr[1], ptr[2], ptr[3], htons(IP->IPLENGTH)); + +/* + MOV AL,IPPROTOCOL[ESI] + CMP AL,6 + JNE @F + + MOV EBX, OFFSET TCP + CALL NORMSTR + JMP ADD_CR +@@: + + CMP AL,1 + JNE @F + + MOV EBX, OFFSET ICMP + CALL NORMSTR + JMP ADD_CR +@@: + + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + +; mov AL,CR +; call PUTCHAR +; +; MOV ECX,39 ; TESTING +;IPLOOP: +; lodsb +; CALL BYTE_TO_HEX +; +; LOOP IPLOOP + + JMP ADD_CR + + +*/ + return Output; +} + + + +UCHAR * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output) +{ + UCHAR * ptr = Datagram; + UCHAR Dest[10]; + + if (ptr[7] == 1) // Request + return Output + sprintf((char *)Output, " < ARP Request who has %d.%d.%d.%d? Tell %d.%d.%d.%d", + ptr[26], ptr[27], ptr[28], ptr[29], ptr[15], ptr[16], ptr[17], ptr[18]); + + // Response + + Dest[ConvFromAX25(&ptr[8], Dest)] = 0; + + return Output + sprintf((char *)Output, " < ARP Rreply %d.%d.%d.%d? is at %s", + ptr[15], ptr[16], ptr[17], ptr[18], "??"); + +} diff --git a/AISCommon.c b/AISCommon.c new file mode 100644 index 0000000..300e888 --- /dev/null +++ b/AISCommon.c @@ -0,0 +1,3291 @@ + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include "time.h" +#include "CHeaders.h" +//#include "tncinfo.h" +//#include "adif.h" +//#include "telnetserver.h" + +VOID __cdecl Debugprintf(const char * format, ...); + +#define TRACKPOINTS 50 + +typedef struct TARGETRECORD +{ + UINT ID; + char name[21]; + char Callsign[8]; + char Dest[21]; + UINT IMO; + double ROT; + int LengthA; + int LengthB; + int WidthC; + int WidthD; + int Draft; + double lat; + double Long; + double course; + double speed; + double Heading; + int NavStatus; + UINT TCA; + UINT BCA; // Bearing at closest + UINT TimeAdded; + UINT TimeLastUpdated; + char Type; + char Class; + + // Fields for Intellegent TrackLog + + double LatTrack[TRACKPOINTS]; // Cyclic Tracklog + double LonTrack[TRACKPOINTS]; + time_t TrackTime[TRACKPOINTS]; + int Trackptr; // Next record in Tracklog + + // info about last track point written + + double Lastlat; + double LastLong; + double LastCourse; + double LastSpeed; + + time_t LastTime; + + int Alt; // For SAR Aircraft + + +} TargetRecord; + +typedef struct NAVAIDRECORD +{ + UINT ID; + char name[21]; + int NavAidType; + double lat; + double Long; + int LengthA; + int LengthB; + int WidthC; + int WidthD; + int FixType; + time_t TimeAdded; + time_t TimeLastUpdated; + +} NavaidRecord; + +typedef struct BASESTATIONRECORD +{ + UINT ID; + int FixType; + double lat; + double Long; + int NumberofStations; + time_t TimeAdded; + time_t TimeLastUpdated; + +} BaseStationRecord; + +typedef struct SHIPDATABASERECORD +{ + UINT ID; + char name[21]; + char Callsign[8]; + int LengthA; + int LengthB; + int WidthC; + int WidthD; + int Draft; + time_t TimeAdded; + time_t TimeLastUpdated; + char Type; + char Class; + +} ShipDatabaseRecord; + +void LookupVessel(UINT UserID); +void LoadVesselDataBase(); +void SaveVesselDataBase(); +void LoadNavAidDataBase(); +void SaveNavAidDataBase(); + + +typedef struct SARRECORD +{ + UINT ID; + char name[21]; + char Callsign[8]; + char Dest[21]; + UINT IMO; + double ROT; + int LengthA; + int LengthB; + int WidthC; + int WidthD; + int Draft; + double lat; + double Long; + double course; + double speed; + double Heading; + int NavStatus; + UINT TCA; + UINT BCA; // Bearing at closest + time_t TimeAdded; + time_t TimeLastUpdated; + char Type; + char Class; + + // Fields for Intellegent TrackLog + + double LatTrack[TRACKPOINTS]; // Cyclic Tracklog + double LonTrack[TRACKPOINTS]; + time_t TrackTime[TRACKPOINTS]; + int Trackptr; // Next record in Tracklog + + // info about last track point written + + double Lastlat; + double LastLong; + double LastCourse; + double LastSpeed; + + time_t LastTime; + + int Alt; // For SAR Aircraft + + +} SARRecord; + + +typedef struct ADSBRECORD +{ + char hex[8]; //the 24-bit ICAO identifier of the aircraft, as 6 hex digits. The identifier may + //start with '~', this means that the address is a non-ICAO address (e.g. from TIS-B). + char squawk[5]; // the 4-digit squawk (octal representation) + char flight[32]; // the flight name / callsign + double lat; + double lon; // the aircraft position in decimal degrees + // nucp: the NUCp (navigational uncertainty category) reported for the position + int seen_pos; // how long ago (in seconds before "now") the position was last updated + int altitude; // the aircraft altitude in feet, or "ground" if it is reporting it is on the ground + int vert_rate; // vertical rate in feet/minute + int track; // true track over ground in degrees (0-359) + int speed; // reported speed in kt. This is usually speed over ground, but might be IAS - you can't tell the difference here, sorry! + int messages; // total number of Mode S messages received from this aircraft + int seen; // how long ago (in seconds before "now") a message was last received from this aircraft + int rssi; // recent average RSSI (signal power), in dbFS; this will always be negative. + + time_t TimeAdded; + time_t TimeLastUpdated; + + // Fields for Intellegent TrackLog + + double LatTrack[TRACKPOINTS]; // Cyclic Tracklog + double LonTrack[TRACKPOINTS]; + time_t TrackTime[TRACKPOINTS]; + int Trackptr; // Next record in Tracklog + + // info about last track point written + + double Lastlat; + double LastLong; + int LastCourse; + int LastSpeed; + time_t LastTime; + +} ADSBRecord; + +extern BOOL TraceAIS; +extern BOOL NoVDO; +extern BOOL NoAlarms; + + +UINT PaintColour; +//char Msg[1000]; +//UINT Colour; +char CloseString[100]; +//extern int ListItem; + + +SOCKET udpsock; + +SOCKADDR_IN sinx; + +SOCKADDR_IN rxaddr, txaddr, pitxaddr; + +double pi = 3.14159265389754; + +double OurSog, OurCog, OurLatitude, OurLongtitude; +double OurLatIncr, OurLongIncr; + +unsigned int UserID; +int NavStatus; +double V_Lat, V_Lon, V_COG, V_SOG, V_ROT; +char V_Name[21], V_Callsign[8], V_Dest[21], V_VendorID[8]; +int V_IMO, V_Heading, V_Type, B_PartNo; +int Msg_Type, Accuracy, FixType; +int UTCyear, UTCMonth, UTCDay, UTCHour, UTCMinute, UTCSecond; +int NavAidType, V_Draught, ETA; +int Alt; + +int V_LenA, V_LenB, V_WidthC, V_WidthD; +int SyncState, SlotTO, SubMsg; + +int GpsOK; +int DecayTimer; +static int MaxAge = 7200; // 2 Hours + +int VessselDBChanged = 0; +int NavAidDBChanged = 0; + +BOOL SoundActive; + +extern BOOL KeepTracks; + +HANDLE TrackHandle; +HANDLE hTrack; +UINT LastTrackFileTime; + +UINT NOW; + +char Stack[500]; + +BOOL Stacked=FALSE; + + +int NavAidCount=0; + +struct NAVAIDRECORD ** NavRecords; + +int SARCount=0; +struct SARRECORD ** SARRecords; + +int TargetCount=0; + +struct TARGETRECORD ** TargetRecords; + +int BaseStationCount=0; + +struct BASESTATIONRECORD ** BaseStationRecords; + +int ADSBCount = 0; + +struct ADSBRECORD ** ADSBRecords; + +UCHAR conv[256]={99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 00 + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 30 + + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 40 @ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 34, 35, 36, 37, 38, 39, // 50 P + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, // 60 + 56, 57, 58, 59, 60, 61, 62, 63, 0, 1, 2, 3, 4, 5, 6, 7, // 77 = + + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}; + +char *NavAidString[] = { + "Default, Type of A to N not specified", + "Reference Point", + "RACON", + "Structure Off shore Structure", + "Spare", + "Light, without sectors", + "Light, with sectors", + "Leading Light Front", + "Leading Light Rear", + "Beacon, Cardinal N", + "Beacon, Cardinal E", + "Beacon, Cardinal S", + "Beacon, Cardinal W", + "Beacon, Port hand", + "Beacon, Starboard hand", + "Beacon, Preferred Channel port hand", + "Beacon, Preferred Channel starboard hand", + "Beacon, Isolated danger", + "Beacon, Safe water", + "Beacon, Special mark", + "Cardinal Mark N", + "Cardinal Mark E", + "Cardinal Mark S", + "Cardinal Mark W", + "Port hand Mark", + "Starboard hand Mark", + "Preferred Channel Port hand", + "Preferred Channel Starboard hand", + "Isolated danger", + "Safe Water", + "Special Mark", + "Light Vessel / LANBY / Rigs"}; + +char * FixTypeString[] = { + "Undefined", + "GPS", + "GLONASS", + "Combined GPS/GLONASS", + "Loran - C", + "Chayka", + "Integrated Navigation System", + "surveyed"}; + + +char * NavStatusString[] = { + "Under way using engine", + "At anchor", + "Not under command", + "Restricted manoeuvrability", + "Constrained by draught", + "Moored", + "Aground", + "Engaged in Fishing", + "Under way sailing", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "Not defined"}; + +char * VesselType[] = { // First Digit + "Undefined", + "Reserved for future use", + "WIG", // 2 + "", // 3 + "HSC", + "", + "Passenger ship", + "Cargo ship", + "Tanker", + "Other types of ship"}; + +char * VesselType3[] = { + "Fishing", + "Towing", + "Towing Len > 200 m or breadth > 25 m", + "Dredging or underwater operations", + "Diving", + "Military", + "Sailing", + "Pleasure Craft", + "Reserved", + "Reserved"}; + +char * VesselType5[] = { + "Pilot vessel", + "Search and rescue vessel", + "Tug", + "Port tender", + "Vessel with anti-pollution facilities or equipment", + "Law enforcement vessel", + "Spare", + "Spare", + "Medical transport", + "Ships according to Resolution No 18 (Mob-83)"}; + +extern struct SHIPDATABASERECORD * ShipDataRec; + +int ACount = 0, BCount = 0; + +char ADSBHost[64] = ""; //"10.8.0.50"; +int ADSBPort = 30003; +int ADSBConnected = 0; + +extern double Lat; +extern double Lon; + + +// Forward declarations of functions included in this code module: + + +double radians(double Degrees); +double degrees(double radians); +BOOL Check0183CheckSum(char * msg,int len); +int ProcessNMEAMessage(char * msg, int len); +int DecodeVDM(char * Data); +int ConvertAsciito6Bit(char * msg, char * buffer, int Len); +void ProcessAISVesselMessage(); +void ProcessSARMessage(); +void ProcessBaseStationMessage(); +void ProcessAISVesselDetails(char Type); +void ProcessAISNavAidMessage(); +void MM_Reinit(); +void CalculateClosestApproach(struct TARGETRECORD * ptr); +static double Distance(double lah, double loh, double laa, double loa); +static double Bearing(double lat1, double lon1, double lat2, double lon2); +double CalcClosest(struct TARGETRECORD * ptr); +void RefreshTargetList(); +void RefreshTargetSub(struct TARGETRECORD * ptr, int j); +void CheckAgeofTargets(UINT MaxAge); +void ActivateTarget(); +void Write_Target_Track(struct TARGETRECORD * ptr); +void Write_Our_Track(); +void MaintainVesselDatabase(UINT UserID, char Type); +VOID AutoPilotUpdate(); +VOID ProcessNMEAMessageCE(char * msg, int len); +void ProcessADSBJSON(char * FN); +void CheckAgeofPlanes(UINT MaxAge); +static VOID ADSBConnect(void * unused); + +char ADSBFN[256] = "/dev/shm/adsb/aircraft.json"; + +char DataBasefn[MAX_PATH]; + +int ShipDatabaseCount=0; + +struct SHIPDATABASERECORD ** ShipDatabaseRecords; + +struct SHIPDATABASERECORD * ShipDataRec; + +void SaveTrackPoint(struct TARGETRECORD * ptr) +{ + // If course and speed have changed, and distance travelled or time is significant, add a track point to log + + if ((NOW - ptr->LastTime) < 15) // Not More that once per 15 secs + return; + + if (fabs(ptr->LastCourse - ptr->course) < 1.0 && fabs(ptr->LastSpeed - ptr->speed) < 0.2 && ptr->speed > 0.2) // if very slow, COG may be random + { + // Steady Course - update once per 5 minutes if moving + + if ((NOW - ptr->LastTime) > 600 && ptr->speed != 0) + goto writeRec; + } + + if (fabs(ptr->Lastlat - ptr->lat) < 0.01 && fabs(ptr->LastLong - ptr->Long) < 0.01) // Not moved much + return; + +writeRec: + + ptr->LastCourse = ptr->course; + ptr->LastSpeed = ptr->speed; + ptr->LastTime = NOW; + ptr->Lastlat = ptr->lat; + ptr->LastLong = ptr->Long; + + ptr->LatTrack[ptr->Trackptr] = ptr->lat; + ptr->LonTrack[ptr->Trackptr] = ptr->Long; + ptr->TrackTime[ptr->Trackptr] = NOW; + + ptr->Trackptr++; + + if (ptr->Trackptr == TRACKPOINTS) + ptr->Trackptr = 0; + +} + + +void SaveSARTrackPoint(struct SARRECORD * ptr) +{ + // If course and speed have changed, and distance travelled or time is significant, add a track point to log + + if ((NOW - ptr->LastTime) < 15) // Not More that once per 15 secs + return; + + if (fabs(ptr->LastCourse - ptr->course) < 1.0 && fabs(ptr->LastSpeed - ptr->speed) < 0.2 && ptr->speed > 0.2) // if very slow, COG may be random + { + // Steady Course - update once per 5 minutes if moving + + if ((NOW - ptr->LastTime) > 600 && ptr->speed != 0) + goto writeRec; + } + + if (fabs(ptr->Lastlat - ptr->lat) < 0.01 && fabs(ptr->LastLong - ptr->Long) < 0.01) // Not moved much + return; + +writeRec: + + ptr->LastCourse = ptr->course; + ptr->LastSpeed = ptr->speed; + ptr->LastTime = NOW; + ptr->Lastlat = ptr->lat; + ptr->LastLong = ptr->Long; + + ptr->LatTrack[ptr->Trackptr] = ptr->lat; + ptr->LonTrack[ptr->Trackptr] = ptr->Long; + ptr->TrackTime[ptr->Trackptr] = NOW; + + ptr->Trackptr++; + + if (ptr->Trackptr == TRACKPOINTS) + ptr->Trackptr = 0; + +} + + + +void MaintainVesselDatabase(UINT UserID, char Type) +{ + LookupVessel(UserID); // Find Vessel - if not present, add it + + if (ShipDataRec == NULL) return; // Malloc failed!! + + if (Type == 'A') ShipDataRec->Class='A'; else ShipDataRec->Class='B'; + + if (Type == 'A') + { + memcpy(ShipDataRec->name, V_Name, 21); + memcpy(ShipDataRec->Callsign,V_Callsign,8); + ShipDataRec->Type = V_Type; + ShipDataRec->LengthA = V_LenA; + ShipDataRec->LengthB = V_LenB; + ShipDataRec->WidthC = V_WidthC; + ShipDataRec->WidthD = V_WidthD; + } + + if (Type == 'B') // Type 19 Class B Details + { + memcpy(ShipDataRec->name, V_Name, 21); + ShipDataRec->Type = V_Type; + ShipDataRec->LengthA = V_LenA; + ShipDataRec->LengthB = V_LenB; + ShipDataRec->WidthC = V_WidthC; + ShipDataRec->WidthD = V_WidthD; + } + + if (Type == '0') // Class B record 24A + { + memcpy(ShipDataRec->name, V_Name, 21); + } + + if (Type == '1') // Class B record 24B + { + memcpy(ShipDataRec->Callsign,V_Callsign,8); + ShipDataRec->Type = V_Type; + ShipDataRec->LengthA = V_LenA; + ShipDataRec->LengthB = V_LenB; + ShipDataRec->WidthC = V_WidthC; + ShipDataRec->WidthD = V_WidthD; + } + + ShipDataRec->TimeLastUpdated = NOW; + + VessselDBChanged = 1; // Save on next timer tick + return; + +} + +void LookupVessel(UINT UserID) +{ + int i; + + for (i = 0; i < ShipDatabaseCount; i++) + { + ShipDataRec=ShipDatabaseRecords[i]; + + if (ShipDataRec->ID == UserID) return; + } + +// Not found + + if (ShipDatabaseCount == 0) + ShipDatabaseRecords=(struct SHIPDATABASERECORD **)malloc(sizeof(void *)); + else + ShipDatabaseRecords=(struct SHIPDATABASERECORD **)realloc(ShipDatabaseRecords,(ShipDatabaseCount+1)*sizeof(void *)); + + ShipDataRec=(struct SHIPDATABASERECORD *)malloc(sizeof(struct SHIPDATABASERECORD)); + memset(ShipDataRec, 0, sizeof(struct SHIPDATABASERECORD)); + + if (ShipDataRec == NULL) return; + + ShipDatabaseRecords[ShipDatabaseCount]=ShipDataRec; + + ShipDataRec->ID = UserID; + + ShipDataRec->TimeAdded = NOW; + + ShipDatabaseCount++; + + sprintf(ShipDataRec->name,"%d",UserID); + +} + +void LoadVesselDataBase() +{ + int i; + + FILE *file; + char buf[256]; + char *token; + char FN[256]; + + if (BPQDirectory[0] == 0) + { + strcpy(FN, "AIS_Vessels.txt"); + } + else + { + strcpy(FN, BPQDirectory); + strcat(FN, "/"); + strcat(FN, "AIS_Vessels.txt"); + } + + if ((file = fopen(FN,"r")) == NULL) return; + + fgets(buf, 255, file); + + sscanf(buf,"%d", &ShipDatabaseCount); + + if (ShipDatabaseCount == 0) + { + fclose(file); + return; + } + + ShipDatabaseRecords = (struct SHIPDATABASERECORD **)malloc(ShipDatabaseCount * sizeof(void *)); + + for (i = 0; i < ShipDatabaseCount; i++) + { + ShipDataRec = (struct SHIPDATABASERECORD *)malloc(sizeof(struct SHIPDATABASERECORD)); + ShipDatabaseRecords[i] = ShipDataRec; + memset(ShipDataRec, 0, sizeof(struct SHIPDATABASERECORD)); + + fgets(buf, 255, file); + + token = strtok(buf, "|\n" ); + + ShipDataRec->ID = atoi(token); + + token = strtok( NULL, "|\n" ); + strcpy(&ShipDataRec->name[0],token); + + token = strtok( NULL, "|\n" ); + strcpy(&ShipDataRec->Callsign[0],token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->LengthA = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->LengthB = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->WidthC = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->WidthD = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->Draft = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->TimeAdded = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->TimeLastUpdated = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->Type = atoi(token); + + token = strtok( NULL, "|\n" ); + ShipDataRec->Class = atoi(token); + + } + + fclose(file); + +} + +void SaveVesselDataBase() +{ + int i, n = 0; + + FILE *file; + char buf[256]; + char FN[256]; + + if (BPQDirectory[0] == 0) + { + strcpy(FN, "AIS_Vessels.txt"); + } + else + { + strcpy(FN, BPQDirectory); + strcat(FN, "/"); + strcat(FN, "AIS_Vessels.txt"); + } + + if ((file = fopen(FN,"w")) == NULL) return; + + for (i = 0; i < ShipDatabaseCount; i++) + { + if (ShipDatabaseRecords[i]->Callsign[0] > 32) // Count those with details + n++; + } + + sprintf(buf,"%d\n", n); + fputs(buf, file); + + for (i = 0; i < ShipDatabaseCount; i++) + { + ShipDataRec = ShipDatabaseRecords[i]; + + if (ShipDataRec->Callsign[0] > 32) // No point in saving if no vessel details + { + sprintf(buf,"%d|%s|%s|%d|%d|%d|%d|%d|%d|%d|%d|%d\n", + ShipDataRec->ID, + ShipDataRec->name, + ShipDataRec->Callsign, + ShipDataRec->LengthA, + ShipDataRec->LengthB, + ShipDataRec->WidthC, + ShipDataRec->WidthD, + ShipDataRec->Draft, + ShipDataRec->TimeAdded, + ShipDataRec->TimeLastUpdated, + ShipDataRec->Type, + ShipDataRec->Class); + + fputs(buf, file); + } + } + + fclose(file); + +} + +void LoadNavAidDataBase() +{ + int i; + + FILE *file; + char buf[256]; + char *token; + char FN[256]; + struct NAVAIDRECORD * navptr; + + if (BPQDirectory[0] == 0) + { + strcpy(FN, "AIS_NavAids.txt"); + } + else + { + strcpy(FN, BPQDirectory); + strcat(FN, "/"); + strcat(FN, "AIS_NavAids.txt"); + } + + if ((file = fopen(FN,"r")) == NULL) return; + + fgets(buf, 255, file); + + sscanf(buf,"%d", &NavAidCount); + + if (NavAidCount == 0) + { + fclose(file); + return; + } + + NavRecords = (struct NAVAIDRECORD **)malloc(NavAidCount * sizeof(void *)); + + for (i = 0; i < NavAidCount; i++) + { + navptr = (struct NAVAIDRECORD *)malloc(sizeof(struct NAVAIDRECORD)); + NavRecords[i] = navptr; + memset(navptr, 0, sizeof(struct NAVAIDRECORD)); + + fgets(buf, 255, file); + + token = strtok(buf, "|\n" ); + navptr->ID = atoi(token); + + token = strtok(NULL, "|\n" ); + strcpy(&navptr->name[0],token); + + token = strtok(NULL, "|\n" ); + navptr->lat = atof(token); + + token = strtok(NULL, "|\n" ); + navptr->Long = atof(token); + + token = strtok(NULL, "|\n" ); + navptr->TimeAdded = atoi(token); + + token = strtok(NULL, "|\n" ); + navptr->TimeLastUpdated = atoi(token); + } + + fclose(file); +} + + +void SaveNavAidDataBase() +{ + int i; + + FILE *file; + char FN[256]; + struct NAVAIDRECORD * navptr; + + if (BPQDirectory[0] == 0) + { + strcpy(FN, "AIS_NavAids.txt"); + } + else + { + strcpy(FN, BPQDirectory); + strcat(FN, "/"); + strcat(FN, "AIS_NavAids.txt"); + } + + if ((file = fopen(FN,"w")) == NULL) return; + + fprintf(file, "%d\n", NavAidCount); + + for (i = 0; i < NavAidCount; i++) + { + navptr=NavRecords[i]; + fprintf(file, "%d|%s|%f|%f|%d|%d\n", navptr->ID, navptr->name, navptr->lat, navptr->Long, navptr->TimeAdded, navptr->TimeLastUpdated); + } + + fclose(file); +} + +void initAIS() +{ + LoadVesselDataBase(); + LoadNavAidDataBase(); +} + +void SaveAIS() +{ + SaveVesselDataBase(); + SaveNavAidDataBase(); +} + +void initADSB() +{ + _beginthread(ADSBConnect, 0, 0); +} + + +int ProcessAISMessage(char * msg, int len) +{ + char * ptr1; + char * ptr2; + char X1[10], X2[10], X3[10], X4[10]; + char Data[256]; + float alt=0.0; + + NOW = time(NULL); + + ptr1 = &msg[7]; + + //' Looks like first is no of fragments in message, 2nd fragment seq no, 3rd msg seq, 4th port + //'!AIVDM,2,1,7,2,502He5h000030000000AD8hTr10u9B1IA<0000000000040000<000000000,0*54 + //'!AIVDM,2,2,7,2,00000000008,2*58 + + if (memcmp(&msg[1],"AIVDM",5) == 0) + { + if (memchr(msg,'*',len) == 0) return 0; // No Checksum + + len-=7; + + ptr2=(char *)memchr(ptr1,',',8); + + if (ptr2 == 0) return 0; // Duff + + *(ptr2++)=0; + strcpy(X1,ptr1); + ptr1=ptr2; + + ptr2=(char *)memchr(ptr1,',',8); + + if (ptr2 == 0) return 0; // Duff + + *(ptr2++)=0; + strcpy(X2,ptr1); + ptr1=ptr2; + + ptr2=(char *)memchr(ptr1,',',8); + + if (ptr2 == 0) return 0; // Duff + + *(ptr2++)=0; + strcpy(X3,ptr1); + ptr1=ptr2; + + if (*ptr1 == 'A') + ACount++; + else + BCount++; + + ptr2=(char *)memchr(ptr1,',',8); + + if (ptr2 == 0) return 0; // Duff + + *(ptr2++)=0; + strcpy(X4,ptr1); + ptr1=ptr2; + + ptr2=(char *)memchr(ptr1,',',78); + + if (ptr2 == 0) return 0; // Duff + + *(ptr2++)=0; + strcpy(Data,ptr1); + + if (X1[0] == '1') + { + // Single fragment + + DecodeVDM (Data); + return 0; + } + + // Multipart. + + if (X2[0] == '1') + { + // First of Multipart + + strcpy(Stack,Data); + return 0; // ' this needs generalising + } + + // Subsequent part of Multipart + + strcat(Stack, Data); + + if (X1[0] == X2[0]) + { + // Last Part + + DecodeVDM (Stack); + return 0; + } + } + + return 0; + +} +int mm1=0, nasa1=0, mm4=0, nasa4=0; + + + +int DecodeVDM(char *msg) +{ + int Conv, i, bits; + + UCHAR val[500]; + + // Start value at offet 1 for compatiblilty woth Basic Code + + ConvertAsciito6Bit(msg, (char *)&val[1], strlen(msg)); // Convert ascii chars to 6-bit values + + Msg_Type = val[1]; + + UserID = ((val[2] & 15) << 26) + (val[3] << 20)+ (val[4] << 14)+ (val[5] << 8)+ (val[6] << 2) + (val[7] >>4); ; + + // if (UserID == 0 ) UserID = 99998888; + + bits=strlen(msg)*6; + + if (Msg_Type == 1 || Msg_Type == 2 || Msg_Type == 3) + { + if (bits == 168) nasa1++; else mm1++; + + NavStatus = val[7] & 15; + + //'Rate of turn 8 50 Char 8 Char 9 bits 0-1±127 (–128 (80 hex) indicates not available, which should be the + + V_ROT = (val[8] << 2) + (val[9] >> 4); + + /* + ' 0...+ 126 = turning right at up to 708 degrees per minute or higher; + ' 0...- 126 = turning left at up to 708 degrees per minute or higher + ' Values between 0 and 708 degrees/min coded by + ' ROTAIS=4.733 SQRT(ROTsensor) degrees/min + ' where ROTsensor is the Rate of Turn as input by an external Rate of Turn + ' Indicator. ROTAIS is rounded to the nearest integer value. + ' + 127 = turning right at more than 5 deg/30s (No TI available) + ' -127 = turning Left at more than 5 deg/30s (No TI available) + ' –128 (80 hex) indicates no turn information available (default). + */ + + if (V_ROT > 128) V_ROT = -(256 - V_ROT); + + if (V_ROT == 128) + + V_ROT = 0; + + else if (V_ROT == 127) + + V_ROT = 3; // 5 deg / 30 secs or more + + else if (V_ROT == -127) + + V_ROT = -3; + + else + { + V_ROT = V_ROT / 4.733; + V_ROT = V_ROT * fabs(V_ROT); + + } + + //'SOG 10 60 Char 9 bits 2-5 char 10 Speed over ground in 1/10 knot steps (0-102.2 knots) + + V_SOG = (((val[9] & 15) << 6) + val[10]); + + if (V_SOG == 1023.0) // Unknown + V_SOG = 0.0; + + V_SOG = V_SOG/10; + + + //'Accuracy 1 61 Char 11 bit 0 + + Accuracy = val[11] >> 5; + + //'Long 28 89 char 11 bits 1-5 char 12 13 14 char 15 bita 0-4 + + + Conv = ((val[11] & 31) << 23) + (val[12] << 17) + (val[13] << 11) + (val[14] << 5) + (val[15] >>1); + + if ((val[11] & 16) == 16) + Conv = -(0x10000000 - Conv); + + V_Lon = Conv; + V_Lon/= 600000; + + if (V_Lon > 18 || V_Lon < -18) + return 0; + + + //Lat 27 116 Char 15 bit 5 16 17 18 19 20 bits 0-1 + + Conv = ((val[15] & 1) << 26) + (val[16]<< 20) + (val[17]<< 14) + (val[18]<< 8) + (val[19]<< 2) + (val[20] >>4); + + if ((val[15] & 1) == 1) + Conv = -(0x8000000 - Conv); + + + V_Lat = Conv; + V_Lat/= 600000; + + + //COG 12 128 char 20 bits 2-5 21 22 bit 0-1 + + V_COG = ((val[20] & 15) << 8) + (val[21]<< 2) + (val[22] >>4); + + V_COG /= 10; + + //True Heading 9 137 char 22 bits 2-5 char 23 bits 0-4 + + V_Heading = ((val[22] & 15) << 5) + (val[23] >>1); + + /* + + 'Message ID 6 Char 1 Identifier for this message 1, 2 or 3 + 'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been + ' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. + 'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number + 'Navigational status 4 42 Char 7 bits 2-5 0 = under way using engine, 1 = at anchor, 2 = not under command, + + 'Rate of turn 8 50 Char 8 Char 9 bits 0-1±127 (–128 (80 hex) indicates not available, which should be the + 'SOG 10 60 Char 9 bits 2-5 char 10 Speed over ground in 1/10 knot steps (0-102.2 knots) + + 'Accuracy 1 61 Char 11 bit 0 + 'Long 28 89 char 11 bits 1-5 char 12 13 14 char 15 bita 0-4 + 'Lat 27 116 Char 15 bit 5 16 17 18 19 20 bits 0-1 + 'COG 12 128 char 20 bits 2-5 21 22 bit 0-1 + 'True Heading 9 137 char 22 bits 2-5 char 23 bits 0-4 + 'Time Stamp 6 143 char 23 bits 5 24 bits 0-4 + 'Reserved 4 147 char 24 bits 5 char 25 bits 0-2 + 'Spare 1 148 char 25 bit 3 + 'RAIM 1 149 char 25 bit 4 + 'Comms State 19 168 char 25 bit 5 26 27 28 See below. + 'Total number of bits 168 28 chars + + */ + + ProcessAISVesselMessage(); + + return 0; + } + + + // if (Msg_Type == 4 || Msg_Type == 11) // 11 are probably ships responding to Tamestamp requests + if (Msg_Type == 4) + { + if (bits == 168) nasa4++; else mm4++; + + + // Base Station + + //'Message ID 6 Char 1 Identifier for this message 1, 2 or 3 + //'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been + //' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. + //'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number + + //'UTC year 14 52 Char 7 bits 2-5, char 8, char 9 bits 0-3 1 - 9999; 0 = UTC year not available = default. + + UTCyear = ((val[7] & 15) << 10) + (val[8] << 4) + (val[9] >> 2); + + //'UTC month 4 56 Char 9 bits 4-5, char 10 bits 0-1 1 - 12; 0 = UTC month not available = default; 13 - 15 not used + + UTCMonth = ((val[9] & 3) << 2) + (val[10] >> 4); + + //'UTC day 5 61 Char 10 bits 2-5, chr 11 bit 0 1 - 31; 0 = UTC day not available = default. + + UTCDay = ((val[10] & 15) << 1) + (val[11] >> 5); + + //'UTC hour 5 66 Char 11 bits 2-5 0 - 23; 24 = UTC hour not available = default; + + UTCHour = val[11] & 31; + + + //'UTC minute 6 72 Char 12 0 - 59; 60 = UTC minute not available = default; + + UTCMinute = val[12]; + + //'UTC second 6 78 Char 13 0 - 59; 60 = UTC second not available = default; + + UTCSecond = val[13]; + + + //'Position accuracy 1 79 Char 14 bit 0 1 = high (< 10 m; Differential Mode of e.g. DGNSS receiver) 0 = low + //'(> 10 m; Autonomous Mode of e.g. GNSS receiver, or of other Electronic + //'Position Fixing Device), default = 0 + + Accuracy = val[14] >> 5; + + //'Long 28 107 char 14 bits 1-5 char 15 16 17 char 18 bitS 0-4 + //'Longitude 28 Longitude in 1/10 000 minute (±180 degrees, East = positive (as per 2´s + //'complement), West = negative(as per 2´s complement); + //'181 degrees (6791AC0 hex) = not available = default) + + + Conv = ((val[14] & 31) << 23) + (val[15] << 17) + (val[16] << 11) + (val[17] << 5) + (val[18] >>1); + + if ((val[14] & 16) == 16) + Conv = -(0x10000000 - Conv); + + V_Lon = Conv; + V_Lon/= 600000; + + //'Latitude 27 Latitude in 1/10 000 minute (±90 degrees, North = positive (as per 2´s + //'complement), South = negative (as per 2´s complement); + //'91 degrees (3412140 hex) = not available = default) + + + //'Lat 27 134 Char 18 bit 5 19 20 21 22 23 bits 0-1 + + Conv = ((val[18] & 1) << 26) + (val[19]<< 20) + (val[20]<< 14) + (val[21]<< 8) + (val[22]<< 2) + (val[23] >>4); + + if ((val[18] & 1) == 1) + Conv = -(0x8000000 - Conv); + + V_Lat = Conv; + V_Lat/= 600000; + + /* + 'Type of Electronic + 'Position Fixing + 'Device + ' 4 138 Char 23 bits 2-5 Use of differential corrections is defined by field "position accuracy" + 'above; + '0 = Undefined (default), + '1 = GPS, + '2 = GLONASS, + '3 = Combined GPS/GLONASS, + + '4 = Loran-C, + '5 = Chayka, + '6 = Integrated Navigation System, + + '7 = surveyed, + '8 - 15 = not used. + */ + + FixType = val[23] & 15; + + /* + 'Spare 10 148 char 24, char 25 bits 0-3 Not used. Should be set to zero. + 'RAIM-Flag 1 149 char 25 bit 4 RAIM (Receiver Autonomous Integrity Monitoring) flag of Electronic + 'Position Fixing Device; 0 = RAIM not in use = default; 1 = RAIM in use) + 'Communication State 19 168 char 25 bit 5, 26 27 28 SOTDMA Communication State as described in § 3.3.7.2.2. + + + 'Sync state 2 0 UTC Direct (refer to § 3.1.1.1). + ' 1 UTC Indirect (refer to § 3.1.1.2). + ' 2 Station is synchronized to a Base station(Base direct) (refer to § 3.1.1.3). + ' 3 Station is synchronized to another station based on the highest + ' number of received stations or to another mobile station, which is + ' directly synchronized to a base station (refer to § 3.1.1.3 and § 3.1.1.4). + 'Slot Time-Out 3 Specifies frames remaining until a new slot is selected. + '0 means that this was the last transmission in this slot. + '1-7 means that 1 to 7 frames respectively are left until slot change. + 'Sub message 14 The sub message depends on the current value in slot time-out as + + */ + SyncState = ((val[25] & 1) << 1) + (val[26] >> 5); + + //' Debug.Print "Sync State "; SyncState; + + SlotTO = (val[26] >>2) & 7; + + //' Debug.Print "Slot TO "; SlotTO; + + SubMsg = ((val[26] & 3) << 12) + (val[27] << 6) + val[28]; + + //'Total number of bits 168 + + ProcessBaseStationMessage(); + return 0; + } + + if (Msg_Type == 5) + { + + //'IMO No 30 70 char 7 4-5, 8 9 10 11, char 12 0-3 + + V_IMO = ((val[7] & 3) << 28) + (val[8]<< 22) + (val[9]<< 16) + (val[10]<< 10) + (val[11]<< 4) + (val[12] >>2); + + //'Callsign 42 112 char 12 4-5, 13 14 15 16 17 18 19 bits 0-3 + + + for (i = 0; i <= 6; i++) + { + Conv = ((val[12 + i] & 3) << 4) + (val[13 + i] >> 2); + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_Callsign[i] = Conv; + } + + //Name 120 232 char 19 bits 4-5 20-38 39 bits 0-3 + + for (i = 0; i <= 19; i++) + { + Conv = ((val[19 + i] & 3) << 4) + (val[20 + i] >> 2); + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_Name[i] = Conv; + } + + //'Type of Ship 8 240 char 39 bits 4-5 40 + + V_Type=((val[39] & 3) << 6) + val[40]; + + + //'Dimensions 30 270 char 41 42 43 44 45 + + //'A Bit 21 – Bit 29 0 – 511 9 + //'B Bit 12 – Bit 20 0 – 511 9 + //'C Bit 6 – Bit 11 0 - 63 ; 6 + //'D Bit 0 – Bit 5 0 - 63 ; 6 + + + V_LenA = (val[41] << 3) + (val[42] >> 3); + V_LenB = ((val[42] & 7) << 6) + val[43]; + V_WidthC = val[44]; + V_WidthD = val[45]; + + // sprintf(TraceMsg,"%s %d %d %d %d %d LenA %d LenB %d\n", V_Name, val[41], val[42], val[43], val[44], val[45], V_LenA, V_LenB); + // OutputDebugString(TraceMsg); + + //' Type of Fix 4 274 char 46 bits 0-3 + + FixType = val[46] >>2; + + //'ETA 20 294 char 46 bits 4-5, 47,48,49 + + ETA =((val[46] & 3) << 18) + (val[47]<< 12) + (val[48]<< 6) + val[49]; + + //'Draught 8 302 char 50, char 51 bits 0-1 + + V_Draught = (val[50] << 2) + (val[51] >>4); + + //'Destination 120 422 char 51 bits 2-5, 52-70 71 bits 0-1 + + for (i = 0; i <= 19; i++) + { + Conv = ((val[51 + i] & 15) << 2) + (val[52 + i] >> 4); + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_Dest[i] = Conv; + } + + //'DTE 1 423 char 71 bit 2 + //'Spare 1 424 char 71 bit 3 + + ProcessAISVesselDetails('A'); + + return 0; + } + + /* + + ' Type 5 + + 'Message ID 6 Char 1 Identifier for this message 1, 2 or 3 + 'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been + ' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. + 'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number + 'AIS Version 2 40 char 7 2-3 + 'IMO No 30 70 char 7 4-5, 8 9 10 11, char 12 0-3 + 'Callsign 42 112 char 12 4-5, 13 14 15 16 17 18 19 bits 0-3 + 'Name 120 232 char 19 bits 4-5 20-38 39 bits 0-3 + 'Type of Ship 8 240 char 39 bits 4-5 40 + + 'Dimensions 30 270 char 41 42 43 44 45 + + 'A Bit 21 – Bit 29 0 – 511 9 + 'B Bit 12 – Bit 20 0 – 511 9 + 'C Bit 6 – Bit 11 0 - 63 ; 6 + 'D Bit 0 – Bit 5 0 - 63 ; 6 + + + + + 'Type of Fix 4 274 char 46 bits 0-3 + + 'ETA 20 294 + 'Draught 8 302 + 'Destination 120 422 + 'DTE 1 423 + 'Spare 1 424 + + ' Total 424 bits = 53 bytes = 70 chars + 4 bits + + + 'A Bit 21 – Bit 29 0 – 511 + 'B Bit 12 – Bit 20 0 – 511 + 'C Bit 6 – Bit 11 0 - 63 ; + 'D Bit 0 – Bit 5 0 - 63 ; + + */ + + + if (Msg_Type == 18) // Class B Position + { + + + /* + Message ID 6 Identifier for Message 18; always 18 + char 1 + + Repeat indicator 2 Used by the repeater to indicate how many times a message has been repeated. 0-3; shall be 0 for Class B “CS” transmissions + char 2 bits 0-1 + + User ID 30 MMSI number + Char 2 2-5, 3, 4, 5, 6, 7 bits 0-1 + + Reserved for regional or local applications 8 Reserved for definition by a competent regional or local authority. Shall be set to zero, if not used for any regional or local application. Regional applications should not use zero + Char 7 bits 2-5, Char 8 bits 1-3 + + SOG 10 Speed over ground in 1/10 knot steps (0-102.2 knots) 1 023 = not available, 1 022 = 102.2 knots or higher + Char 8 bits 4,5, Char 9, Char 10 bits 0-1 + */ + + V_SOG = (((val[8] & 3) <<8) + (val[9] << 2) + (val[10] >>4)); + V_SOG = V_SOG/10; + + + // Position accuracy 1 1 = high (<10 m) 0 = low (>10 m) + // Char 10 bit 2 + + Accuracy = ((val[10] & 8) >> 3); + + //Longitude 28 Longitude in 1/10 000 min (±180°, East = positive (as per 2's complement), West = negative (as per 2's complement), 181° (6791AC0 hex) = not available = default) + // Char 10 bits 3-5, 11, 12, 13, 14, 15 bit 0 + + Conv = ((val[10] & 7) << 25) + (val[11] << 19) + (val[12] << 13) + (val[13] << 7) + (val[14] << 1) + (val[15] >>5); + + if ((val[10] & 4) == 4) + Conv = -(0x10000000 - Conv); + + V_Lon = Conv; + V_Lon/= 600000; + + + //Latitude 27 Latitude in 1/10 000 min (±90°, North = positive (as per 2's complement), South = negative (as per 2's complement), 91° (3412140 hex) = not available = default) + // Char 15 bits 1-5 16 17 18 19 bits 0-3 + + Conv = ((val[15] & 31) << 22) + (val[16]<< 16) + (val[17]<< 10) + (val[18]<< 4) + (val[19] >>2); + + if ((val[15] & 16) == 16) + Conv = -(0x8000000 - Conv); + + + V_Lat = Conv; + V_Lat/= 600000; + + + //COG 12 Course over ground in 1/10° (0-3 599). 3 600 (E10h) = not available = default; 3 601-4 095 shall not be used + // Char 19 bits 4-5 20 21 bit 0-3 + + V_COG = ((val[19] & 3) << 10) + (val[20]<< 4) + (val[21] >>2); + + V_COG /= 10; + + //True heading 9 Degrees (0-359) (511 indicates not available = default) + // char 21 bits 4-5 char 22 char 23 bit 0 + + V_Heading = ((val[21] & 3) << 7) + (val[22]<< 1) + (val[23] >>5); + + //Time stamp 6 UTC second when the report was generated by the EPFS (0-59); 60 if time stamp is not available, which shall also be the default value 61, 62 and 63 are not used by the Class B “CS” AIS + // Char 23 bits 1-5, 24 bit 0 + + + //Reserved for regional applications 2 Reserved for definition by a competent regional authority. Shall be set to zero, if not used for any regional application. Regional applications should not use zero + // 24 bit 1-2 + + + //Class B unit flag 1 0 = Class B SOTDMA unit 1 = Class B “CS” unit + // 24 bit 3 + + //Class B display flag 1 0 = No display available; not capable of displaying Message 12 and 14 1 = Equipped with integrated display displaying Message 12 and 14 + // 24 bit 4 + + //Class B DSC flag 1 0 = Not equipped with DSC function 1 = Equipped with DSC function (dedicated or time-shared) + // 24 bit 5 + + //Class B band flag 1 0 = Capable of operating over the upper 525 kHz band of the marine band 1 = Capable of operating over the whole marine band (irrelevant if “Class B Msg22 flag” is 0) + // 25 bit 0 + + //Class B Message 22 flag 1 0 = No frequency management via Message 22 , operating on AIS1, AIS2 only 1 = Frequency management via Message 22 + // 25 bit 1 + + //Mode flag 1 0 = Station operating in autonomous mode = default 1 = Station operating in assigned mode + // 25 bit 2 + + //RAIM-flag 1 RAIM flag of electronic position fixing device, optional; 0 = RAIM not in use = default; 1 = RAIM in use (valid data for expected position error) + // 25 bit 3 + + //Communication state selector flag 1 1 = ITDMA communication state follows + // 25 bit 4 + + //Communication state 19 ITDMA communication state; refer to § 4.3.3.5 + // 25 bit 5, 26, 27. 28 + + //Total number of bits 168 Occupies one-time period (28 chars) + + ProcessAISVesselMessage(); + + return 0; + + + } + + + + if (Msg_Type == 19) // Class B Extended Position + { + + + /* + Message ID 6 Identifier for Message 18; always 18 + char 1 + + Repeat indicator 2 Used by the repeater to indicate how many times a message has been repeated. 0-3; shall be 0 for Class B “CS” transmissions + char 2 bits 0-1 + + User ID 30 MMSI number + Char 2 2-5, 3, 4, 5, 6, 7 bits 0-1 + + Reserved for regional or local applications 8 Reserved for definition by a competent regional or local authority. Shall be set to zero, if not used for any regional or local application. Regional applications should not use zero + Char 7 bits 2-5, Char 8 bits 1-3 + + SOG 10 Speed over ground in 1/10 knot steps (0-102.2 knots) 1 023 = not available, 1 022 = 102.2 knots or higher + Char 8 bits 4,5, Char 9, Char 10 bits 0-1 + */ + + V_SOG = (((val[8] & 3) <<8) + (val[9] << 2) + (val[10] >>4)); + V_SOG = V_SOG/10; + + + // Position accuracy 1 1 = high (<10 m) 0 = low (>10 m) + // Char 10 bit 2 + + Accuracy = ((val[10] & 8) >> 3); + + //Longitude 28 Longitude in 1/10 000 min (±180°, East = positive (as per 2's complement), West = negative (as per 2's complement), 181° (6791AC0 hex) = not available = default) + // Char 10 bits 3-5, 11, 12, 13, 14, 15 bit 0 + + Conv = ((val[10] & 7) << 25) + (val[11] << 19) + (val[12] << 13) + (val[13] << 7) + (val[14] << 1) + (val[15] >>5); + + if ((val[10] & 4) == 4) + Conv = -(0x10000000 - Conv); + + V_Lon = Conv; + V_Lon/= 600000; + + + //Latitude 27 Latitude in 1/10 000 min (±90°, North = positive (as per 2's complement), South = negative (as per 2's complement), 91° (3412140 hex) = not available = default) + // Char 15 bits 1-5 16 17 18 19 bits 0-3 + + Conv = ((val[15] & 31) << 22) + (val[16]<< 16) + (val[17]<< 10) + (val[18]<< 4) + (val[19] >>2); + + if ((val[15] & 16) == 16) + Conv = -(0x8000000 - Conv); + + + V_Lat = Conv; + V_Lat/= 600000; + + + //COG 12 Course over ground in 1/10° (0-3 599). 3 600 (E10h) = not available = default; 3 601-4 095 shall not be used + // Char 19 bits 4-5 20 21 bit 0-3 + + V_COG = ((val[19] & 3) << 10) + (val[20]<< 4) + (val[21] >>2); + + V_COG /= 10; + + //True heading 9 Degrees (0-359) (511 indicates not available = default) + // char 21 bits 4-5 char 22 char 23 bit 0 + + V_Heading = ((val[21] & 3) << 7) + (val[22]<< 1) + (val[23] >>5); + + //Time stamp 6 UTC second when the report was generated by the EPFS (0-59); 60 if time stamp is not available, which shall also be the default value 61, 62 and 63 are not used by the Class B “CS” AIS + // Char 23 bits 1-5, 24 bit 0 + + + //Reserved for regional applications 4 Reserved for definition by a competent regional authority. Shall be set to zero, if not used for any regional application. Regional applications should not use zero + // 24 bit 1-4 + + + + //Name 120 Maximum 20 characters 6-bit ASCII, @@@@@@@@@@@@@@@@@@@@ = not available = default + // 24 bit 5 - 44 bits 0-4 + + + for (i = 0; i <= 19; i++) + { + Conv = (((val[24 + i]) & 1) << 5) + (val[25 + i] >> 1); + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_Name[i] = Conv; + } + + + + //Type of ship and cargo type 8 0 = not available or no ship = default + // 1-99 = as defined in § 3.3.8.2.3.2 + // 100-199 = preserved, for regional use + // 200-255 = preserved, for future use + // 44 bit 5 45 46 bit 0 + + V_Type=((val[44] & 1) << 5) + (val[45] << 1) + (val[46] >> 5); + + //Dimension of ship/reference for position 30 Dimensions of ship in metres and reference point for reported position (see Fig. 17 and § 3.3.8.2.3.3) + // 46 bit 1-5 47 48 49 50 51 bit 0 + // 46 bit 1-5, 47 bits 0-3 - 47 bits 4,5 48, 49 bit bit 0 - 49 bits 1-5, 50 bit 0 - 50 1-5, 51 bit 0 + + V_LenA = ((val[46] & 31) << 5) + (val[47] >> 2); + V_LenB = ((val[47] & 3) << 7) + (val[48] << 1) + (val[49] >> 5); + V_WidthC = ((val[49] & 31) << 1) + (val[50] >> 5); + V_WidthD = ((val[50] & 31) << 1) + (val[51] >> 5); + + + //Type of electronic position fixing device 4 0 = Undefined (default); 1 = GPS, 2 = GLONASS, 3 = combined GPS/GLONASS, 4 = Loran-C, 5 = Chayka, 6 = integrated navigation system, 7 = surveyed; 8-15 = not used + // 51 bits 1-4 + + //RAIM-flag 1 RAIM flag of electronic position fixing device; 0 = RAIM not in use = default; 1 = RAIM in use + // 51 bit 5 + + //DTE 1 Data terminal ready (0 = available 1 = not available = default) (see § 3.3.8.2.3.1) + // 52 bit 0 + //Spare 5 Not used. Should be set to zero + // 52 bits 1-5 + // Total number of bits 312 (52 chars) Occupies two slots + + ProcessAISVesselMessage(); + ProcessAISVesselDetails('B'); + + return 0; + + + } + + + // Navaid + + if (Msg_Type == 21) + { + + //'Message ID 6 Char 1 Identifier for this message 21 + //'Repeat Indicator 2 8 Char 2 bits 0-1 Used by the repeater to indicate how many times a message has been + //' repeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. + //'User ID 30 38 Char 2 bits 2-5, Chars 3 4 5 6 Char 7 bits 0-1 MMSI number + + + //'Type of Aids-to- Navigation 5 43 Char 7 bits 2-5 Char 8 bit 0 0 = not available = default; 1 – 15 = Fixed Aid-to-Navigation; 16 - 31 = + //' Floating Aid-to-Navigation; refer to appropriate definition set up by IALA; + //' refer to Table 34bis. + + NavAidType = ((val[7] & 15) << 1) + (val[8] >>5); + + //'Name of Aid s-to-Navigation + //' 120 Maximum 20 characters 6 bit ASCII, + //' "@@@@@@@@@@@@@@@@@@@@" = not available = default. + //' Navigation "@@@@@@@@@@@@@@@@@@@@" = not available = default. + //'The name of the Aid-to-Navigation may be extended by the parameter + //'"Name of Aid-to-Navigation Extension" below. + + //'Name 120 163 char 8 bits 1-5 9-27 28 bits 0 + + for (i = 0; i <= 19; i++) + { + Conv = ((val[8 + i] & 31) << 1) + (val[9 + i] >> 5); + + if (Conv < 32) Conv = Conv + 64; + + V_Name[i] = Conv; + } + + V_Name[20]=0; + + //'Position accuracy 1 164 28 bit 1 1 = high (< 10 m; Differential Mode of e.g. DGNSS receiver) 0 = low + + //'(> 10 m; Autonomous Mode of e.g. GNSS receiver or of other Electronic + //'Position Fixing Device) ; Default = 0 + + Accuracy = ((val[28] & 16) >> 4); + + + //'Long 28 192 char 28 bits 2-5 char 29 30 31 32 + //'Longitude 28 Longitude in 1/10 000 minute (±180 degrees, East = positive (as per 2´s + //'complement), West = negative(as per 2´s complement); + //'181 degrees (6791AC0 hex) = not available = default) + + Conv = ((val[28] & 15) << 24) + (val[29] << 18) + (val[30] << 12) + (val[31] << 6) + val[32]; + + if ((val[28] & 8) == 8) + Conv = -(0x10000000 - Conv); + + V_Lon = Conv; + V_Lon/= 600000; + + //'Latitude 27 Latitude in 1/10 000 minute (±90 degrees, North = positive (as per 2´s + //'complement), South = negative (as per 2´s complement); + //'91 degrees (3412140 hex) = not available = default) + + //'Lat 27 219 Char 33 34 35 36 37 bits 0-2 + + Conv = (val[33] << 21) + (val[34]<< 15) + (val[35]<< 9) + (val[36]<< 3) + (val[37] >> 3); + + if ((val[33] & 32) == 32) + Conv = -(0x8000000 - Conv); + + V_Lat = Conv; + V_Lat/= 600000; + + + + //'Dimension/Reference for Position 30 249 37 bits 3-5 38 39 40 41 42 bits 0-2 Reference point for reported position; also indicates the dimension of an + //'Aid-to-Navigation in metres (see Fig. 18 and § 3.3.8.2.3.3), if relevant. (1) + //'Type of Electronic + + //'A Bit 21 – Bit 29 0 – 511 9 + //'B Bit 12 – Bit 20 0 – 511 9 + //'C Bit 6 – Bit 11 0 - 63 ; 6 + //'D Bit 0 – Bit 5 0 - 63 ; 6 + + + V_LenA = ((val[37] & 7) << 3) + val[38]; + + V_LenB = (val[39] << 6) + (val[40] >> 3); + + V_WidthC = ((val[40] & 7) << 3) + (val[41] >> 3); + + V_WidthD = ((val[41] & 7) << 3) + (val[42] >> 3); + + /* + + 'Position Fixing Device 4 253 42 bits 3-5 43 bit 0 + '0 = Undefined (default); + '1 = GPS, + '2 = GLONASS, + '3 = Combined GPS/GLONASS, + '4 = Loran-C, + '5 = Chayka, + '6 = Integrated Navigation System, + '7 = surveyed.For fixed AtoNs and virtual AtoNs, the surveyed position + 'should be used. The accurate position enhances its function as a radar + 'reference target. + '8 – 15 = not used. + + */ + FixType = ((val[42] & 7) << 1) + (val[43] >> 5); + + /* + 'Time Stamp 6 259 43 bit 1-5 44 bit 0 UTC second when the report was generated by the EPFS (0 –59, + + 'or 60 if time stamp is not available, which should also be the default value, + 'or 61 if positioning system is in manual input mode, + 'or 62 if Ele ctronic Position Fixing System operates in estimated (dead reckoning) mode, + 'or 63 if the positioning system is inoperative) + + + 'Off-Position Indicator 1 260 44 bit 1 For floating Aids-to-Navigation, only: 0 = on position; 1 = off position; + 'NOTE – This flag should only be considered valid by receiving station, if + 'the Aid -to-Navigation is a floating aid, and if Time Stamp is equal to or + 'below 59. For floating AtoN the guard zone parameters should be set on installation. + + 'Reserved for regional or local application + + ' 8 268 44 bits 2-5 45 bit 0-3 + + 'Reserved for definition by a competent regional or local authority. Should + 'be set to zero, if not used for any regional or local application. Regional + 'applications should not use zero. + + + 'RAIM-Flag 1 269 45 bit 4 RAIM (Receiver Autonomous Integrity Monitoring) flag of Electronic + 'Position Fixing Device; 0 = RAIM not in use = default; 1 = RAIM in use) + + 'Virtual AtoN Flag 1 270 45 bit 5 0 = default = real A to N at indicated position; 1 = virtual AtoN, does not + 'physically exist, may only be transmitted from an AIS station nearby under + 'the direction of a competent authority. (2) + + + 'Assigned Mode Flag 1 271 46 bit 0 0 = Station operating in autonomous and continuous mode =default + + '1 = Station operating in assigned mode + + 'Spare 1 272 46 bit 1 Spare. Not used. Should be set to zero. + + 'Name of Aid-to-Navigation Extension + '0, 6, 12, + '18, 24, + '30, 36, + 'This parameter of up to 14 additional 6-bit-ASCII characters for a 2-slot + 'message may be combined with the parameter "Name of Aid-to- + 'Navigation " at the end of that parameter, when more than 20 characters are" + '30, 36, + '... 84 + 'Navigation " at the end of that parameter, when more than 20 characters are" + 'needed for the Name of the Aid-to-Navigation. This parameter should be + 'omitted when no more than 20 characters for the name of the A-to-N are + 'needed in total. Only the required number of characters should be + 'transmitted, i. e. no @-character should be used. + 'Spare 0, 2, 4, + 'or 6 + 'Spare. Used only when parameter "Name of Aid-to-Navigation Extension" + 'is used. Should be set to zero. The number of spare bits should be adjusted + 'in order to observe byte boundaries. + 'Number of bits 272 – 360 + 'Occupies two slots. + */ + + + ProcessAISNavAidMessage(); + + return 0; + } + + + if (Msg_Type == 24) // Class B Details + { + //Message ID 6 Identifier for Message 24; always 24 + //Repeat indicator 2 Used by the repeater to indicate how many times a message has been repeated. 0 = default; 3 = do not repeat any more + //User ID 30 MMSI number + // char 2 bits 2-5, 3 4, 5, 6, 7 bits 0,1 + + //Part number 2 Identifier for the message part number; always 0 for Part A + // char 7 bits 2,3 + + B_PartNo=(val[7] & 15) >> 2; + + + if (B_PartNo == 0) + { + + //Name 120 Name of the MMSI-registered vessel. Maximum 20 characters 6-bit ASCII, @@@@@@@@@@@@@@@@@@@@ = not available = default + // char 7 bits 4,5 - char 26 bits 0-3 + + for (i = 0; i <= 19; i++) + { + Conv = (((val[7 + i]) & 3) << 4) + (val[8 + i] >> 2); + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_Name[i] = Conv; + } + + + //Total number of bits 160 = 26 chars 4 bits Occupies one-time period + + ProcessAISVesselDetails('0'); + + return 0; + } + + if (B_PartNo == 1) + { + //Type of ship and cargo type 8 0 = not available or no ship = default + // 1-99 = as defined in § 3.3.8.2.3.2 of Annex 2 + // 100-199 = preserved, for regional use + // 200-255 = preserved, for future use + // char 7 bits 4-5, char 8 + + V_Type=((val[7] & 3) << 2) + val[8]; + + //Vendor ID 42 Unique identification of the Unit by a number as defined by the manufacturer (option; “@@@@@@@“ = not available = default)// char 9-15 + + for (i = 0; i <= 6; i++) + { + Conv = val[i+9]; + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_VendorID[i] = Conv; + } + + + //Call sign 42 Call sign of the MMSI-registered vessel. 7 X 6-bit ASCII characters, “@@@@@@@“ = not available = default + // char 16-22 + + for (i = 0; i <= 6; i++) + { + Conv = val[i+16]; + + if (Conv == 0) Conv = 32; else if (Conv < 32) Conv = Conv + 64; + + V_Callsign[i] = Conv; + } + + //Dimension of ship/reference for position. Or, for unregistered daughter vessels, use the MMSI of the mother ship + // 30 Dimensions of ship in meters and reference point for reported position (see Annex 2 Fig. 17 and § 3.3.8.2.3.3). Or, for an unregistered daughter vessel, use the MMSI of the associated mother ship in this data field + // char 23-27 + + V_LenA = (val[23] << 3) + (val[24] >> 3); + V_LenB = ((val[24] & 7) << 3) + val[25]; + V_WidthC = val[26]; + V_WidthD = val[27]; + + + // Spare 6 + // Total number of bits 168 (28 chars) Occupies one-time period + + + ProcessAISVesselDetails('1'); + + return 0; + } + } + + if (Msg_Type == 9) + { + // Msg_Type = val[1]; + //Message ID 6 Identifier for message 9; always 9 + //Repeat Indicator 2 Used by the repeater to indicate how many times a message has beenrepeated. Refer to § 4.6.1; 0 - 3; default = 0; 3 = do not repeat any more. + + // char 2 bits 2-5, 3 4, 5, 6, 7 bits 0,1 + // UserID = ((val[2] & 15) << 26) + (val[3] << 20)+ (val[4] << 14)+ (val[5] << 8)+ (val[6] << 2) + (val[7] >>4); ; + + //Part number 2 Identifier for the message part number; always 0 for Part A + // char 7 bits 2,3 + + //Altitude (GNSS) 12 Altitude 7 bits 2-5 8 9 bits 0, 1 + + Alt = ((val[7] & 15) << 8) + (val[8] << 2) + (val[9] >> 4); + + //'SOG 10 60 Char 9 bits 2-5 char 10 Speed over ground in knot steps (0-1022 knots) + + V_SOG = (((val[9] & 15) <<6) + val[10]); + + + //Position accuracy 1 Char 11 bit 0 1 = high (< 10 m; Differential Mode of e.g. DGNSS receiver) 0 = low(> 10 m; Autonomous Mode of e.g. GNSS receiver or of other ElectronicPosition Fixing Device) ; default = 0 + + + //Longitude 28 Longitude in 1/10 000 min (±180°, East = positive (as per 2's complement), West = negative (as per 2's complement), 181° (6791AC0 hex) = not available = default) + // Char 11 bits 1-5, 12, 13, 14, 15 0-4 + + Conv = ((val[11] & 31) << 23) + (val[12] << 17) + (val[13] << 11) + (val[14] << 5) + (val[15] >> 1); + + if ((val[11] & 16) == 16) + Conv = -(0x10000000 - Conv); + + V_Lon = Conv; + V_Lon/= 600000; + + + //Latitude 27 Latitude in 1/10 000 min (±90°, North = positive (as per 2's complement), South = negative (as per 2's complement), 91° (3412140 hex) = not available = default) + // Char 15 bit 5 16 17 18 19 20 bits 0-1 + + Conv = ((val[15] & 1) << 26) + (val[16]<< 20) + (val[17]<< 14) + (val[18]<< 8) + (val[19]<< 2) + (val[20] >> 4); + + if ((val[15] & 1) == 1) + Conv = -(0x8000000 - Conv); + + + V_Lat = Conv; + V_Lat/= 600000; + + //COG 12 128 char 20 bits 2-5 21 22 bit 0-1 + + V_COG = ((val[20] & 15) << 8) + (val[21]<< 2) + (val[22] >>4); + + V_COG /= 10; + + //Time stamp 6 UTC second when the report was generated by the EPFS (0-59,or 60 if time stamp is not available, which should also be the defaultvalue,or 62 if Electronic Position Fixing System operates in estimated (deadreckoning) mode,or 61 if positioning system is in manual input modeor 63 if the positioning system is inoperative) + //Reserved forregionalapplications 8 Reserved for definition by a competent regional authority. Should be setto zero, if not used for any regional application. Regional applicationsshould not use zero. + //DTE 1 Data terminal ready (0 = available 1 = not available = default) (refer to§ 3.3.8.2.3.1). + //Spare 5 3 Not used. Should be set to zero + //Assigned ModeFlag1 0 = Station operating in autonomous and continous mode =default1 = Station operating in assigned mode + //RAIM-Flag 1 RAIM (Receiver Autonomous Integrity Monitoring) flag of ElectronicPosition Fixing Device; 0 = RAIM not in use = default; 1 = RAIM in use)Communication + //State SelectorFlag1 0 = SOTDMA Communication State follows;1 = ITDMA Communication State follows. + //Communication State 19 SOTDMA (refer to § 3.3.7.2.2). + //Total number of bits168 + + ProcessSARMessage(); + + } + + return Msg_Type; + + + /* + '@ 0 0x00 00 0000 64 0x40 0100 0000 ! 33 0x21 10 0001 33 0x21 0010 0001 + 'A 1 0x01 00 0001 65 0x41 0100 0001 " 34 0x22 10 0010 34 0x22 0010 0010 + 'B 2 0x02 00 0010 66 0x42 0100 0010 # 35 0x23 10 0011 35 0x23 0010 0011 + 'C 3 0x03 00 0011 67 0x43 0100 0011 $ 36 0x24 10 0100 36 0x24 0010 0100 + 'D 4 0x04 00 0100 68 0x44 0100 0100 % 37 0x25 10 0101 37 0x25 0010 0101 + 'E 5 0x05 00 0101 69 0x45 0100 0101 & 38 0x26 10 0110 38 0x26 0010 0110 + 'F 6 0x06 00 0110 70 0x46 0100 0110 ` 39 0x27 10 0111 39 0x27 0010 0111 + 'G 7 0x07 00 0111 71 0x47 0100 0111 ( 40 0x28 10 1000 40 0x28 0010 1000 + 'H 8 0x08 00 1000 72 0x48 0100 1000 ) 41 0x29 10 1001 41 0x29 0010 1001 + 'I 9 0x09 00 1001 73 0x49 0100 1001 * 42 0x2A 10 1010 42 0x2A 0010 1010 + 'J 10 0x0A 00 1010 74 0x4A 0100 1010 + 43 0x2B 10 1011 43 0x2B 0010 1011 + 'K 11 0x0B 00 1011 75 0x4B 0100 1011 ´ 44 0x2C 10 1100 44 0x2C 0010 1100 + 'L 12 0x0C 00 1100 76 0x4C 0100 1100 - 45 0x2D 10 1101 45 0x2D 0010 1101 + 'M 13 0x0D 00 1101 77 0x4D 0100 1101 , 46 0x2E 10 1110 46 0x2E 0010 1110 + 'N 14 0x0E 00 1110 78 0x4E 0100 1110 / 47 0x2F 10 1111 47 0x2F 0010 1111 + 'O 15 0x0F 00 1111 79 0x4F 0100 1111 0 48 0x30 11 0000 48 0x30 0011 0000 + 'P 16 0x10 01 0000 80 0x50 0101 0000 1 49 0x31 11 0001 49 0x31 0011 0001 + 'Q 17 0x11 01 0001 81 0x51 0101 0001 2 50 0x32 11 0010 50 0x32 0011 0010 + 'R 18 0x12 01 0010 82 0x52 0101 0010 3 51 0x33 11 0011 51 0x33 0011 0011 + 'S 19 0x13 01 0011 83 0x53 0101 0011 4 52 0x34 11 0100 52 0x34 0011 0100 + 'T 20 0x14 01 0100 84 0x54 0101 0100 5 53 0x35 11 0101 53 0x35 0011 0101 + 'U 21 0x15 01 0101 85 0x55 0101 0101 6 54 0x36 11 0110 54 0x36 0011 0110 + 'V 22 0x16 01 0110 86 0x56 0101 0110 7 55 0x37 11 0111 55 0x37 0011 0111 + 'W 23 0x17 01 0111 87 0x57 0101 0111 8 56 0x38 11 1000 56 0x38 0011 1000 + 'X 24 0x18 01 1000 88 0x58 0101 1000 9 57 0x39 11 1001 57 0x39 0011 1001 + 'Y 25 0x19 01 1001 89 0x59 0101 1001 : 58 0x3A 11 1010 58 0x3A 0011 1010 + 'Z 26 0x1A 01 1010 90 0x5A 0101 1010 ; 59 0x3B 11 1011 59 0x3B 0011 1011 + '[ 27 0x1B 01 1011 91 0x5B 0101 1011 < 60 0x3C 11 1100 60 0x3C 0011 1100 + '\ 28 0x1C 01 1100 92 0x5C 0101 1100 = 61 0x3D 11 1101 61 0x3D 0011 1101 + '] 29 0x1D 01 1101 93 0x5D 0101 1101 > 62 0x3E 11 1110 62 0x3E 0011 1110 + '^ 30 0x1E 01 1110 94 0x5E 0101 1110 ? 63 0x3F 11 1111 63 0x3F 0011 1111 + '_ 31 0x1F 01 1111 95 0x5F 0101 1111 + '[SP] 32 0x20 10 0000 32 0x20 0010 0000 + + */ + + return Msg_Type; + +} + + + +int ConvertAsciito6Bit(char * msg, char * buffer, int Len) +{ + int i; + + for (i = 0; i< Len; i++) + { + buffer[i]=conv[msg[i]]; + } + + return 0; +} + +void ProcessSARMessage() +{ + int i; + struct SARRECORD * ptr; + + for (i = 0; i < SARCount; i++) + { + ptr=SARRecords[i]; + + if (ptr->ID == UserID) + { + if (V_Lat < 50 || V_Lat > 70) + return; + + ptr->lat = V_Lat; + ptr->Long = V_Lon; + ptr->course = V_COG; + ptr->speed = V_SOG; + ptr->Alt = Alt; + + ptr->TimeLastUpdated = NOW; + + SaveSARTrackPoint(ptr); + return; + } + } + + if (SARCount == 0) + SARRecords = (struct SARRECORD **)malloc(sizeof(void *)); + else + SARRecords = (struct SARRECORD **)realloc(SARRecords,(SARCount+1)*sizeof(void *)); + + ptr = (struct SARRECORD *)malloc(sizeof(struct SARRECORD)); + + if (ptr == NULL) return; + + memset(ptr, 0, sizeof(struct SARRECORD)); + + SARRecords[SARCount] = ptr; + + ptr->ID = UserID; + ptr->TimeAdded = NOW; + + SARCount++; + + LookupVessel(UserID); + + if (ShipDataRec != NULL) + { + memcpy(ptr->name, ShipDataRec->name, 21); + } + + ProcessSARMessage(); + + return; +} + + + +void ProcessAISVesselMessage() +{ + int i; + struct TARGETRECORD * ptr; + int Length; + int Dimensions[10]; + + if (V_Lat > 90.0) + return; + + for (i = 0; i < TargetCount; i++) + { + ptr=TargetRecords[i]; + + if (ptr->ID == UserID) + { + ptr->lat = V_Lat; + ptr->Long = V_Lon; + ptr->course = V_COG; + if (V_COG == 360) ptr->course = V_Heading; //' 360 means not available + ptr->speed = V_SOG; + ptr->NavStatus = NavStatus; + ptr->ROT = V_ROT; + ptr->Heading = V_Heading; + + ptr->TimeLastUpdated = NOW; + + SaveTrackPoint(ptr); + + return; + + } + } + +// Not found - add on end + + if (TargetCount == 0) + + TargetRecords=(struct TARGETRECORD **)malloc(sizeof(void *)); + else + TargetRecords=(struct TARGETRECORD **)realloc(TargetRecords,(TargetCount+1)*sizeof(void *)); + + ptr=(struct TARGETRECORD *)malloc(sizeof(struct TARGETRECORD)); + memset(ptr, 0, sizeof(struct TARGETRECORD)); + + if (ptr == NULL) return; + + TargetRecords[TargetCount]=ptr; + + ptr->ID = UserID; + ptr->TimeAdded = NOW; + + TargetCount++; + + // See if vessel is in database - if not, add + + LookupVessel(UserID); // Find Vessel - if not present, add it + + if (ShipDataRec != NULL) // Malloc failed!! + { + memcpy(ptr->name,ShipDataRec->name, 21); + memcpy(ptr->Callsign, ShipDataRec->Callsign, 8); + ptr->Type = ShipDataRec->Type; + ptr->LengthA = ShipDataRec->LengthA; + ptr->LengthB = ShipDataRec->LengthB; + ptr->WidthC = ShipDataRec->WidthC; + ptr->WidthD = ShipDataRec->WidthD; + + V_LenA = ShipDataRec->LengthA; + V_LenB = ShipDataRec->LengthB; + V_WidthC = ShipDataRec->WidthC; + V_WidthD = ShipDataRec->WidthD; + + Length = V_LenA + V_LenB; + + if(Length > 0) + { + Dimensions[0] = -V_WidthC; + Dimensions[1] = -V_LenB; + Dimensions[2] = V_WidthD; + Dimensions[3] = -V_LenB; + Dimensions[4] = V_WidthD; + Dimensions[5] = V_LenA - Length / 5; + Dimensions[6] = ((V_WidthC + V_WidthD) / 2) - V_WidthC; + Dimensions[7] = V_LenA; + Dimensions[8] = -V_WidthC; + Dimensions[9] = V_LenA - Length / 5; + } + } + + ProcessAISVesselMessage(); + + return; +} + +void ProcessBaseStationMessage() +{ + int i; + struct BASESTATIONRECORD * ptr; + + for (i = 0; i < BaseStationCount; i++) + { + ptr=BaseStationRecords[i]; + + if (ptr->ID == UserID) + { + ptr->lat = V_Lat; + ptr->Long = V_Lon; + ptr->FixType = FixType; + + if (SlotTO == 3 || SlotTO == 5 || SlotTO == 7) + ptr->NumberofStations = SubMsg; + + ptr->TimeLastUpdated = NOW; + + return; + + } + + } + + if (BaseStationCount == 0) + + BaseStationRecords=(struct BASESTATIONRECORD **)malloc(sizeof(void *)); + else + BaseStationRecords=(struct BASESTATIONRECORD **)realloc(BaseStationRecords,(BaseStationCount+1)*sizeof(void *)); + + ptr=(struct BASESTATIONRECORD *)malloc(sizeof(struct BASESTATIONRECORD)); + + if (ptr == NULL) return; + memset(ptr, 0, sizeof(struct BASESTATIONRECORD)); + + BaseStationRecords[BaseStationCount]=ptr; + + ptr->ID = UserID; + ptr->TimeAdded = NOW; + + BaseStationCount++; + + ProcessBaseStationMessage(); + + return; +} + + +void ProcessAISVesselDetails(char Type) +{ + int i; + struct TARGETRECORD * ptr; + int Dimensions[10]; + int Length; + + for (i = 0; i < TargetCount; i++) + { + ptr=TargetRecords[i]; + + if (ptr->ID == UserID) + { + if (Type == 'A') + { + memcpy(ptr->name, V_Name, 21); + ptr->IMO = V_IMO; + memcpy(ptr->Callsign,V_Callsign,8); + memcpy(ptr->Dest, V_Dest, 21); + ptr->Type = V_Type; + ptr->LengthA = V_LenA; + ptr->LengthB = V_LenB; + ptr->WidthC = V_WidthC; + ptr->WidthD = V_WidthD; + ptr->Class='A'; + } + + if (Type == 'B') // Type 19 Class B Details + { + memcpy(ptr->name, V_Name, 21); + ptr->Type = V_Type; + ptr->LengthA = V_LenA; + ptr->LengthB = V_LenB; + ptr->WidthC = V_WidthC; + ptr->WidthD = V_WidthD; + } + + if (Type == '0') // Class B record 24A + { + memcpy(ptr->name, V_Name, 21); + } + + if (Type == '1') // Class B record 24B + { + memcpy(ptr->Callsign,V_Callsign,8); + ptr->Type = V_Type; + ptr->LengthA = V_LenA; + ptr->LengthB = V_LenB; + ptr->WidthC = V_WidthC; + ptr->WidthD = V_WidthD; + } + + ptr->TimeLastUpdated = NOW; + + Length = V_LenA + V_LenB; + + if(Length > 0) + { + Dimensions[0] = -V_WidthC; + Dimensions[1] = -V_LenB; + Dimensions[2] = V_WidthD; + Dimensions[3] = -V_LenB; + Dimensions[4] = V_WidthD; + Dimensions[5] = V_LenA - Length / 5; + Dimensions[6] = ((V_WidthC + V_WidthD) / 2) - V_WidthC; + Dimensions[7] = V_LenA; + Dimensions[8] = -V_WidthC; + Dimensions[9] = V_LenA - Length / 5; + } + + MaintainVesselDatabase(UserID, Type); + + return; + } + } + +// Not found + + if (TargetCount == 0) + TargetRecords=(struct TARGETRECORD **)malloc(sizeof(void *)); + else + TargetRecords=(struct TARGETRECORD **)realloc(TargetRecords,(TargetCount+1)*sizeof(void *)); + + ptr=(struct TARGETRECORD *)malloc(sizeof(struct TARGETRECORD)); + memset(ptr, 0, sizeof(struct TARGETRECORD)); + + if (ptr == NULL) return; + + TargetRecords[TargetCount]=ptr; + + ptr->ID = UserID; + + if (Type == 'A') ptr->Class='A'; else ptr->Class='B'; + + ptr->TimeAdded = NOW; + + TargetCount++; + + sprintf(ptr->name,"%d",UserID); + + memcpy(ptr->name, V_Name,21); + + ProcessAISVesselDetails(Type); + + return; +} + + +void ProcessAISNavAidMessage() +{ + int i; + struct NAVAIDRECORD * navptr; + + for (i = 0; i < NavAidCount; i++) + { + navptr=NavRecords[i]; + + if (navptr->ID == UserID) + { + memcpy(navptr->name,V_Name,21); + navptr->lat = V_Lat; + navptr->Long = V_Lon; + navptr->LengthA = V_LenA; + navptr->LengthB = V_LenB; + navptr->WidthC = V_WidthC; + navptr->WidthD = V_WidthD; + + navptr->NavAidType = NavAidType; + navptr->FixType = FixType; + + navptr->TimeLastUpdated = NOW; + NavAidDBChanged = 1; + + return; + } + } + + // Not found, so add + + if (NavAidCount == 0) + NavRecords=(struct NAVAIDRECORD **)malloc(sizeof(void *)); + else + NavRecords=(struct NAVAIDRECORD **)realloc(NavRecords,(NavAidCount+1)*sizeof(void *)); + + navptr=(struct NAVAIDRECORD *)malloc(sizeof(struct NAVAIDRECORD)); + + if (navptr == NULL) return; + + NavRecords[NavAidCount]=navptr; + + navptr->ID = UserID; + navptr->TimeAdded = NOW; + + NavAidCount++; + + ProcessAISNavAidMessage(); + + return; + +} + +static double Distance(double lah, double loh, double laa, double loa) +{ + + //' Our Lat, Long other Lat,Long + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians +*/ + +loh = radians(loh); lah = radians(lah); +loa = radians(loa); laa = radians(laa); + +return 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))); + +} + +static double Bearing(double lat1, double lon1, double lat2, double lon2) +{ + +//' Our Lat, Long other Lat,Long + + double dlat, dlon, TC1; + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; + +} + +void AISTimer() +{ + // Entered every minute + + NOW = time(NULL); + + CheckAgeofTargets(MaxAge); + + if (VessselDBChanged) + { + VessselDBChanged = 0; + SaveVesselDataBase(); + } + + if (NavAidDBChanged) + { + NavAidDBChanged = 0; + SaveNavAidDataBase(); + } +} + +void ADSBTimer() +{ + // Entered every minute + + NOW = time(NULL); + CheckAgeofPlanes(300); + ProcessADSBJSON(ADSBFN); + +// WriteMiniDump(); + + if (ADSBHost[0]) + { + if (ADSBConnected == 0) // Try to connect again + _beginthread(ADSBConnect, 0, 0); + } +} + +void CheckAgeofTargets(UINT MaxAge) +{ + int i, j=0; + struct TARGETRECORD * ptr; + + for (i = 0; i < TargetCount; i++) + { + ptr=TargetRecords[i]; + + if (NOW - ptr->TimeLastUpdated > MaxAge) + { + free(ptr); // Release Memory Block + + // Delete Current, and move all others down + + TargetCount--; + + while (i < TargetCount) + { + TargetRecords[i] = TargetRecords[i+1]; + i++; + } + + return; // Test rest on next pass + } + } +} + + + +void CheckAgeofPlanes(UINT MaxAge) +{ + int i, j=0; + struct ADSBRECORD * ptr; + + for (i = 0; i < ADSBCount; i++) + { + ptr = ADSBRecords[i]; + + if (ptr->hex[0] && NOW - ptr->seen_pos > MaxAge) + memset(ptr, 0, sizeof(struct ADSBRECORD)); // Zap the record // Release Memory Block + } +} + +int GetAISPageInfo(char * Buffer, int ais, int adsb) +{ + int i; + int Len = 0; + struct NAVAIDRECORD * navptr; + struct TARGETRECORD * ptr; + char Line[280]; + int Age; + char * pType; + char * Name; + char nameString[32]; + struct tm * TM; + char TimeAdded[50]; + char TimeHeard[50]; + char * p; + char DestBuffer[128] = ""; + + NOW = time(NULL); + + if (adsb) + ProcessADSBJSON(ADSBFN); + + if (Lat != 0.0 && Lon != 0) + Len += sprintf(&Buffer[Len],"H,%f,%f,\r\n|", Lat, Lon); + + if (ais) + { + for (i = 0; i < NavAidCount; i++) + { + navptr=NavRecords[i]; + + if (strchr(navptr->name, '>') || strchr(navptr->name, '<')) + continue; + + TM = gmtime(&navptr->TimeAdded); + + sprintf(TimeAdded, "
Created %02d:%02d %02d/%02d/%04d", + TM->tm_hour, TM->tm_min, TM->tm_mday, TM->tm_mon + 1, TM->tm_year + 1900); + + TM = gmtime(&navptr->TimeLastUpdated); + + sprintf(TimeHeard, "
Last Heard %02d:%02d %02d/%02d/%04d", + TM->tm_hour, TM->tm_min, TM->tm_mday, TM->tm_mon + 1, TM->tm_year + 1900); + + Len += sprintf(&Buffer[Len],"N,%f,%f,%s%s%s,\r\n|", + navptr->lat, navptr->Long, navptr->name, TimeAdded, TimeHeard); + } + + // Vessels + + for (i = 0; i < TargetCount; i++) + { + ptr = TargetRecords[i]; + + Age = NOW - ptr->TimeLastUpdated; + + if (Age > 3600) + continue; + + if (ptr->Type >= 30 && ptr->Type < 40) + pType=VesselType3[ptr->Type-30]; + else + if (ptr->Type >= 50 && ptr->Type < 60) + pType=VesselType5[ptr->Type-50]; + else + pType=VesselType[ptr->Type/10]; + + if (ptr->name[0]) + Name = ptr->name; + else + { + sprintf(nameString, "%d", ptr->ID); + Name = nameString; + } + + // Dest may contain comma + + strcpy(DestBuffer, ptr->Dest); + + while (p = strchr(DestBuffer, ',')) + { + memmove(p + 4, p, strlen(p)); + memcpy(p, ",", 5); + } + + sprintf(Line,"%s
Dest %s
MMSI %d
Callsign %s
Type %d
Heading %2.0f° ROT %2.1f
Dimensions %d %d %d %d
%2.1f kts %2.0f°
Age %d", + Name, DestBuffer, ptr->ID, ptr->Callsign, ptr->Type , ptr->Heading, ptr->ROT, + ptr->LengthA, ptr->LengthB, ptr->WidthC, ptr->WidthD, + ptr->speed, ptr->course, Age); + + Len += sprintf(&Buffer[Len],"V,%.4f,%.4f,%s,%.0f,%.1f,%d\r\n|", + ptr->lat, ptr->Long, Line, ptr->course, ptr->speed, Age); + + if (ptr->TrackTime[0]) // Have trackpoints + { + int n = ptr->Trackptr; + int i; + + // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros + + Len += sprintf(&Buffer[Len],"T,"); + + for (i = 0; i < TRACKPOINTS; i++) + { + if (ptr->TrackTime[n]) + { + Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); + } + + n++; + if (n == TRACKPOINTS) + n = 0; + } + Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->lat, ptr->Long); //Add current position to end of track + } + } + + // SAR Vessels + + for (i = 0; i < SARCount; i++) + { + struct SARRECORD * ptr = SARRecords[i]; + + Age = NOW - ptr->TimeLastUpdated; + + if (Age > 3600) + continue; + + if (ptr->name[0]) + Name = ptr->name; + else + { + sprintf(nameString, "%d", ptr->ID); + Name = nameString; + } + + sprintf(Line,"%s
Dest %s
MMSI %d
%2.1f kts %2.0f°
Age %d", + Name, ptr->Dest, ptr->ID, ptr->speed, ptr->course, Age); + + Len += sprintf(&Buffer[Len],"S,%.4f,%.4f,%s,%.0f,%.0f,%d\r\n|", + ptr->lat, ptr->Long, Line, ptr->course, ptr->speed, Age); + + if (ptr->TrackTime[0]) // Have trackpoints + { + int n = ptr->Trackptr; + int i; + + // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros + + Len += sprintf(&Buffer[Len],"T,"); + + for (i = 0; i < TRACKPOINTS; i++) + { + if (ptr->TrackTime[n]) + { + Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); + } + + n++; + if (n == TRACKPOINTS) + n = 0; + } + Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->lat, ptr->Long); //Add current position to end of track + } + } + + } + + if (adsb) + { + // Aircraft + + + for (i = 0; i < ADSBCount; i++) + { + struct ADSBRECORD * ptr = ADSBRecords[i]; + + Age = NOW - ptr->seen_pos; + + if (Age > 600) + continue; + + if (ptr->lat == 0.0 && ptr->lon == 0.0) + continue; + + sprintf(Line,"%s
Flight %s
Heading %d°
Speed %d kts
Altitude %d feet
rssi %d
Age %d", + ptr->hex, ptr->flight, ptr->track, ptr->speed, ptr->altitude, ptr->rssi, Age); + + Len += sprintf(&Buffer[Len],"P,%.4f,%.4f,%s,%s,%d,%d,%d\r\n|", + ptr->lat, ptr->lon, Line, ptr->flight, ptr->track, ptr->speed, Age); + + if (ptr->TrackTime[0]) // Have trackpoints + { + int n = ptr->Trackptr; + int i; + + // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros + + Len += sprintf(&Buffer[Len],"T,"); + + for (i = 0; i < TRACKPOINTS; i++) + { + if (ptr->TrackTime[n]) + { + Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); + } + + n++; + if (n == TRACKPOINTS) + n = 0; + } + Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->lat, ptr->lon); //Add current position to end of track + } + } + } + return Len; +} + +/* +{ "now" : 1634055090.3, + "messages" : 28814, + "aircraft" : [ + {"hex":"4b8467","squawk":"1146","flight":"RUN410 ","lat":58.554749,"lon":-7.728350,"nucp":7,"seen_pos":0.0,"altitude":37000,"vert_rate":0,"track":111,"speed":537,"messages":197,"seen":0.0,"rssi":-32.7}, + {"hex":"abed10","squawk":"1170","flight":"FDX4 ","lat":57.969498,"lon":-7.888449,"nucp":7,"seen_pos":7.2,"altitude":37000,"vert_rate":0,"track":137,"speed":553,"messages":588,"seen":0.1,"rssi":-34.6}, + {"hex":"4007dd","squawk":"2000","altitude":1825,"messages":180,"seen":37.8,"rssi":-33.4} + ] +} +*/ + +void ProcessADSBMessage(struct ADSBRECORD * plane); +void ProcessADSBLine(char * Msg, int Len); + +void ProcessADSBJSON(char * FN) +{ + FILE *file; + char buf[2048]; + char *token; + struct ADSBRECORD plane; + time_t ReportTime; + + // Testing, read dump1090 file + + if (ADSBHost[0]) + return; // Using TCP + + if ((file = fopen(FN,"r")) == NULL) return; + + fgets(buf, 2048, file); // now + ReportTime = atoi(&buf[9]); + fgets(buf, 2048, file); // messages + fgets(buf, 2048, file); // aircraft + + while (1) + { + if (fgets(buf, 2048, file) == 0) + { + fclose(file); + return; + } + + if (strchr(buf, ']')) + { + fclose(file); + return; + } + + memset(&plane, 0, sizeof(struct ADSBRECORD)); + + if (token = strstr(buf, "hex")) + { + memcpy(plane.hex, &token[6], 7); + strlop(plane.hex, '"'); + } + + if (token = strstr(buf, "squawk")) + { + memcpy(plane.squawk, &token[9], 4); + } + + if (token = strstr(buf, "flight")) + { + memcpy(plane.flight, &token[9], 8); + } + + if (token = strstr(buf, "lat")) + { + plane.lat = atof(&token[5]); + } + + if (token = strstr(buf, "lon")) + { + plane.lon = atof(&token[5]); + } + + if (token = strstr(buf, "seen_pos")) + { + plane.seen_pos = ReportTime - atoi(&token[10]); + } + + if (token = strstr(buf, "altitude")) + { + plane.altitude = atoi(&token[10]); + } + + if (token = strstr(buf, "vert_rate")) + { + plane.vert_rate = atoi(&token[11]); + } + + if (token = strstr(buf, "track")) + { + plane.track = atoi(&token[7]); + } + + if (token = strstr(buf, "speed")) + { + plane.speed = atoi(&token[7]); + } + + if (token = strstr(buf, "messages")) + { + plane.messages = atoi(&token[10]); + } + + if (token = strstr(buf, "seen")) + { + plane.seen = ReportTime - atoi(&token[6]); + } + + + if (token = strstr(buf, "rssi")) + { + plane.rssi = atoi(&token[6]); + } + ProcessADSBMessage(&plane); + } +} + +void ProcessADSBMessage(struct ADSBRECORD * plane) +{ + int i; + struct ADSBRECORD * ptr; + struct ADSBRECORD * spare = 0; // Pointer to a cleared record + + for (i = 0; i < ADSBCount; i++) + { + ptr = ADSBRecords[i]; + + if (strcmp(ptr->hex, plane->hex) == 0) + { + if (plane->squawk[0]) + strcpy(ptr->squawk,plane->squawk); + + if (plane->flight[0]) + strcpy(ptr->flight, plane->flight); + + if (plane->lat) + ptr->lat = plane->lat; + + if (plane->lon) + ptr->lon = plane->lon; + + if (plane->altitude) + ptr->altitude = plane->altitude; + + if (plane->seen_pos) + ptr->seen_pos = plane->seen_pos; + + if (plane->vert_rate) + ptr->vert_rate = plane->vert_rate; + + if (plane->track) + ptr->track = plane->track; + + if (plane->speed) + ptr->speed = plane->speed; + + if (plane->messages) + ptr->messages = plane->messages; + + if (plane->seen) + ptr->seen = plane->seen; + + if (plane->rssi) + ptr->rssi = plane->rssi; + + ptr->TimeLastUpdated = ptr->seen; + + if (ptr->lat == 0.0) + return; + + // Save Track Point + + if ((NOW - ptr->LastTime) < 15) // Not More that once per 15 secs + return; + + ptr->LastCourse = ptr->track; + ptr->LastSpeed = ptr->speed; + ptr->LastTime = NOW; + ptr->Lastlat = ptr->lat; + ptr->LastLong = ptr->lon; + + ptr->LatTrack[ptr->Trackptr] = ptr->lat; + ptr->LonTrack[ptr->Trackptr] = ptr->lon; + ptr->TrackTime[ptr->Trackptr] = NOW; + + ptr->Trackptr++; + + if (ptr->Trackptr == TRACKPOINTS) + ptr->Trackptr = 0; + + return; + } + + if (ptr->hex[0] == 0 && spare == 0) //Zapped record + spare = ptr; + } + + // Not found, if got a freed entry use it, elsee add new one + + if (spare) + ptr = spare; + else + { + if (ADSBCount == 0) + ADSBRecords = (struct ADSBRECORD **)malloc(sizeof(void *)); + else + ADSBRecords = (struct ADSBRECORD **)realloc(ADSBRecords,(ADSBCount+1)*sizeof(void *)); + + ptr = (struct ADSBRECORD *)zalloc(sizeof(struct ADSBRECORD)); + + if (ptr == NULL) return; + + ADSBRecords[ADSBCount] = ptr; + ADSBCount++; + } + + strcpy(ptr->hex, plane->hex); + ptr->TimeAdded = NOW; + + ProcessADSBMessage(plane); // Reenter to save details + return; +} + + +// Thread to process ADS-B messages from dump1090 + + +void ProcessADSBLine(char * Msg, int Len) +{ + int i; + struct ADSBRECORD * rec; + struct ADSBRECORD * spare = 0; // Pointer to a cleared record + + char * p[22] = {0}; + char * ptr; + char Type; + char hex[10]; + +//MSG,3,111,11111,394A05,111111,2021/10/13,07:56:46.075,2021/10/13,07:56:46.128,,37000,,,59.11278,-6.53052,,,,,,0 + + if (memcmp(Msg, "MSG,", 4) != 0) + return; + + // Simplest way to process comma separated with null fields is strlop; + + ptr = &Msg[4]; + + for (i = 0; i < 21; i++) + { + p[i] = ptr; + ptr = strlop(ptr, ','); + } + + if (p[20] == 0) + return; // Should have 21 params + + strcpy(hex, p[3]); // identifier + + for (i = 0; i < ADSBCount; i++) + { + rec = ADSBRecords[i]; + + if (strcmp(rec->hex, hex) == 0) + goto Found; + + if (rec->hex[0] == 0 && spare == 0) //Zapped record + spare = rec; + } + + if (spare) + rec = spare; + else + { + // Not found, so add + + if (ADSBCount == 0) + ADSBRecords = (struct ADSBRECORD **)malloc(sizeof(void *)); + else + ADSBRecords = (struct ADSBRECORD **)realloc(ADSBRecords,(ADSBCount+1) * sizeof(void *)); + + rec = (struct ADSBRECORD *)zalloc(sizeof(struct ADSBRECORD)); + + if (rec == NULL) return; + + ADSBRecords[ADSBCount] = rec; + ADSBCount++; + } + + strcpy(rec->hex, hex); + rec->TimeAdded = NOW; + +Found: + + rec->seen = NOW; + rec->messages++; + + Type = atoi(p[0]); + + switch (Type) + { + case 1: + + strcpy(rec->flight, p[9]); + return; + + case 2: + + rec->altitude = atoi(p[10]); + rec->speed = atoi(p[11]); + rec->track = atoi(p[12]); + rec->lat = atof(p[13]); + rec->lon = atof(p[14]); + rec->seen_pos = NOW; + break; + + case 3: + + // Position + + rec->altitude = atoi(p[10]); + rec->lat = atof(p[13]); + rec->lon = atof(p[14]); + rec->seen_pos = NOW; + break; + + case 4: + + rec->speed = atoi(p[11]); + rec->track = atoi(p[12]); + rec->vert_rate = atoi(p[15]); + return; + + case 5: + return; + + case 6: + + strcpy(rec->squawk, p[16]); + return; + + case 7: + case 8: + return; + } + + // Recs with position get here + + if (rec->lat == 0.0) + return; + + // Save Track Point + +// if ((NOW - rec->LastTime) < 15) // Not More that once per 15 secs +// return; + + rec->LastCourse = rec->track; + rec->LastSpeed = rec->speed; + rec->LastTime = NOW; + rec->Lastlat = rec->lat; + rec->LastLong = rec->lon; + + rec->LatTrack[rec->Trackptr] = rec->lat; + rec->LonTrack[rec->Trackptr] = rec->lon; + rec->TrackTime[rec->Trackptr] = NOW; + + rec->Trackptr++; + + if (rec->Trackptr == TRACKPOINTS) + rec->Trackptr = 0; +} + +static VOID ProcessADSBData(SOCKET TCPSock) +{ + char Buffer[66000]; + char Msg[1024]; + + int len; + + char * ptr; + char * Lastptr; + + // Although it is possible that a packet will be split by a partial read. + // it is so unlikely with a 65536 buffer that I'll just accept the loss + + len = recv(TCPSock, Buffer, 65536, 0); + + if (len <= 0) + { + closesocket(TCPSock); + ADSBConnected = FALSE; + return; + } + + if (len > 5000) + Debugprintf("ADSB Len %d", len); + + ptr = Lastptr = Buffer; + + Buffer[len] = 0; + + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr - 1; + + if (Len > 1020) + return; + + memcpy(Msg, Lastptr, Len); + Msg[Len++] = 13; + Msg[Len++] = 10; + Msg[Len] = 0; + + ProcessADSBLine(Msg, Len); + + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } +} + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +static VOID ADSBConnect(void * unused) +{ + int err, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + struct sockaddr_in destaddr; + SOCKET TCPSock; + int addrlen=sizeof(sinx); + + if (ADSBHost[0] == 0) + return; + + destaddr.sin_addr.s_addr = inet_addr(ADSBHost); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(ADSBPort); + + TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TCPSock == INVALID_SOCKET) + { + return; + } + + setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + ADSBConnected = TRUE; // So we don't try to reconnect while waiting + + if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ioctl(TCPSock, FIONBIO, ¶m); + } + else + { + err=WSAGetLastError(); +#ifdef LINBPQ + printf("Connect Failed for ADSB socket - error code = %d\n", err); +#else + Debugprintf("Connect Failed for ADSB socket - error code = %d", err); +#endif + closesocket(TCPSock); + ADSBConnected = FALSE; + + return; + } + + ADSBConnected = TRUE; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TCPSock,&readfs); + FD_SET(TCPSock,&errorfs); + + timeout.tv_sec = 60; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TCPSock, &readfs)) + { + ProcessADSBData(TCPSock); + } + + if (FD_ISSET(TCPSock, &errorfs)) + { +Lost: +#ifdef LINBPQ + printf("ADSB Connection lost\n"); +#endif + closesocket(TCPSock); + ADSBConnected = FALSE; + return; + } + } + else + { + // 60 secs without data. Shouldn't happen + + shutdown(TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TCPSock); + ADSBConnected = FALSE; + return; + } + } +} + +//http://fr24.com/+selected.flight +//http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?flightNumber='+selected.flight \ No newline at end of file diff --git a/APRSCode.c b/APRSCode.c new file mode 100644 index 0000000..b9cd79a --- /dev/null +++ b/APRSCode.c @@ -0,0 +1,8750 @@ +/* +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 +*/ + +// Module to implement APRS "New Paradigm" Digipeater and APRS-IS Gateway + +// First Version, November 2011 + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include "CHeaders.h" +#include "bpq32.h" +#include +#include "kernelresource.h" + +#include "tncinfo.h" + +#include "bpqaprs.h" + +#ifndef WIN32 + +#include +#include +#include + +int sfd; +struct sockaddr_un my_addr, peer_addr; +socklen_t peer_addr_size; + + +#endif + + +#define MAXAGE 3600 * 12 // 12 Hours +#define MAXCALLS 20 // Max Flood, Trace and Digi +#define GATETIMELIMIT 40 * 60 // Don't gate to RF if station not heard for this time (40 mins) + +static BOOL APIENTRY GETSENDNETFRAMEADDR(); +static VOID DoSecTimer(); +static VOID DoMinTimer(); +static int APRSProcessLine(char * buf); +static BOOL APRSReadConfigFile(); +VOID APRSISThread(void * Report); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +char * strlop(char * buf, char delim); +int APRSDecodeFrame(char * msg, char * buffer, time_t Stamp, UINT Mask); // Unsemaphored DecodeFrame +APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port); +BOOL CheckforDups(char * Call, char * Msg, int Len); +VOID ProcessQuery(char * Query); +VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis); +VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len); +VOID SendBeacon(int toPort, char * Msg, BOOL SendISStatus, BOOL SendSOGCOG); +Dll BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); +VOID ProcessAPRSISMsg(char * APRSMsg); +static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +APRSHEARDRECORD * FindStationInMH(char * call); +BOOL OpenGPSPort(); +void PollGPSIn(); +int CountLocalStations(); +BOOL SendAPPLAPRSMessage(char * Frame); +VOID SendAPRSMessage(char * Message, int toPort); +static VOID TCPConnect(void * unuxed); +struct STATIONRECORD * DecodeAPRSISMsg(char * msg); +struct STATIONRECORD * ProcessRFFrame(char * buffer, int len, int * ourMessage); +VOID APRSSecTimer(); +double myDistance(double laa, double loa, BOOL KM); +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFound); +int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station); +BOOL KillOldTNC(char * Path); +int FromLOC(char * Locator, double * pLat, double * pLon); +BOOL ToLOC(double Lat, double Lon , char * Locator); +BOOL InternalSendAPRSMessage(char * Text, char * Call); +void UndoTransparency(char * input); +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); +char * GetStandardPage(char * FN, int * Len); +VOID WriteMiniDump(); +BOOL ProcessConfig(); +int ProcessAISMessage(char * msg, int len); +int read_png(unsigned char *bytes); +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); + +extern int SemHeldByAPI; +extern int APRSMONDECODE(); +extern struct ConsoleInfo MonWindow; +extern char VersionString[]; + + +BOOL LogAPRSIS = FALSE; + +// All data should be initialised to force into shared segment + +static char ConfigClassName[]="CONFIG"; + +BPQVECSTRUC * APRSMONVECPTR; + +extern int MONDECODE(); +extern VOID * zalloc(int len); +extern BOOL StartMinimized; + +extern char * PortConfig[]; +extern char TextVerstring[]; + +extern HWND hConsWnd; +extern HKEY REGTREE; + +extern char LOCATOR[80]; +extern char LOC[7]; + +static int SecTimer = 10; +static int MinTimer = 60; + +BOOL APRSApplConnected = FALSE; +BOOL APRSWeb = FALSE; + +void * APPL_Q = 0; // Queue of frames for APRS Appl +void * APPLTX_Q = 0; // Queue of frames from APRS Appl +UINT APRSPortMask = 0; + +char APRSCall[10] = ""; +char APRSDest[10] = "APBPQ1"; + +char WXCall[10]; + +UCHAR AXCall[7] = ""; + +char CallPadded[10] = " "; + +char GPSPort[80] = ""; +int GPSSpeed = 0; +char GPSRelay[80] = ""; + +BOOL GateLocal = FALSE; +double GateLocalDistance = 0.0; + +int MaxDigisforIS = 7; // Dont send to IS if more digis uued to reach us + +char WXFileName[MAX_PATH]; +char WXComment[80]; +BOOL SendWX = FALSE; +int WXInterval = 30; +int WXCounter = 29 * 60; + +char APRSCall[10]; +char LoppedAPRSCall[10]; + +BOOL WXPort[32]; // Ports to send WX to + +BOOL GPSOK = 0; + +char LAT[] = "0000.00N"; // in standard APRS Format +char LON[] = "00000.00W"; //in standard APRS Format + +char HostName[80]; // for BlueNMEA +int HostPort = 4352; + +extern int ADSBPort; +extern char ADSBHost[]; + +BOOL BlueNMEAOK = FALSE; +int BlueNMEATimer = 0; + +BOOL GPSSetsLocator = 0; // Update Map Location from GPS + +double SOG, COG; // From GPS + +double Lat = 0.0; +double Lon = 0.0; + +BOOL PosnSet = FALSE; +/* +The null position should be include the \. symbol (unknown/indeterminate +position). For example, a Position Report for a station with unknown position +will contain the coordinates …0000.00N\00000.00W.… +*/ +char * FloodCalls = 0; // Calls to relay using N-n without tracing +char * TraceCalls = 0; // Calls to relay using N-n with tracing +char * DigiCalls = 0; // Calls for normal relaying + +UCHAR FloodAX[MAXCALLS][7] = {0}; +UCHAR TraceAX[MAXCALLS][7] = {0}; +UCHAR DigiAX[MAXCALLS][7] = {0}; + +int FloodLen[MAXCALLS]; +int TraceLen[MAXCALLS]; +int DigiLen[MAXCALLS]; + +int ISPort = 0; +char ISHost[256] = ""; +int ISPasscode = 0; +char NodeFilter[1000] = "m/50"; // Filter when the isn't an application +char ISFilter[1000] = "m/50"; // Current Filter +char APPLFilter[1000] = ""; // Filter when an Applcation is running + +extern BOOL IGateEnabled; + +char StatusMsg[256] = ""; // Must be in shared segment +int StatusMsgLen = 0; + +char * BeaconPath[33] = {0}; + +char CrossPortMap[33][33] = {0}; +char APRSBridgeMap[33][33] = {0}; + +UCHAR BeaconHeader[33][10][7] = {""}; // Dest, Source and up to 8 digis +int BeaconHddrLen[33] = {0}; // Actual Length used + +UCHAR GatedHeader[33][10][7] = {""}; // Dest, Source and up to 8 digis for messages gated from IS +int GatedHddrLen[33] = {0}; // Actual Length used + + +char CFGSYMBOL = 'a'; +char CFGSYMSET = 'B'; + +char SYMBOL = '='; // Unknown Locaton +char SYMSET = '/'; + +BOOL TraceDigi = FALSE; // Add Trace to packets relayed on Digi Calls +BOOL SATGate = FALSE; // Delay Gating to IS directly heard packets + +BOOL DefaultLocalTime = FALSE; +BOOL DefaultDistKM = FALSE; + +int multiple = 0; // Allows multiple copies of LinBPQ/APRS on one machine + +extern BOOL needAIS; + +extern unsigned long long IconData[]; // Symbols as a png image. + +typedef struct _ISDELAY +{ + struct _ISDELAY * Next; + char * ISMSG; + time_t SendTIme; +} ISDELAY; + +ISDELAY * SatISQueue = NULL; + +int MaxTraceHops = 2; +int MaxFloodHops = 2; + +int BeaconInterval = 0; +int MobileBeaconInterval = 0; +time_t LastMobileBeacon = 0; +int BeaconCounter = 0; +int IStatusCounter = 3600; // Used to send ?ISTATUS? Responses +//int StatusCounter = 0; // Used to send Status Messages + +char RunProgram[128] = ""; // Program to start + +BOOL APRSISOpen = FALSE; +BOOL BeacontoIS = TRUE; + +int ISDelayTimer = 0; // Time before trying to reopen APRS-IS link + +char APRSDESTS[][7] = {"AIR*", "ALL*", "AP*", "BEACON", "CQ*", "GPS*", "DF*", "DGPS*", "DRILL*", + "DX*", "ID*", "JAVA*", "MAIL*", "MICE*", "QST*", "QTH*", "RTCM*", "SKY*", + "SPACE*", "SPC*", "SYM*", "TEL*", "TEST*", "TLM*", "WX*", "ZIP"}; + +UCHAR AXDESTS[30][7] = {""}; +int AXDESTLEN[30] = {0}; + +UCHAR axTCPIP[7]; +UCHAR axRFONLY[7]; +UCHAR axNOGATE[7]; + +int MessageCount = 0; + +struct PortInfo +{ + int Index; + int ComPort; + char PortType[2]; + BOOL NewVCOM; // Using User Mode Virtual COM Driver + int ReopenTimer; // Retry if open failed delay + int RTS; + int CTS; + int DCD; + int DTR; + int DSR; + char Params[20]; // Init Params (eg 9600,n,8) + char PortLabel[20]; + HANDLE hDevice; + BOOL Created; + BOOL PortEnabled; + int FLOWCTRL; + int gpsinptr; +#ifdef WIN32 + OVERLAPPED Overlapped; + OVERLAPPED OverlappedRead; +#endif + char GPSinMsg[160]; + int GPSTypeFlag; // GPS Source flags + BOOL RMCOnly; // Only send RMC msgs to this port +}; + + + +struct PortInfo InPorts[1] = {0}; + +// Heard Station info + +#define MAXHEARD 1000 + +int HEARDENTRIES = 0; +int MAXHEARDENTRIES = 0; +int MHLEN = sizeof(APRSHEARDRECORD); + +// Area is allocated as needed + +APRSHEARDRECORD MHTABLE[MAXHEARD] = {0}; + +APRSHEARDRECORD * MHDATA = &MHTABLE[0]; + +static SOCKET sock = (SOCKET) NULL; + +//Duplicate suppression Code + +#define MAXDUPS 100 // Number to keep +#define DUPSECONDS 28 // Time to Keep + +struct DUPINFO +{ + time_t DupTime; + int DupLen; + char DupUser[8]; // Call in ax.35 format + char DupText[100]; +}; + +struct DUPINFO DupInfo[MAXDUPS]; + +struct OBJECT +{ + struct OBJECT * Next; + UCHAR Path[10][7]; // Dest, Source and up to 8 digis + int PathLen; // Actual Length used + char Message[81]; + char PortMap[33]; + int Interval; + int Timer; +}; + +struct OBJECT * ObjectList; // List of objects to send; + +int ObjectCount = 0; + +#include + +#define M_PI 3.14159265358979323846 + +int RetryCount = 4; +int RetryTimer = 45; +int ExpireTime = 120; +int TrackExpireTime = 1440; +BOOL SuppressNullPosn = FALSE; +BOOL DefaultNoTracks = FALSE; + +int MaxStations = 1000; + +int SharedMemorySize = 0; + + +RECT Rect, MsgRect, StnRect; + +char Key[80]; + +// function prototypes + +VOID RefreshMessages(); + +// a few global variables + +char APRSDir[MAX_PATH] = "BPQAPRS"; +char DF[MAX_PATH]; + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +int StationCount = 0; + +UCHAR NextSeq = 1; + +// Stationrecords are stored in a shared memory segment. based at APRSStationMemory (normally 0x43000000) + +// A pointer to the first is placed at the start of this + +struct STATIONRECORD ** StationRecords = NULL; +struct STATIONRECORD * StationRecordPool = NULL; +struct APRSMESSAGE * MessageRecordPool = NULL; + +struct SharedMem * SMEM; + +UCHAR * Shared; +UCHAR * StnRecordBase; + +VOID SendObject(struct OBJECT * Object); +VOID MonitorAPRSIS(char * Msg, int MsgLen, BOOL TX); + +#ifndef WIN32 +#define WSAEWOULDBLOCK 11 +#endif + +HANDLE hMapFile; + +// Logging + +static int LogAge = 14; + +#ifdef WIN32 + +int DeleteAPRSLogFiles() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetLogDirectory()); + strcat(szDir, "/logs/APRS*.log"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return dwError; + + // Walk directory + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (int)((now - ft.LowPart) / 86400); + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + Debugprintf("Deleting %s", File); + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return dwError; +} + +#else + +#include + +int APRSFilter(const struct dirent * dir) +{ + return (memcmp(dir->d_name, "APRS", 4) == 0 && strstr(dir->d_name, ".log")); +} + +int DeleteAPRSLogFiles() +{ + struct dirent **namelist; + int n; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("logs", &namelist, APRSFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + sprintf(FN, "logs/%s", namelist[n]->d_name); + if (stat(FN, &STAT) == 0) + { + Age = (now - STAT.st_mtime) / 86400; + + if (Age > LogAge) + { + Debugprintf("Deleting %s\n", FN); + unlink(FN); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +int APRSWriteLog(char * msg) +{ + FILE *file; + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + + if (LogAPRSIS == 0) + return 0; + + if (strchr(msg, '\n') == 0) + strcat(msg, "\r\n"); + + T = time(NULL); + tm = gmtime(&T); + + if (GetLogDirectory()[0] == 0) + { + strcpy(Value, "logs/APRS_"); + } + else + { + strcpy(Value, GetLogDirectory()); + strcat(Value, "/"); + strcat(Value, "logs/APRS_"); + } + + sprintf(Value, "%s%02d%02d%02d.log", Value, + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + + if ((file = fopen(Value, "ab")) == NULL) + return FALSE; + + fputs(msg, file); + fclose(file); + return 0; +} + + +int ISSend(SOCKET sock, char * Msg, int Len, int flags) +{ + int Loops = 0; + int Sent; + + MonitorAPRSIS(Msg, Len, TRUE); + + Sent = send(sock, Msg, Len, flags); + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + if ((Sent == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) + return SOCKET_ERROR; + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Msg, &Msg[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Msg, Len, flags); + } + + return Sent; +} + +void * endofStations; + +Dll BOOL APIENTRY Init_APRS() +{ + int i; + char * DCall; + +#ifdef WIN32 + HKEY hKey=0; + int retCode, Vallen, Type; +#else + int fd; + char RX_SOCK_PATH[] = "BPQAPRSrxsock"; + char TX_SOCK_PATH[] = "BPQAPRStxsock"; + char SharedName[256]; + char * ptr1; +#endif + struct STATIONRECORD * Stn1, * Stn2; + struct APRSMESSAGE * Msg1, * Msg2; + + // Clear tables in case a restart + + StationRecords = NULL; + + StationCount = 0; + HEARDENTRIES = 0; + MAXHEARDENTRIES = 0; + MobileBeaconInterval = 0; + BeaconInterval = 0; + + DeleteAPRSLogFiles(); + + memset(MHTABLE, 0, sizeof(MHTABLE)); + + ConvToAX25(MYNODECALL, MYCALL); + + ConvToAX25("TCPIP", axTCPIP); + ConvToAX25("RFONLY", axRFONLY); + ConvToAX25("NOGATE", axNOGATE); + + memset(&FloodAX[0][0], 0, sizeof(FloodAX)); + memset(&TraceAX[0][0], 0, sizeof(TraceAX)); + memset(&DigiAX[0][0], 0, sizeof(DigiAX)); + + APRSPortMask = 0; + + memset(BeaconPath, sizeof(BeaconPath), 0); + + memset(&CrossPortMap[0][0], 0, sizeof(CrossPortMap)); + memset(&APRSBridgeMap[0][0], 0, sizeof(APRSBridgeMap)); + + for (i = 1; i <= 32; i++) + { + CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port + CrossPortMap[i][0] = TRUE; // and APRS-IS + } + + PosnSet = 0; + ObjectList = NULL; + ObjectCount = 0; + + ISPort = ISHost[0] = ISPasscode = 0; + + if (APRSReadConfigFile() == 0) + return FALSE; + + if (APRSCall[0] == 0) + { + strcpy(APRSCall, MYNODECALL); + strlop(APRSCall, ' '); + strcpy(LoppedAPRSCall, APRSCall); + memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging + ConvToAX25(APRSCall, AXCall); + } + + if (WXCall[0] == 0) + strcpy(WXCall, APRSCall); + + // Caluclate size of Shared Segment + + SharedMemorySize = sizeof(struct STATIONRECORD) * (MaxStations + 4) + + sizeof(struct APRSMESSAGE) * (MAXMESSAGES + 4) + 32; // 32 for header + + +#ifndef WIN32 + + // Create a Shared Memory Object + + Shared = NULL; + + // Append last bit of current directory to shared name + + ptr1 = BPQDirectory; + + while (strchr(ptr1, '/')) + { + ptr1 = strchr(ptr1, '/'); + ptr1++; + } + + if (multiple) + sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); + else + strcpy(SharedName, "/BPQAPRSSharedMem"); + + printf("Using Shared Memory %s\n", SharedName); + +#ifndef WIN32 + + fd = shm_open(SharedName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) + { + perror("Create Shared Memory"); + printf("Create APRS Shared Memory Failed\n"); + } + else + { + if (ftruncate(fd, SharedMemorySize)) + { + perror("Extend Shared Memory"); + printf("Extend APRS Shared Memory Failed\n"); + } + else + { + // Map shared memory object + + Shared = mmap((void *)APRSSHAREDMEMORYBASE, + SharedMemorySize, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (Shared == MAP_FAILED) + { + perror("Map Shared Memory"); + printf("Map APRS Shared Memory Failed\n"); + Shared = NULL; + } + + if (Shared != (void *)APRSSHAREDMEMORYBASE) + { + printf("Map APRS Shared Memory Allocated at %x\n", Shared); + Shared = NULL; + } + + } + } + +#endif + + printf("Map APRS Shared Memory Allocated at %p\n", Shared); + + if (Shared == NULL) + { + printf("APRS not using shared memory\n"); + Shared = malloc(SharedMemorySize); + printf("APRS Non-Shared Memory Allocated at %x\n", Shared); + } + +#else + +#ifndef LINBPQ + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen = 4; + retCode = RegQueryValueEx(hKey, "IGateEnabled", 0, &Type, (UCHAR *)&IGateEnabled, &Vallen); + } + +#endif + + // Create Memory Mapping for Station List + + hMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + SharedMemorySize, // maximum object size (low-order DWORD) + "BPQAPRSStationsMappingObject");// name of mapping object + + if (hMapFile == NULL) + { + Consoleprintf("Could not create file mapping object (%d).\n", GetLastError()); + return 0; + } + + UnmapViewOfFile((void *)APRSSHAREDMEMORYBASE); + + + Shared = (LPTSTR) MapViewOfFileEx(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + SharedMemorySize, + (void *)APRSSHAREDMEMORYBASE); + + if (Shared == NULL) + { + Consoleprintf("Could not map view of file (%d).\n", GetLastError()); + CloseHandle(hMapFile); + return 0; + } + +#endif + + // First record has pointer to table + + memset(Shared, 0, SharedMemorySize); + + StnRecordBase = Shared + 32; + SMEM = (struct SharedMem *)Shared; + + SMEM->Version = 1; + SMEM->SharedMemLen = SharedMemorySize; + SMEM->NeedRefresh = TRUE; + SMEM->Arch = sizeof(void *); + SMEM->SubVersion = 1; + + Stn1 = (struct STATIONRECORD *)StnRecordBase; + + StationRecords = (struct STATIONRECORD **)Stn1; + + Stn1++; + + StationRecordPool = Stn1; + + for (i = 1; i < MaxStations; i++) // Already have first + { + Stn2 = Stn1; + Stn2++; + Stn1->Next = Stn2; + + Stn1 = Stn2; + } + + Debugprintf("End of Stations %p", Stn1); + endofStations = Stn1; + + Stn1 += 2; // Try to fix corruption of messages. + + // Build Message Record Pool + + Msg1 = (struct APRSMESSAGE *)Stn1; + + MessageRecordPool = Msg1; + + for (i = 1; i < MAXMESSAGES; i++) // Already have first + { + Msg2 = Msg1; + Msg2++; + Msg1->Next = Msg2; + + Msg1 = Msg2; + } + + if (PosnSet == 0) + { + SYMBOL = '.'; + SYMSET = '\\'; // Undefined Posn Symbol + } + else + { + // Convert posn to floating degrees + + char LatDeg[3], LonDeg[4]; + memcpy(LatDeg, LAT, 2); + LatDeg[2]=0; + Lat=atof(LatDeg) + (atof(LAT+2)/60); + + if (LAT[7] == 'S') Lat=-Lat; + + memcpy(LonDeg, LON, 3); + LonDeg[3]=0; + Lon=atof(LonDeg) + (atof(LON+3)/60); + + if (LON[8]== 'W') Lon=-Lon; + + SYMBOL = CFGSYMBOL; + SYMSET = CFGSYMSET; + } + + // First record has control info for APRS Mapping App + + Stn1 = (struct STATIONRECORD *)StnRecordBase; + memcpy(Stn1->Callsign, APRSCall, 10); + Stn1->Lat = Lat; + Stn1->Lon = Lon; + Stn1->LastPort = MaxStations; + +#ifndef WIN32 + + // Open unix socket for messaging app + + sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + + if (sfd == -1) + { + perror("Socket"); + } + else + { + u_long param=1; + ioctl(sfd, FIONBIO, ¶m); // Set non-blocking + + memset(&my_addr, 0, sizeof(struct sockaddr_un)); + my_addr.sun_family = AF_UNIX; + strncpy(my_addr.sun_path, TX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); + + memset(&peer_addr, 0, sizeof(struct sockaddr_un)); + peer_addr.sun_family = AF_UNIX; + strncpy(peer_addr.sun_path, RX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); + + unlink(TX_SOCK_PATH); + + if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) + perror("bind"); + } +#endif + + // Convert Dest ADDRS to AX.25 + + for (i = 0; i < 26; i++) + { + DCall = &APRSDESTS[i][0]; + if (strchr(DCall, '*')) + AXDESTLEN[i] = (int)strlen(DCall) - 1; + else + AXDESTLEN[i] = 6; + + ConvToAX25(DCall, &AXDESTS[i][0]); + } + + // Process any Object Definitions + + // Setup Heard Data Area + + HEARDENTRIES = 0; + MAXHEARDENTRIES = MAXHEARD; + + APRSMONVECPTR->HOSTAPPLFLAGS = 0x80; // Request Monitoring + + if (ISPort && IGateEnabled) + { + _beginthread(APRSISThread, 0, (VOID *) TRUE); + } + + if (GPSPort[0]) + OpenGPSPort(); + + WritetoConsole("APRS Digi/Gateway Enabled\n"); + + APRSWeb = TRUE; + + read_png((unsigned char *)IconData); + + // If a Run parameter was supplied, run the program + + if (RunProgram[0] == 0) + return TRUE; + + #ifndef WIN32 + { + char * arg_list[] = {NULL, NULL}; + pid_t child_pid; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + // Fork and Exec ARDOP + + printf("Trying to start %s\n", RunProgram); + + arg_list[0] = RunProgram; + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("APRS fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", RunProgram); + exit(0); // Kill the new process + } + } +#else + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + while (KillOldTNC(RunProgram) && n++ < 100) + { + Sleep(100); + } + + if (!CreateProcess(RunProgram, NULL, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", RunProgram, GetLastError()); + } +#endif + + return TRUE; +} + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +BOOL APRSActive; + +VOID APRSClose() +{ + APRSActive = FALSE; + + if (sock) + { + shutdown(sock, SD_BOTH); + Sleep(50); + + closesocket(sock); + } +#ifdef WIN32 + if (InPorts[0].hDevice) + CloseHandle(InPorts[0].hDevice); +#endif +} + +Dll VOID APIENTRY Poll_APRS() +{ + SecTimer--; + + if (SecTimer == 0) + { + SecTimer = 10; + DoSecTimer(); + + MinTimer--; + + if (MinTimer == 0) + { + MinTimer = 10; + DoMinTimer(); + } + } + + if (SMEM->ClearRX) + { + // Clear Received Messages request from GUI + + struct APRSMESSAGE * ptr = SMEM->Messages; + + // Move Message Queue to Free Queue + + if (ptr) + { + while (ptr->Next) // Find end of chain + { + ptr = ptr->Next; + } + + // ptr is end of chain - chain free pool to it + + ptr->Next = MessageRecordPool; + + MessageRecordPool = SMEM->Messages; + MessageCount = 0; + } + + SMEM->Messages = NULL; + SMEM->ClearRX = 0; + SMEM->NeedRefresh = TRUE; + } + + if (SMEM->ClearTX) + { + // Clear Sent Messages )request from GUI + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + // Move Message Queue to Free Queue + + if (ptr) + { + while (ptr->Next) // Find end of chain + { + ptr = ptr->Next; + } + + // ptr is end of chain - chain free pool to it + + ptr->Next = MessageRecordPool; + + MessageRecordPool = SMEM->OutstandingMsgs; + MessageCount = 0; + } + + SMEM->OutstandingMsgs = NULL; + SMEM->ClearTX = 0; + SMEM->NeedRefresh = TRUE; + } +#ifdef LINBPQ +#ifndef WIN32 + { + char Msg[256]; + int numBytes; + + // Look for messages from App + + numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); + + if (numBytes > 0) + { + InternalSendAPRSMessage(&Msg[10], &Msg[0]); + } + } +#endif +#endif + + if (GPSPort[0]) + PollGPSIn(); + + if (APPLTX_Q) + { + PMSGWITHLEN buffptr = Q_REM(&APPLTX_Q); + + InternalSendAPRSMessage(&buffptr->Data[10], &buffptr->Data[0]); + ReleaseBuffer(buffptr); + } + + while (APRSMONVECPTR->HOSTTRACEQ) + { + time_t stamp; + int len; + BOOL MonitorNODES = FALSE; + PMESSAGE monbuff; + UCHAR * monchars; + MESSAGE * Orig; + int Digis = 0; + MESSAGE * AdjBuff; // Adjusted for digis + BOOL FirstUnused = FALSE; + int DigisUsed = 0; // Digis used to reach us + DIGIMESSAGE Msg = {0}; + int Port; + unsigned char buffer[1024]; + char ISMsg[500]; + char * ptr1; + char * Payload; + char * ptr3; + char * ptr4; + BOOL ThirdParty = FALSE; + BOOL NoGate = FALSE; + APRSHEARDRECORD * MH; + char MsgCopy[500]; + int toPort; + struct STATIONRECORD * Station; + int ourMessage = 0; + +#ifdef WIN32 + struct _EXCEPTION_POINTERS exinfo; + char EXCEPTMSG[80] = ""; +#endif + monbuff = Q_REM((VOID **)&APRSMONVECPTR->HOSTTRACEQ); + + monchars = (UCHAR *)monbuff; + AdjBuff = Orig = (MESSAGE *)monchars; // Adjusted for digis + + Port = Orig->PORT; + + if (Port & 0x80) // TX + { + ReleaseBuffer(monbuff); + continue; + } + + if ((APRSPortMask & (1 << (Port - 1))) == 0)// Port in use for APRS? + { + ReleaseBuffer(monbuff); + continue; + } + + stamp = monbuff->Timestamp; + + if ((UCHAR)monchars[4] & 0x80) // TX + { + ReleaseBuffer(monbuff); + continue; + } + + // See if digipeaters present. + + while ((AdjBuff->ORIGIN[6] & 1) == 0 && Digis < 9) + { + UCHAR * temp = (UCHAR *)AdjBuff; + temp += 7; + AdjBuff = (MESSAGE *)temp; + + // If we have already digi'ed it or if we sent it, + // ignore (Dup Check my fail on slow links) + + if (AdjBuff->ORIGIN[6] & 0x80) + { + // Used Digi + + if (memcmp(AdjBuff->ORIGIN, AXCall, 7) == 0) + { + ReleaseBuffer(monbuff); + return; + } + DigisUsed++; + } + + if (memcmp(AdjBuff->ORIGIN, axTCPIP, 6) == 0) + ThirdParty = TRUE; + + Digis ++; + + if (FirstUnused == FALSE && (AdjBuff->ORIGIN[6] & 0x80) == 0) + { + // Unused Digi - see if we should digi it + + FirstUnused = Digis; + // CheckDigi(buff, AdjBuff->ORIGIN); + } + } + + if (Digis > 8) + { + ReleaseBuffer(monbuff); + continue; // Corrupt + } + + if (Digis) + { + if (memcmp(AdjBuff->ORIGIN, axNOGATE, 6) == 0 + || memcmp(AdjBuff->ORIGIN, axRFONLY, 6) == 0 + || DigisUsed > MaxDigisforIS) + + // TOo many digis or Last digis is NOGATE or RFONLY - dont send to IS + + NoGate = TRUE; + } + if (AdjBuff->CTL != 3 || AdjBuff->PID != 0xf0) // Only UI + { + ReleaseBuffer(monbuff); + continue; + } + + // Bridge if requested + + for (toPort = 1; toPort <= 32; toPort++) + { + if (APRSBridgeMap[Port][toPort]) + { + MESSAGE * Buffer = GetBuff(); + struct PORTCONTROL * PORT; + + if (Buffer) + { + memcpy(Buffer, Orig, Orig->LENGTH); + Buffer->PORT = toPort; + PORT = GetPortTableEntryFromPortNum(toPort); + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + } + } + } + + // Used to check for dups here but according to "Notes to iGate developers" IS should be sent dups, and dup + // check only applied to digi'ing + +// if (SATGate == 0) +// { +// if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *))) +// { +// ReleaseBuffer(monbuff); +// continue; +// } +// } + // Decode Frame to TNC2 Monitor Format + + len = APRSDecodeFrame((char *)monchars, buffer, stamp, APRSPortMask); + + if (len == 0) + { + // Couldn't Decode + + ReleaseBuffer(monbuff); + Debugprintf("APRS discarded frame - decode failed\n"); + continue; + } + + buffer[len] = 0; + + memcpy(MsgCopy, buffer, len); + MsgCopy[len] = 0; + + // Do internal Decode + +#ifdef WIN32 + + strcpy(EXCEPTMSG, "ProcessRFFrame"); + + __try + { + + Station = ProcessRFFrame(MsgCopy, len, &ourMessage); + } + #include "StdExcept.c" + + } +#else + Station = ProcessRFFrame(MsgCopy, len, &ourMessage); +#endif + + if (Station == NULL) + { + ReleaseBuffer(monbuff); + continue; + } + + memcpy(MsgCopy, buffer, len); // Process RF Frame may have changed it + MsgCopy[len] = 0; + + buffer[len++] = 10; + buffer[len] = 0; + ptr1 = &buffer[10]; // Skip Timestamp + Payload = strchr(ptr1, ':') + 2; // Start of Payload + ptr3 = strchr(ptr1, ' '); // End of addresses + *ptr3 = 0; + + // We should send path to IS unchanged, so create IS + // message before chopping path. We won't decide if + // we will actually send it to IS till later + + len = sprintf(ISMsg, "%s,qAR,%s:%s", ptr1, APRSCall, Payload); + + + // if digis, remove any unactioned ones + + if (Digis) + { + ptr4 = strchr(ptr1, '*'); // Last Used Digi + + if (ptr4) + { + // We need header up to ptr4 + + *(ptr4) = 0; + } + else + { + // No digis actioned - remove them all + + ptr4 = strchr(ptr1, ','); // End of Dest + if (ptr4) + *ptr4 = 0; + } + } + + ptr4 = strchr(ptr1, '>'); // End of Source + *ptr4++ = 0; + + MH = UpdateHeard(ptr1, Port); + + MH->Station = Station; + + if (ThirdParty) + { +// Debugprintf("Setting Igate Flag - %s", MsgCopy); + MH->IGate = TRUE; // if we've seen msgs to TCPIP, it must be an Igate + } + + if (NoGate) + goto NoIS; + + // I think all PID F0 UI frames go to APRS-IS, + // Except General Queries, Frames Gated from IS to RF, and Messages Addressed to us + + // or should we process Query frames locally ?? + + if (Payload[0] == '}') + goto NoIS; + + if (Payload[0] == '?') + { + // General Query + + ProcessQuery(&Payload[1]); + + // ?? Should we pass addressed Queries to IS ?? + + goto NoIS; + } + + if (Payload[0] == ':' && memcmp(&Payload[1], CallPadded, 9) == 0) + { + // Message for us + + if (Payload[11] == '?') // Only queries - the node doesnt do messaging + ProcessSpecificQuery(&Payload[12], Port, ptr1, ptr4); + + goto NoIS; + } + + if (APRSISOpen && CrossPortMap[Port][0]) // No point if not open + { +// was done above len = sprintf(ISMsg, "%s>%s,qAR,%s:%s", ptr1, ptr4, APRSCall, Payload); + + if (SATGate && (DigisUsed == 0)) + { + // If in Satgate mode delay directly heard to IGate + + ISDELAY * SatISEntry = malloc(sizeof(ISDELAY)); + SatISEntry->Next = NULL; + SatISEntry->ISMSG = _strdup(ISMsg); + SatISEntry->SendTIme = time(NULL) + 10; // Delay 10 seconds + + if (SatISQueue) + SatISEntry->Next = SatISQueue; // Chain + + SatISQueue = SatISEntry; + goto NoIS; + } + + ISSend(sock, ISMsg, len, 0); + + ptr1 = strchr(ISMsg, 13); + if (ptr1) *ptr1 = 0; +// Debugprintf(">%s", ISMsg); + } + + NoIS: + + // We skipped DUP check for SATGate Mode, so apply it here + + // Now we don't dup check earlier so always check here + +// if (SATGate) +// { + if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *)))) + { + ReleaseBuffer(monbuff); + continue; + } +// } + + // See if it is an APRS frame + + // If MIC-E, we need to process, whatever the destination + + // Now process any dest + +/* + DEST = &Orig->DEST[0]; + + for (i = 0; i < 26; i++) + { + if (memcmp(DEST, &AXDESTS[i][0], AXDESTLEN[i]) == 0) + goto OK; + } + + switch(AdjBuff->L2DATA[0]) + { + case '`': + case 0x27: // ' + case 0x1c: + case 0x1d: // MIC-E + + break; + // default: + + // Not to an APRS Destination + +// ReleaseBuffer(monbuff); +// continue; + } + +OK: +*/ + + // If there are unused digis, we may need to digi it. + + if (ourMessage) + { + // A message addressed to us, so no point in digi'ing it + + ReleaseBuffer(monbuff); + continue; + } + + if (Digis == 0 || FirstUnused == 0) + { + // No Digis, so finished + + ReleaseBuffer(monbuff); + continue; + } + + if (memcmp(monbuff->ORIGIN, AXCall, 7) == 0) // We sent it + { + ReleaseBuffer(monbuff); + continue; + } + + // Copy frame to a DIGIMessage Struct + + memcpy(&Msg, monbuff, 21 + (7 * Digis)); // Header, Dest, Source, Addresses and Digis + + len = Msg.LENGTH - 21 - (7 * Digis); // Payload Length (including CTL and PID + + memcpy(&Msg.CTL, &AdjBuff->CTL, len); + + // Pass to Digi Code + + CheckandDigi(&Msg, Port, FirstUnused, Digis, len); // Digi if necessary + + ReleaseBuffer(monbuff); + } + + return; +} + +VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len) +{ + UCHAR * Digi = &Msg->DIGIS[--FirstUnused][0]; + UCHAR * Call; + int Index = 0; + int SSID; + + // Check ordinary digi first + + Call = &DigiAX[0][0]; + SSID = Digi[6] & 0x1e; + + while (*Call) + { + if ((memcmp(Digi, Call, 6) == 0) && ((Call[6] & 0x1e) == SSID)) + { + // Trace Call if enabled + + if (TraceDigi) + memcpy(Digi, AXCall, 7); + + // mark as used; + + Digi[6] |= 0x80; // Used bit + + SendtoDigiPorts(Msg, Len, Port); + return; + } + Call += 7; + Index++; + } + + Call = &TraceAX[0][0]; + Index = 0; + + while (*Call) + { + if (memcmp(Digi, Call, TraceLen[Index]) == 0) + { + // if possible move calls along + // insert our call, set used + // decrement ssid, and if zero, mark as used; + + SSID = (Digi[6] & 0x1E) >> 1; + + if (SSID == 0) + return; // Shouldn't have SSID 0 for Rrace/Flood + + if (SSID > MaxTraceHops) + SSID = MaxTraceHops; // Enforce our limit + + SSID--; + + if (SSID ==0) // Finihed with it ? + Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits + else + Digi[6] = (SSID << 1) | 0x60; // Fixed bits + + if (Digis < 8) + { + memmove(Digi + 7, Digi, (Digis - FirstUnused) * 7); + } + + memcpy(Digi, AXCall, 7); + Digi[6] |= 0x80; + + SendtoDigiPorts(Msg, Len, Port); + + return; + } + Call += 7; + Index++; + } + + Index = 0; + Call = &FloodAX[0][0]; + + while (*Call) + { + if (memcmp(Digi, Call, FloodLen[Index]) == 0) + { + // decrement ssid, and if zero, mark as used; + + SSID = (Digi[6] & 0x1E) >> 1; + + if (SSID == 0) + return; // Shouldn't have SSID 0 for Trace/Flood + + if (SSID > MaxFloodHops) + SSID = MaxFloodHops; // Enforce our limit + + SSID--; + + if (SSID ==0) // Finihed with it ? + Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits + else + Digi[6] = (SSID << 1) | 0x60; // Fixed bits + + SendtoDigiPorts(Msg, Len, Port); + + return; + } + Call += 7; + Index++; + } +} + + + +static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + // Len is the Payload Length (from CTL onwards) + // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 + + // We send to all ports enabled in CrossPortMap + + UCHAR * EndofDigis = &Block->CTL; + int i = 0; + int toPort; + + while (Block->DIGIS[i][0] && i < 8) + { + i++; + } + + EndofDigis = &Block->DIGIS[i][0]; + *(EndofDigis -1) |= 1; // Set End of Address Bit + + if (i != 8) + memmove(EndofDigis, &Block->CTL, Len); + + Len = Len + (i * 7) + 14; // Include Source, Dest and Digis + +// Block->DEST[6] &= 0x7e; // Clear End of Call +// Block->ORIGIN[6] |= 1; // Set End of Call + +// Block->CTL = 3; //UI + + for (toPort = 1; toPort <= 32; toPort++) + { + if (CrossPortMap[Port][toPort]) + Send_AX((PMESSAGE)Block, Len, toPort); + } + return; + +} + +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + + // Len is the Payload Length (CTL, PID, Data) + + // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 + + UCHAR * EndofDigis = &Block->CTL; + + int i = 0; + + while (Block->DIGIS[i][0] && i < 8) + { + i++; + } + + EndofDigis = &Block->DIGIS[i][0]; + *(EndofDigis -1) |= 1; // Set End of Address Bit + + if (i != 8) + memmove(EndofDigis, &Block->CTL, Len); // Include PID + + Len = Len + (i * 7) + 14; // Include Source, Dest and Digis + + Send_AX((PMESSAGE)Block, Len, Port); + + return; + +} + +static BOOL APRSReadConfigFile() +{ + char * Config; + char * ptr1, * ptr2; + + char buf[256],errbuf[256]; + + Config = PortConfig[34]; // Config fnom bpq32.cfg + + sprintf(StatusMsg, "BPQ32 Igate V %s", VersionString); // Set Default Status Message + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!APRSProcessLine(buf)) + { + WritetoConsole("APRS Bad config record "); + strcat(errbuf, "\r\n"); + WritetoConsole(errbuf); + } + } + return TRUE; + } + return FALSE; +} + +BOOL ConvertCalls(char * DigiCalls, UCHAR * AX, int * Lens) +{ + int Index = 0; + char * ptr; + char * Context; + UCHAR Work[MAXCALLS][7] = {0}; + int Len[MAXCALLS] = {0}; + + ptr = strtok_s(DigiCalls, ", ", &Context); + + while(ptr) + { + if (Index == MAXCALLS) return FALSE; + + ConvToAX25(ptr, &Work[Index][0]); + Len[Index++] = (int)strlen(ptr); + ptr = strtok_s(NULL, ", ", &Context); + } + + memcpy(AX, Work, sizeof(Work)); + memcpy(Lens, Len, sizeof(Len)); + return TRUE; +} + + + +static int APRSProcessLine(char * buf) +{ + char * ptr, * p_value; + + ptr = strtok(buf, "= \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + +// OBJECT PATH=APRS,WIDE1-1 PORT=1,IS INTERVAL=30 TEXT=;444.80TRF*111111z4807.60N/09610.63Wr%156 R15m + + if (_stricmp(ptr, "OBJECT") == 0) + { + char * p_Path, * p_Port, * p_Text; + int Interval; + struct OBJECT * Object; + int Digi = 2; + char * Context; + int SendTo; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "PATH")) + return FALSE; + + p_Path = strtok(NULL, "\t\n\r "); + if (p_Path == NULL) return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "PORT")) + return FALSE; + + p_Port = strtok(NULL, "\t\n\r "); + if (p_Port == NULL) return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "INTERVAL")) + return FALSE; + + p_value = strtok(NULL, " \t"); + if (p_value == NULL) return FALSE; + + Interval = atoi(p_value); + + if (Interval == 0) + return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "TEXT")) + return FALSE; + + p_Text = strtok(NULL, "\n\r"); + if (p_Text == NULL) return FALSE; + + Object = zalloc(sizeof(struct OBJECT)); + + if (Object == NULL) + return FALSE; + + Object->Next = ObjectList; + ObjectList = Object; + + if (Interval < 10) + Interval = 10; + + Object->Interval = Interval; + Object->Timer = (ObjectCount++) * 10 + 30; // Spread them out; + + // Convert Path to AX.25 + + ConvToAX25(APRSCall, &Object->Path[1][0]); + + ptr = strtok_s(p_Path, ",\t\n\r", &Context); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &Object->Path[0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &Object->Path[0][0]); + else + ConvToAX25(ptr, &Object->Path[0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &Object->Path[Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + Object->PathLen = Digi * 7; + + // Process Port List + + ptr = strtok_s(p_Port, ",", &Context); + + while (ptr) + { + SendTo = atoi(ptr); // this gives zero for IS + + if (SendTo > 32) + return FALSE; + + Object->PortMap[SendTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + if (strlen(p_Text) > 80) + p_Text[80] = 0; + + strcpy(Object->Message, p_Text); + return TRUE; + } + + if (_stricmp(ptr, "STATUSMSG") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + memcpy(StatusMsg, p_value, 128); // Just in case too long + StatusMsgLen = (int)strlen(p_value); + return TRUE; + } + + if (_stricmp(ptr, "WXFileName") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + strcpy(WXFileName, p_value); + SendWX = TRUE; + return TRUE; + } + if (_stricmp(ptr, "WXComment") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + + if (p_value == NULL) + return TRUE; + + if (strlen(p_value) > 79) + p_value[80] = 0; + + strcpy(WXComment, p_value); + return TRUE; + } + + + if (_stricmp(ptr, "ISFILTER") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + strcpy(ISFilter, p_value); + strcpy(NodeFilter, ISFilter); + return TRUE; + } + + if (_stricmp(ptr, "ReplaceDigiCalls") == 0) + { + TraceDigi = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "Multiple") == 0) + { + multiple = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SATGate") == 0) + { + SATGate = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "DISTKM") == 0) + { + DefaultDistKM = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LOCALTIME") == 0) + { + DefaultLocalTime = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LOGAPRSIS") == 0) + { + LogAPRSIS = TRUE; + return TRUE; + } + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return FALSE; + + if (_stricmp(ptr, "APRSCALL") == 0) + { + strcpy(APRSCall, p_value); + strcpy(LoppedAPRSCall, p_value); + memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging + + // Convert to ax.25 + + return ConvToAX25(APRSCall, AXCall); + } + + if (_stricmp(ptr, "WXCALL") == 0) + { + strcpy(WXCall, p_value); + return TRUE; + } + + if (_stricmp(ptr, "APRSPATH") == 0) + { + int Digi = 2; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + APRSPortMask |= 1 << (Port - 1); + + if (Context == NULL || Context[0] == 0) + return TRUE; // No dest - a receive-only port + + BeaconPath[Port] = _strdup(_strupr(Context)); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + if (ptr == NULL) + return FALSE; + + ConvToAX25(APRSCall, &BeaconHeader[Port][1][0]); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &BeaconHeader[Port][0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &BeaconHeader[Port][0][0]); + else + ConvToAX25(ptr, &BeaconHeader[Port][0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &BeaconHeader[Port][Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + BeaconHddrLen[Port] = Digi * 7; + + return TRUE; + } + + if (_stricmp(ptr, "GATEDPATH") == 0) + { + int Digi = 2; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + +// APRSPortMask |= 1 << (Port - 1); + + if (Context == NULL || Context[0] == 0) + return TRUE; // No dest - a receive-only port + + BeaconPath[Port] = _strdup(_strupr(Context)); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + if (ptr == NULL) + return FALSE; + + ConvToAX25(APRSCall, &GatedHeader[Port][1][0]); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &GatedHeader[Port][0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &GatedHeader[Port][0][0]); + else + ConvToAX25(ptr, &GatedHeader[Port][0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &GatedHeader[Port][Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + GatedHddrLen[Port] = Digi * 7; + + return TRUE; + } + + + if (_stricmp(ptr, "DIGIMAP") == 0) + { + int DigiTo; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + CrossPortMap[Port][Port] = FALSE; // Cancel Default mapping + CrossPortMap[Port][0] = FALSE; // Cancel Default APRSIS + + if (Context == NULL || Context[0] == 0) + return FALSE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); // this gives zero for IS + + if (DigiTo && GetPortTableEntryFromPortNum(DigiTo) == NULL) + return FALSE; + + CrossPortMap[Port][DigiTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return TRUE; + } + if (_stricmp(ptr, "BRIDGE") == 0) + { + int DigiTo; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + if (Context == NULL) + return FALSE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); // this gives zero for IS + + if (DigiTo > 32) + return FALSE; + + APRSBridgeMap[Port][DigiTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return TRUE; + } + + + if (_stricmp(ptr, "BeaconInterval") == 0) + { + BeaconInterval = atoi(p_value); + + if (BeaconInterval < 5) + BeaconInterval = 5; + + if (BeaconInterval) + BeaconCounter = 30; // Send first after 30 secs + + return TRUE; + } + + if (_stricmp(ptr, "MobileBeaconInterval") == 0) + { + MobileBeaconInterval = atoi(p_value) * 60; + return TRUE; + } + if (_stricmp(ptr, "MobileBeaconIntervalSecs") == 0) + { + MobileBeaconInterval = atoi(p_value); + if (MobileBeaconInterval < 10) + MobileBeaconInterval = 10; + + return TRUE; + } + + if (_stricmp(ptr, "BeacontoIS") == 0) + { + BeacontoIS = atoi(p_value); + return TRUE; + } + + + if (_stricmp(ptr, "TRACECALLS") == 0) + { + TraceCalls = _strdup(_strupr(p_value)); + ConvertCalls(TraceCalls, &TraceAX[0][0], &TraceLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "FLOODCALLS") == 0) + { + FloodCalls = _strdup(_strupr(p_value)); + ConvertCalls(FloodCalls, &FloodAX[0][0], &FloodLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "DIGICALLS") == 0) + { + char AllCalls[1024]; + + DigiCalls = _strdup(_strupr(p_value)); + strcpy(AllCalls, APRSCall); + strcat(AllCalls, ","); + strcat(AllCalls, DigiCalls); + ConvertCalls(AllCalls, &DigiAX[0][0], &DigiLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "MaxStations") == 0) + { + MaxStations = atoi(p_value); + + if (MaxStations > 10000) + MaxStations = 10000; + + return TRUE; + } + + if (_stricmp(ptr, "MaxAge") == 0) + { + ExpireTime = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSPort") == 0) + { + if (strcmp(p_value, "0") != 0) + strcpy(GPSPort, p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSSpeed") == 0) + { + GPSSpeed = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSRelay") == 0) + { + if (strlen(p_value) > 79) + return FALSE; + + strcpy(GPSRelay, p_value); + return TRUE; + } + + if (_stricmp(ptr, "BlueNMEA") == 0 || _stricmp(ptr, "TCPHost") == 0 || _stricmp(ptr, "AISHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(HostName, p_value); + return TRUE; + } + + if (_stricmp(ptr, "TCPPort") == 0 || _stricmp(ptr, "AISPort") == 0) + { + HostPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ADSBHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(ADSBHost, p_value); + return TRUE; + } + + if (_stricmp(ptr, "ADSBPort") == 0) + { + ADSBPort = atoi(p_value); + return TRUE; + } + + + + if (_stricmp(ptr, "GPSSetsLocator") == 0) + { + GPSSetsLocator = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "LAT") == 0) + { + if (strlen(p_value) != 8) + return FALSE; + + memcpy(LAT, _strupr(p_value), 8); + PosnSet = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LON") == 0) + { + if (strlen(p_value) != 9) + return FALSE; + + memcpy(LON, _strupr(p_value), 9); + PosnSet = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SYMBOL") == 0) + { + if (p_value[0] > ' ' && p_value[0] < 0x7f) + CFGSYMBOL = p_value[0]; + + return TRUE; + } + + if (_stricmp(ptr, "SYMSET") == 0) + { + CFGSYMSET = p_value[0]; + return TRUE; + } + + if (_stricmp(ptr, "MaxTraceHops") == 0) + { + MaxTraceHops = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxFloodHops") == 0) + { + MaxFloodHops = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ISHOST") == 0) + { + strncpy(ISHost, p_value, 250); + return TRUE; + } + + if (_stricmp(ptr, "ISPORT") == 0) + { + ISPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ISPASSCODE") == 0) + { + ISPasscode = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxDigisforIS") == 0) + { + MaxDigisforIS = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GateLocalDistance") == 0) + { + GateLocalDistance = atoi(p_value); + if (GateLocalDistance > 0.0) + GateLocal = TRUE; + + return TRUE; + } + + if (_stricmp(ptr, "WXInterval") == 0) + { + WXInterval = atoi(p_value); + WXCounter = (WXInterval - 1) * 60; + return TRUE; + } + + if (_stricmp(ptr, "WXPortList") == 0) + { + char ParamCopy[80]; + char * Context; + int Port; + char * ptr; + int index = 0; + + for (index = 0; index < 32; index++) + WXPort[index] = FALSE; + + if (strlen(p_value) > 79) + p_value[80] = 0; + + strcpy(ParamCopy, p_value); + + ptr = strtok_s(ParamCopy, " ,\t\n\r", &Context); + + while (ptr) + { + Port = atoi(ptr); // this gives zero for IS + + WXPort[Port] = TRUE; + + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + return TRUE; + } + + if (_stricmp(ptr, "Run") == 0) + { + strcpy(RunProgram, p_value); + return TRUE; + } + + // + // Bad line + // + return (FALSE); +} + +VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated); + + +VOID SendAPRSMessage(char * Message, int toPort) +{ + SendAPRSMessageEx(Message, toPort, APRSCall, 0); +} + +// Ex allows setting source call (For WX Messages) + + +VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated) +{ + int Port; + DIGIMESSAGE Msg; + + int Len; + + // toPort = -1 means all tadio ports. 0 = IS + + if (toPort == -1) + { + for (Port = 1; Port <= 32; Port++) + { + if (Gated && GatedHddrLen[Port]) + memcpy(Msg.DEST, &GatedHeader[Port][0][0], 10 * 7); + else if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + else + continue; + + ConvToAX25(FromCall, Msg.ORIGIN); + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Message); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + + return; + } + + if (toPort == 0 && APRSISOpen) + { + char ISMsg[300]; + + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", FromCall, APRSDest, Message); + ISSend(sock, ISMsg, Len, 0); + } + + if (toPort == 0) + return; + + if (Gated && GatedHddrLen[toPort]) + memcpy(Msg.DEST, &GatedHeader[toPort][0][0], 10 * 7); + else if (BeaconHddrLen[toPort]) // Only send to ports with a DEST defined + memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); + else + return; + + ConvToAX25(FromCall, Msg.ORIGIN); + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Message); + Send_AX_Datagram(&Msg, Len + 2, toPort); + + return; +} + + +VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis) +{ + if (_memicmp(Query, "APRSS", 5) == 0) + { + char Message[255]; + + sprintf(Message, ":%-9s:%s", Origin, StatusMsg); + SendAPRSMessage(Message, Port); + + return; + } + + if (_memicmp(Query, "APRST", 5) == 0 || _memicmp(Query, "PING?", 5) == 0) + { + // Trace Route + //:KH2ZV :?APRST :N8UR :KH2Z>APRS,DIGI1,WIDE*: + //:G8BPQ-14 :Path - G8BPQ-14>APU25N + + char Message[255]; + + sprintf(Message, ":%-9s:Path - %s>%s", Origin, Origin, DestPlusDigis); + SendAPRSMessage(Message, Port); + + return; + } +} + +VOID ProcessQuery(char * Query) +{ + if (memcmp(Query, "IGATE?", 6) == 0) + { + IStatusCounter = (rand() & 31) + 5; // 5 - 36 secs delay + return; + } + + if (memcmp(Query, "APRS?", 5) == 0) + { + BeaconCounter = (rand() & 31) + 5; // 5 - 36 secs delay + return; + } +} +Dll VOID APIENTRY APISendBeacon() +{ + BeaconCounter = 2; +} + +typedef struct _BeaconParams +{ + int toPort; + char * BeaconText; + BOOL SendStatus; + BOOL SendSOGCOG; +} Params; + + +Params BeaconParams; + +void SendBeaconThread(void * Params); + +VOID SendBeacon(int toPort, char * BeaconText, BOOL SendStatus, BOOL SendSOGCOG) +{ + // Send to IS if needed then start a thread to send to radio ports + + if (PosnSet == FALSE) + return; + + if (APRSISOpen && toPort == 0 && BeacontoIS) + { + char SOGCOG[10] = ""; + char ISMsg[300]; + int Len; + + Debugprintf("Sending APRS Beacon to APRS-IS"); + + if (SendSOGCOG | (COG != 0.0)) + sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); + + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, + (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + + ISSend(sock, ISMsg, Len, 0); + Debugprintf(">%s", ISMsg); + } + + BeaconParams.toPort = toPort; + BeaconParams.BeaconText = BeaconText; + BeaconParams.SendStatus = SendStatus; + BeaconParams.SendSOGCOG = SendSOGCOG; + + _beginthread(SendBeaconThread, 0, (VOID *) &BeaconParams); +} + +void SendBeaconThread(void * Param) +{ + // runs as a thread so we can sleep() between calls + + // Params are passed via a param block + + Params * BeaconParams = (Params *)Param; + + int toPort = BeaconParams->toPort; + char * BeaconText = BeaconParams->BeaconText; + BOOL SendStatus = BeaconParams->SendStatus; + BOOL SendSOGCOG = BeaconParams->SendSOGCOG; + + int Port; + DIGIMESSAGE Msg; + int Len; + char SOGCOG[10] = ""; + struct STATIONRECORD * Station; + struct PORTCONTROL * PORT; + + if (PosnSet == FALSE) + return; + + if (SendSOGCOG | (COG != 0.0)) + sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); + + BeaconCounter = BeaconInterval * 60; + + if (ISPort && IGateEnabled) + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + else + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + + Msg.PID = 0xf0; + Msg.CTL = 3; + + // Add to dup check list, so we won't digi it if we hear it back + // Should we drop it if we've sent it recently ?? + + if (CheckforDups(APRSCall, Msg.L2DATA, Len - (19 + sizeof(void *)))) + return; + + // Add to our station list + + Station = FindStation(APRSCall, TRUE); + + if (Station == NULL) + return; + + + strcpy(Station->Path, "APBPQ1"); + strcpy(Station->LastPacket, Msg.L2DATA); +// Station->LastPort = Port; + + DecodeAPRSPayload(Msg.L2DATA, Station); + Station->TimeLastUpdated = time(NULL); + + if (toPort) + { + if (BeaconHddrLen[toPort] == 0) + return; + + Debugprintf("Sending APRS Beacon to port %d", toPort); + + memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); // Clear unused digis + + GetSemaphore(&Semaphore, 12); + Send_AX_Datagram(&Msg, Len + 2, toPort); + FreeSemaphore(&Semaphore); + + return; + } + + for (Port = 1; Port <= 32; Port++) // Check all ports + { + if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + { + Debugprintf("Sending APRS Beacon to port %d", Port); + + if (ISPort && IGateEnabled) + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + else + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + Msg.PID = 0xf0; + Msg.CTL = 3; + + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + GetSemaphore(&Semaphore, 12); + Send_AX_Datagram(&Msg, Len + 2, Port); + FreeSemaphore(&Semaphore); + + // if Port has interlock set pause before next + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT && PORT->PORTINTERLOCK) + Sleep(20000); + } + } + return ; +} + +VOID SendObject(struct OBJECT * Object) +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + // Add to dup list in case we get it back + + CheckforDups(APRSCall, Object->Message, (int)strlen(Object->Message)); + + for (Port = 1; Port <= 32; Port++) + { + if (Object->PortMap[Port]) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Object->Message); + memcpy(Msg.DEST, &Object->Path[0][0], Object->PathLen + 1); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + } + + // Also send to APRS-IS if connected + + if (APRSISOpen && Object->PortMap[0]) + { + char ISMsg[300]; + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", APRSCall, APRSDest, Object->Message); + ISSend(sock, ISMsg, Len, 0); + } +} + + +/* +VOID SendStatus(char * StatusText) +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + if (APRSISOpen) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + + Len = sprintf(Msg.L2DATA, ">%s", StatusText); + + for (Port = 1; Port <= NUMBEROFPORTS; Port++) + { + if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + { + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + } + + Len = sprintf(Msg.L2DATA, "%s>%s,TCPIP*:>%s\r\n", APRSCall, APRSDest, StatusText); + ISSend(sock, Msg.L2DATA, Len, 0); +// Debugprintf(">%s", Msg.L2DATA); + } +} + + +*/ +VOID SendIStatus() +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + IStatusCounter = 3600; // One per hour + + if (APRSISOpen && BeacontoIS) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + + Len = sprintf(Msg.L2DATA, "%s,TCPIP*:%s", Msg.L2DATA); + } + +} + + +VOID DoSecTimer() +{ + struct OBJECT * Object = ObjectList; + + while (Object) + { + Object->Timer--; + + if (Object->Timer == 0) + { + Object->Timer = 60 * Object->Interval; + SendObject(Object); + } + Object = Object->Next; + } + + // Check SatGate Mode delay Q + + if (SatISQueue) + { + time_t NOW = time(NULL); + ISDELAY * SatISEntry = SatISQueue; + ISDELAY * Prev = NULL; + + while (SatISEntry) + { + if (SatISEntry->SendTIme < NOW) + { + // Send it + + ISSend(sock, SatISEntry->ISMSG, (int)strlen(SatISEntry->ISMSG), 0); + free(SatISEntry->ISMSG); + + if (Prev) + Prev->Next = SatISEntry->Next; + else + SatISQueue = SatISEntry->Next; + + free(SatISEntry); + return; // unlinkely to get 2 in sam esecond and doesn;t matter if we delay a bit more + } + + Prev = SatISEntry; + SatISEntry = SatISEntry->Next; + } + } + + if (ISPort && APRSISOpen == 0 && IGateEnabled) + { + ISDelayTimer++; + + if (ISDelayTimer > 60) + { + ISDelayTimer = 0; + _beginthread(APRSISThread, 0, (VOID *) TRUE); + } + } + + if (HostName[0]) + { + if (BlueNMEAOK == 0) + { + BlueNMEATimer++; + if (BlueNMEATimer > 15) + { + BlueNMEATimer = 0; + _beginthread(TCPConnect, 0, 0); + } + } + } + + + if (BeaconCounter) + { + BeaconCounter--; + + if (BeaconCounter == 0) + { + BeaconCounter = BeaconInterval * 60; + SendBeacon(0, StatusMsg, TRUE, FALSE); + } + } + + if (IStatusCounter) + { + IStatusCounter--; + + if (IStatusCounter == 0) + { + SendIStatus(); + } + } + + if (GPSOK) + { + GPSOK--; + + if (GPSOK == 0) +#ifdef LINBPQ + Debugprintf("GPS Lost"); +#else + SetDlgItemText(hConsWnd, IDC_GPS, "No GPS"); +#endif + } + + APRSSecTimer(); // Code from APRS APPL +} + +int CountPool() +{ + struct STATIONRECORD * ptr = StationRecordPool; + int n = 0; + + while (ptr) + { + n++; + ptr = ptr->Next; + } + return n; +} + +static VOID DoMinTimer() +{ + struct STATIONRECORD * ptr = *StationRecords; + struct STATIONRECORD * last = NULL; + time_t AgeLimit = time(NULL ) - (ExpireTime * 60); + int i = 0; + + // Remove old records + + while (ptr) + { + if (ptr->TimeLastUpdated < AgeLimit) + { + StationCount--; + + if (last) + { + last->Next = ptr->Next; + + // Put on front of free chain + + ptr->Next = StationRecordPool; + StationRecordPool = ptr; + + ptr = last->Next; + } + else + { + // First in list + + *StationRecords = ptr->Next; + + // Put on front of free chain + + ptr->Next = StationRecordPool; + StationRecordPool = ptr; + + if (*StationRecords) + { + ptr = *StationRecords; + } + else + { + ptr = NULL; + } + } + } + else + { + last = ptr; + ptr = ptr->Next; + } + } +} + +char APRSMsg[300]; + +int ISHostIndex = 0; +char RealISHost[256]; + +VOID APRSISThread(void * Report) +{ + // Receive from core server + + char Signon[500]; + unsigned char work[4]; + + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct addrinfo hints, *res = 0, *saveres; + size_t len; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + int InputLen = 1; // Non-zero + char errmsg[100]; + char * ptr; + size_t inptr = 0; + char APRSinMsg[1000]; + char PortString[20]; + char serv[256]; + + Debugprintf("BPQ32 APRS IS Thread"); +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Connecting"); +#endif + + if (ISFilter[0]) + sprintf(Signon, "user %s pass %d vers BPQ32 %s filter %s\r\n", + APRSCall, ISPasscode, TextVerstring, ISFilter); + else + sprintf(Signon, "user %s pass %d vers BPQ32 %s\r\n", + APRSCall, ISPasscode, TextVerstring); + + + sprintf(PortString, "%d", ISPort); + + // get host info, make socket, and connect it + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_STREAM; + getaddrinfo(ISHost, PortString, &hints, &res); + + InputLen = sprintf(errmsg, "Connecting to APRS Host %s\r\n", ISHost); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + if (!res) + { + err = WSAGetLastError(); + InputLen = sprintf(errmsg, "APRS IS Resolve %s Failed Error %d\r\n", ISHost, err); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + return; // Resolve failed + + } + + // Step thorough the list of hosts + + saveres = res; // Save for free + + if (res->ai_next) // More than one + { + int n = ISHostIndex; + + while (n && res->ai_next) + { + res = res->ai_next; + n--; + } + + if (n) + { + // We have run off the end of the list + + ISHostIndex = 0; // Back to start + res = saveres; + } + else + ISHostIndex++; + + } + + getnameinfo(res->ai_addr, (int)res->ai_addrlen, RealISHost, 256, serv, 256, 0); + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (sock == INVALID_SOCKET) + return; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + memcpy(work, res->ai_addr->sa_data, 4); + + Debugprintf("Trying APRSIS Host %d.%d.%d.%d (%d) %s", work[0], work[1], work[2], work[3], ISHostIndex, RealISHost); + + if (connect(sock, res->ai_addr, (int)res->ai_addrlen)) + { + err=WSAGetLastError(); + + // + // Connect failed + // + +#ifndef LINBPQ + MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connect Failed"); +#else + printf("APRS Igate connect failed\n"); +#endif + err=WSAGetLastError(); + InputLen = sprintf(errmsg, "Connect Failed %s af %d Error %d \r\n", RealISHost, res->ai_family, err); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + freeaddrinfo(res); + return; + } + + freeaddrinfo(saveres); + + APRSISOpen = TRUE; + +#ifndef LINBPQ + MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connected"); +#endif + + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } + + ISSend(sock, Signon, (int)strlen(Signon), 0); +/* + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } + + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } +*/ + while (InputLen > 0 && IGateEnabled) + { + InputLen = recv(sock, &APRSinMsg[inptr], (int)(500 - inptr), 0); + + if (InputLen > 0) + { + inptr += InputLen; + + ptr = memchr(APRSinMsg, 0x0a, inptr); + + while (ptr != NULL) + { + ptr++; // include lf + len = ptr-(char *)APRSinMsg; + + inptr -= len; // bytes left + + // UIView server has a null before crlf + + if (*(ptr - 3) == 0) + { + *(ptr - 3) = 13; + *(ptr - 2) = 10; + *(ptr - 1) = 0; + + len --; + } + + if (len > 10 && len < 300) // Ignore if way too long or too short + { + memcpy(&APRSMsg, APRSinMsg, len); + MonitorAPRSIS(APRSMsg, (int)len, FALSE); + if (APRSMsg[len - 2] == 13) + APRSMsg[len - 2] = 0; + else + APRSMsg[len - 1] = 0; + +// Debugprintf("%s", APRSMsg); + + ProcessAPRSISMsg(APRSMsg); + } + + if (inptr > 0) + { + memmove(APRSinMsg, ptr, inptr); + ptr = memchr(APRSinMsg, 0x0a, inptr); + } + else + ptr = 0; + + if (inptr < 0) + break; + } + } + } + + closesocket(sock); + + APRSISOpen = FALSE; + + Debugprintf("BPQ32 APRS IS Thread Exited"); + +#ifndef LINBPQ + if (IGateEnabled) + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disconnected"); + else + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disabled"); +#endif + ISDelayTimer = 30; // Retry pretty quickly + return; +} + +VOID ProcessAPRSISMsg(char * APRSMsg) +{ + char * Payload; + char * Source; + char * Dest; + char IGateCall[10] = " "; + char * ptr; + char Message[255]; + PAPRSHEARDRECORD MH; + time_t NOW = time(NULL); + char ISCopy[1024]; + struct STATIONRECORD * Station = NULL; +#ifdef WIN32 + struct _EXCEPTION_POINTERS exinfo; + char EXCEPTMSG[80] = ""; +#endif + + if (APRSMsg[0] == '#') // Comment + return; + + // if APRS Appl is atttached, queue message to it + + strcpy(ISCopy, APRSMsg); + + GetSemaphore(&Semaphore, 12); + +#ifdef WIN32 + + strcpy(EXCEPTMSG, "ProcessAPRSISMsg"); + + __try + { + + Station = DecodeAPRSISMsg(ISCopy); + + } + #include "StdExcept.c" + Debugprintf(APRSMsg); + } +#else + Station = DecodeAPRSISMsg(ISCopy); +#endif + + FreeSemaphore(&Semaphore); + +//}WB4APR-14>APRS,RELAY,TCPIP,G9RXG*::G3NRWVVVV:Hi Ian{001 +//KE7XO-2>hg,TCPIP*,qAC,T2USASW::G8BPQ-14 :Path - G8BPQ-14>APU25N +//IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data + + Payload = strchr(APRSMsg, ':'); + + // Get call of originating Igate + + ptr = Payload; + + if (Payload == NULL) + return; + + *(Payload++) = 0; + + while (ptr[0] != ',') + ptr--; + + ptr++; + + if (strlen(ptr) > 9) + return; + + memcpy(IGateCall, ptr, (int)strlen(ptr)); + + if (strstr(APRSMsg, ",qAS,") == 0) // Findu generates invalid q construct + { + MH = FindStationInMH(IGateCall); + if (MH) + { +// Debugprintf("Setting Igate Flag - %s:%s", APRSMsg, Payload); + MH->IGate = TRUE; // If we have seen this station on RF, set it as an Igate + } + } + Source = APRSMsg; + Dest = strchr(APRSMsg, '>'); + + if (Dest == NULL) + return; + + *(Dest++) = 0; // Termainate Source + ptr = strchr(Dest, ','); + + if (ptr) + *ptr = 0; + + MH = UpdateHeard(Source, 0); + + MH->Station = Station; + + // See if we should gate to RF. + + // Have we heard dest recently? (use the message dest (not ax.25 dest) - does this mean we only gate Messages? + // Not if it is an Igate (it will get a copy direct) + // Have we recently sent a message from this call - if so, we gate the next Position + +/* + + From http://www.aprs-is.net/IGateDetails.aspx + + Gate message packets and associated posits to RF if all of the following are true: + + the receiving station has been heard within range within a predefined time period (range defined + as digi hops, distance, or both). + + the sending station has not been heard via RF within a predefined time period (packets gated + from the Internet by other stations are excluded from this test). + + the sending station does not have TCPXX, NOGATE, or RFONLY in the header. + + the receiving station has not been heard via the Internet within a predefined time period. + + A station is said to be heard via the Internet if packets from the station contain TCPIP* or + TCPXX* in the header or if gated (3rd-party) packets are seen on RF gated by the station + and containing TCPIP or TCPXX in the 3rd-party header (in other words, the station is seen on RF + as being an IGate). + +*/ + + if (Payload[0] == ':') // Message + { + char MsgDest[10]; + APRSHEARDRECORD * STN; + + if (strlen(Payload) > 100) // I don't think any valid APRS msgs are more than this + return; + + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + if (strcmp(MsgDest, CallPadded) == 0) // to us + return; + + // Check that the sending station has not been heard via RF recently + + if (MH->rfPort && (NOW - MH->MHTIME) < GATETIMELIMIT) + return; + + STN = FindStationInMH(MsgDest); + + // Shouldn't we check DUP list, in case we have digi'ed this message directly? + + if (CheckforDups(Source, Payload, (int)strlen(Payload))) + return; + + // has the receiving station has been heard on RF and is not an IGate + + if (STN && STN->rfPort && !STN->IGate && (NOW - STN->MHTIME) < GATETIMELIMIT) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + + GetSemaphore(&Semaphore, 12); + SendAPRSMessageEx(Message, STN->rfPort, APRSCall, 1); // Set gated to IS flag + FreeSemaphore(&Semaphore); + + MessageCount++; + MH->LASTMSG = NOW; + + return; + } + } + + // Not a message. If it is a position report gate if have sent a message recently + + if (Payload[0] == '!' || Payload[0] == '/' || Payload[0] == '=' || Payload[0] == '@') // Posn Reports + { + if ((NOW - MH->LASTMSG) < 900 && MH->rfPort) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + + GetSemaphore(&Semaphore, 12); + SendAPRSMessageEx(Message, MH->rfPort, APRSCall, 1); // Set gated to IS flag + FreeSemaphore(&Semaphore); + + return; + } + } + + // If Gate Local to RF is defined, and station is in range, Gate it + + if (GateLocal && Station) + { + if (Station->Object) + Station = Station->Object; // If Object Report, base distance on Object, not station + + if (Station->Lat != 0.0 && Station->Lon != 0.0 && myDistance(Station->Lat, Station->Lon, 0) < GateLocalDistance) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + GetSemaphore(&Semaphore, 12); + SendAPRSMessage(Message, -1); // Send to all APRS Ports + FreeSemaphore(&Semaphore); + + return; + } + } +} + +APRSHEARDRECORD * FindStationInMH(char * Call) +{ + APRSHEARDRECORD * MH = MHDATA; + int i; + + // We keep call in ascii format, as that is what we get from APRS-IS, and we have it in that form + + for (i = 0; i < HEARDENTRIES; i++) + { + if (memcmp(Call, MH->MHCALL, 9) == 0) + return MH; + + MH++; + } + + return NULL; +} + +APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port) +{ + APRSHEARDRECORD * MH = MHDATA; + APRSHEARDRECORD * MHBASE = MH; + int i; + time_t NOW = time(NULL); + time_t OLDEST = NOW - MAXAGE; + char CallPadded[10] = " "; + BOOL SaveIGate = FALSE; + time_t SaveLastMsg = 0; + int SaveheardViaIS = 0; + + // We keep call in ascii format, space padded, as that is what we get from APRS-IS, and we have it in that form + + // Make Sure Space Padded + + memcpy(CallPadded, Call, (int)strlen(Call)); + + for (i = 0; i < MAXHEARDENTRIES; i++) + { + if (memcmp(CallPadded, MH->MHCALL, 10) == 0) + { + // if from APRS-IS, only update if record hasn't been heard via RF + + if (Port == 0) + MH->heardViaIS = 1; // Flag heard via IS + + if (Port == 0 && MH->rfPort) + return MH; // Don't update RF with IS + + if (Port == MH->rfPort) + { + SaveIGate = MH->IGate; + SaveLastMsg = MH->LASTMSG; + SaveheardViaIS = MH->heardViaIS; + goto DoMove; + } + } + + if (MH->MHCALL[0] == 0 || MH->MHTIME < OLDEST) // Spare entry + goto DoMove; + + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MAXHEARDENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(APRSHEARDRECORD)); + + if (i >= HEARDENTRIES) + { + char Status[80]; + + HEARDENTRIES = i + 1; + + sprintf(Status, "IGATE Stats: Msgs %d Local Stns %d", MessageCount , CountLocalStations()); +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IGATESTATS, Status); +#endif + } + + memcpy (MHBASE->MHCALL, CallPadded, 10); + MHBASE->rfPort = Port; + MHBASE->MHTIME = NOW; + MHBASE->IGate = SaveIGate; + MHBASE->LASTMSG = SaveLastMsg; + MHBASE->heardViaIS = SaveheardViaIS; + + return MHBASE; +} + +int CountLocalStations() +{ + APRSHEARDRECORD * MH = MHDATA; + int i, n = 0; + + for (i = 0; i < HEARDENTRIES; i++) + { + if (MH->rfPort) // DOn't count IS Stations + n++; + + MH++; + } + return n; +} + + +BOOL CheckforDups(char * Call, char * Msg, int Len) +{ + // Primitive duplicate suppression - see if same call and text reeived in last few seconds + + time_t Now = time(NULL); + time_t DupCheck = Now - DUPSECONDS; + int i, saveindex = -1; + char * ptr1; + + if (Len < 1) + return TRUE; + + for (i = 0; i < MAXDUPS; i++) + { + if (DupInfo[i].DupTime < DupCheck) + { + // too old - use first if we need to save it + + if (saveindex == -1) + { + saveindex = i; + } + + if (DupInfo[i].DupTime == 0) // Off end of used area + break; + + continue; + } + + if ((Len == DupInfo[i].DupLen || (DupInfo[i].DupLen == 99 && Len > 99)) && memcmp(Call, DupInfo[i].DupUser, 7) == 0 && (memcmp(Msg, DupInfo[i].DupText, DupInfo[i].DupLen) == 0)) + { + // Duplicate, so discard + + Msg[Len] = 0; + ptr1 = strchr(Msg, 13); + if (ptr1) + *ptr1 = 0; + +// Debugprintf("Duplicate Message supressed %s", Msg); + return TRUE; // Duplicate + } + } + + // Not in list + + if (saveindex == -1) // List is full + saveindex = MAXDUPS - 1; // Stick on end + + DupInfo[saveindex].DupTime = Now; + memcpy(DupInfo[saveindex].DupUser, Call, 7); + + if (Len > 99) Len = 99; + + DupInfo[saveindex].DupLen = Len; + memcpy(DupInfo[saveindex].DupText, Msg, Len); + + return FALSE; +} + +char * FormatAPRSMH(APRSHEARDRECORD * MH) + { + // Called from CMD.ASM + + struct tm * TM; + static char MHLine[50]; + time_t szClock = MH->MHTIME; + + szClock = (time(NULL) - szClock); + TM = gmtime(&szClock); + + sprintf(MHLine, "%-10s %d %.2d:%.2d:%.2d:%.2d %s\r", + MH->MHCALL, MH->rfPort, TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, (MH->IGate) ? "IGATE" : ""); + + return MHLine; + } + +// GPS Handling Code + +void SelectSource(BOOL Recovering); +void DecodeRMC(char * msg, size_t len); + +void PollGPSIn(); + + +UINT GPSType = 0xffff; // Source of Postion info - 1 = Phillips 2 = AIT1000. ffff = not posn message + +int RecoveryTimer; // Serial Port recovery + +double PI = 3.1415926535; +double P2 = 3.1415926535 / 180; + +double Latitude, Longtitude, SOG, COG, LatIncrement, LongIncrement; +double LastSOG = -1.0; + +BOOL Check0183CheckSum(char * msg, size_t len) +{ + BOOL retcode=TRUE; + char * ptr; + UCHAR sum,xsum1,xsum2; + + sum=0; + ptr=++msg; // Skip $ + +loop: + + if (*(ptr)=='*') goto eom; + + sum ^=*(ptr++); + + len--; + + if (len > 0) goto loop; + + return TRUE; // No Checksum + +eom: + _strupr(ptr); + + xsum1=*(++ptr); + xsum1-=0x30; + if (xsum1 > 9) xsum1-=7; + + xsum2=*(++ptr); + xsum2-=0x30; + if (xsum2 > 9) xsum2-=7; + + xsum1=xsum1<<4; + xsum1+=xsum2; + + return (xsum1==sum); +} + +BOOL OpenGPSPort() +{ + struct PortInfo * portptr = &InPorts[0]; + + // open COMM device + + if (strlen(GPSPort) < 4) + { + int port = atoi(GPSPort); +#ifdef WIN32 + sprintf(GPSPort, "COM%d", port); +#else + sprintf(GPSPort, "com%d", port); +#endif + } + + portptr->hDevice = OpenCOMPort(GPSPort, GPSSpeed, TRUE, TRUE, FALSE, 0); + + if (portptr->hDevice == 0) + { + return FALSE; + } + + return TRUE; +} + +void PollGPSIn() +{ + size_t len; + char GPSMsg[2000] = "$GPRMC,061213.000,A,5151.5021,N,00056.8388,E,0.15,324.11,190414,,,A*6F"; + char * ptr; + struct PortInfo * portptr; + + portptr = &InPorts[0]; + + if (!portptr->hDevice) + return; + + getgpsin: + +// Comm Error - probably lost USB Port. Try closing and reopening after a delay + +// if (RecoveryTimer == 0) +// { +// RecoveryTimer = 100; // 10 Secs +// return; +// } +// } + + if (portptr->gpsinptr == 160) + portptr->gpsinptr = 0; + + len = ReadCOMBlock(portptr->hDevice, &portptr->GPSinMsg[portptr->gpsinptr], + 160 - portptr->gpsinptr); + + if (len > 0) + { + portptr->gpsinptr += (int)len; + + ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); + + while (ptr != NULL) + { + ptr++; // include lf + len=ptr-(char *)&portptr->GPSinMsg; + memcpy(&GPSMsg,portptr->GPSinMsg,len); + + GPSMsg[len] = 0; + + if (Check0183CheckSum(GPSMsg, len)) + if (memcmp(&GPSMsg[1], "GPRMC", 5) == 0) + DecodeRMC(GPSMsg, len); + + portptr->gpsinptr -= (int)len; // bytes left + + if (portptr->gpsinptr > 0 && *ptr == 0) + { + *ptr++; + portptr->gpsinptr--; + } + + if (portptr->gpsinptr > 0) + { + memmove(portptr->GPSinMsg,ptr, portptr->gpsinptr); + ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); + } + else + ptr=0; + } + + goto getgpsin; + } + return; +} + + +void ClosePorts() +{ + if (InPorts[0].hDevice) + { + CloseCOMPort(InPorts[0].hDevice); + InPorts[0].hDevice=0; + } + + return; +} + +void DecodeRMC(char * msg, size_t len) +{ + char * ptr1; + char * ptr2; + char TimHH[3], TimMM[3], TimSS[3]; + char OurSog[5], OurCog[4]; + char LatDeg[3], LonDeg[4]; + char NewLat[10] = "", NewLon[10] = ""; + struct STATIONRECORD * Stn1; + + char Day[3]; + + ptr1 = &msg[7]; + + len-=7; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(TimHH,ptr1,2); + memcpy(TimMM,ptr1+2,2); + memcpy(TimSS,ptr1+4,2); + TimHH[2]=0; + TimMM[2]=0; + TimSS[2]=0; + + ptr1=ptr2; + + if (*(ptr1) != 'A') // ' Data Not Valid + { +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IDC_GPS, "No GPS Fix"); +#endif + return; + } + + ptr1+=2; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(NewLat, ptr1, 7); + memcpy(LatDeg, ptr1, 2); + LatDeg[2]=0; + Lat=atof(LatDeg) + (atof(ptr1+2)/60); + + if (*(ptr1+7) > '4') if (NewLat[6] < '9') NewLat[6]++; + + ptr1=ptr2; + + NewLat[7] = (*ptr1); + if ((*ptr1) == 'S') Lat=-Lat; + + ptr1+=2; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + *(ptr2++)=0; + + memcpy(NewLon, ptr1, 8); + + memcpy(LonDeg,ptr1,3); + LonDeg[3]=0; + Lon=atof(LonDeg) + (atof(ptr1+3)/60); + + if (*(ptr1+8) > '4') if (NewLon[7] < '9') NewLon[7]++; + + ptr1=ptr2; + + NewLon[8] = (*ptr1); + if ((*ptr1) == 'W') Lon=-Lon; + + // Now have a valid posn, so stop sending Undefined LOC Sysbol + + SYMBOL = CFGSYMBOL; + SYMSET = CFGSYMSET; + + PosnSet = TRUE; + + Stn1 = (struct STATIONRECORD *)StnRecordBase; // Pass to App + Stn1->Lat = Lat; + Stn1->Lon = Lon; + + if (GPSOK == 0) + { +#ifdef LINBPQ + Debugprintf("GPS OK"); +#else + SetDlgItemText(hConsWnd, IDC_GPS, "GPS OK"); +#endif + } + + GPSOK = 30; + + ptr1+=2; + + ptr2 = (char *)memchr(ptr1,',',30); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(OurSog, ptr1, 4); + OurSog[4] = 0; + + ptr1=ptr2; + + ptr2 = (char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(OurCog, ptr1, 3); + OurCog[3] = 0; + + memcpy(Day,ptr2,2); + Day[2]=0; + + SOG = atof(OurSog); + COG = atof(OurCog); + + if (strcmp(NewLat, LAT) || strcmp(NewLon, LON)) + { + if (MobileBeaconInterval) + { + time_t NOW = time(NULL); + + if ((NOW - LastMobileBeacon) > MobileBeaconInterval) + { + LastMobileBeacon = NOW; + SendBeacon(0, StatusMsg, FALSE, TRUE); + } + } + if (GPSSetsLocator) + { + ToLOC(Lat, Lon, LOC); + sprintf(LOCATOR, "%f:%f", Lat, Lon); + } + } + + strcpy(LAT, NewLat); + strcpy(LON, NewLon); +} + +Dll VOID APIENTRY APRSConnect(char * Call, char * Filter) +{ + // Request APRS Data from Switch (called by APRS Applications) + + APRSApplConnected = TRUE; + APRSWeb = TRUE; + + strcpy(APPLFilter, Filter); + + if (APPLFilter[0]) + { + // This is called in APPL context so must queue the message + + char Msg[2000]; + PMSGWITHLEN buffptr; + + sprintf(Msg, "filter %s", Filter); + + if (strlen(Msg) > 240) + Msg[240] = 0; + + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], Msg); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], "filter?"); + C_Q_ADD(&APPLTX_Q, buffptr); + } + FreeSemaphore(&Semaphore); + } + strcpy(Call, CallPadded); +} + +Dll VOID APIENTRY APRSDisconnect() +{ + // Stop requesting APRS Data from Switch (called by APRS Applications) + + char Msg[2000]; + PMSGWITHLEN buffptr; + + strcpy(ISFilter, NodeFilter); + sprintf(Msg, "filter %s", NodeFilter); + + APRSApplConnected = FALSE; + APRSWeb = FALSE; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], Msg); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], "filter?"); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + while (APPL_Q) + { + buffptr = Q_REM(&APPL_Q); + ReleaseBuffer(buffptr); + } + + FreeSemaphore(&Semaphore); +} + + +Dll char * APIENTRY APRSGetStatusMsgPtr() +{ + return StatusMsg; +} + + + +Dll BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call) +{ + // Request APRS Data from Switch (called by APRS Applications) + + void ** buffptr; +#ifdef bpq32 + struct _EXCEPTION_POINTERS exinfo; +#endif + + GetSemaphore(&Semaphore, 10); + { + if (APPL_Q) + { + buffptr = Q_REM(&APPL_Q); + + memcpy(Call, (char *)&buffptr[2], 12); + strcpy(Frame, (char *)&buffptr[5]); + + ReleaseBuffer(buffptr); + FreeSemaphore(&Semaphore); + return TRUE; + } + } + + FreeSemaphore(&Semaphore); + + return FALSE; +} + +Dll BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port) +{ + // Called from BPQAPRS App + // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) + + PMSGWITHLEN buffptr; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = ++Len; // Len doesn't include Null + memcpy(&buffptr->Data[0], Frame, Len); + C_Q_ADD(&APPLTX_Q, buffptr); + } + +// buffptr-> = Port; // Pass to SendAPRSMessage(); + + FreeSemaphore(&Semaphore); + + return TRUE; +} + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall) +{ + // Called from BPQAPRS App or BPQMail + // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) + + PMSGWITHLEN buffptr; + + if (APRSActive == 0) + return FALSE; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + memcpy(&buffptr->Data[0], ToCall, 9); + buffptr->Data[9] = 0; + strcpy(&buffptr->Data[10], Text); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + FreeSemaphore(&Semaphore); + + return TRUE; +} + +Dll BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon) +{ + *PLat = Lat; + *PLon = Lon; + + return GPSOK; +} + +Dll BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon) +{ + strcpy(PLat, LAT); + strcpy(PLon, LON); + + return GPSOK; +} + +// Code to support getting GPS from Andriod Device running BlueNMEA + + +#define SD_BOTH 0x02 + +static VOID ProcessReceivedData(SOCKET TCPSock) +{ + char UDPMsg[8192]; + char Buffer[65536]; + + int len = recv(TCPSock, Buffer, 65500, 0); + + char * ptr; + char * Lastptr; + + if (len <= 0) + { + closesocket(TCPSock); + BlueNMEAOK = FALSE; + return; + } + + ptr = Lastptr = Buffer; + Buffer[len] = 0; + + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr -1; + + if (Len > 8100) + return; + + memcpy(UDPMsg, Lastptr, Len); + UDPMsg[Len++] = 13; + UDPMsg[Len++] = 10; + UDPMsg[Len] = 0; + + if (!Check0183CheckSum(UDPMsg, Len)) + { + Debugprintf("Checksum Error %s", UDPMsg); + } + else + { + if (memcmp(UDPMsg, "$GPRMC", 6) == 0) + DecodeRMC(UDPMsg, Len); + + else if (memcmp(UDPMsg, "!AIVDM", 6) == 0) + ProcessAISMessage(UDPMsg, Len); + + } + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } +} + +static VOID TCPConnect(void * unused) +{ + int err, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + struct sockaddr_in destaddr; + SOCKET TCPSock; + + if (HostName[0] == 0) + return; + + destaddr.sin_addr.s_addr = inet_addr(HostName); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(HostPort); + + TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TCPSock == INVALID_SOCKET) + { + return; + } + + setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + BlueNMEAOK = TRUE; // So we don't try to reconnect while waiting + + if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ioctl(TCPSock, FIONBIO, ¶m); + } + else + { + err=WSAGetLastError(); +#ifdef LINBPQ + printf("Connect Failed for AIS socket - error code = %d\n", err); +#else + Debugprintf("Connect Failed for AIS socket - error code = %d", err); +#endif + closesocket(TCPSock); + BlueNMEAOK = FALSE; + + return; + } + + BlueNMEAOK = TRUE; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TCPSock,&readfs); + FD_SET(TCPSock,&errorfs); + + timeout.tv_sec = 900; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TCPSock, &readfs)) + { + ProcessReceivedData(TCPSock); + } + + if (FD_ISSET(TCPSock, &errorfs)) + { +Lost: +#ifdef LINBPQ + printf("AIS Connection lost\n"); +#endif + closesocket(TCPSock); + BlueNMEAOK = FALSE;; + return; + } + } + else + { + // 15 mins without data. Shouldn't happen + + shutdown(TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TCPSock); + BlueNMEAOK = FALSE; + return; + } + } +} +// Code Moved from APRS Application + +// +// APRS Mapping and Messaging App for BPQ32 Switch. +// + + +VOID APIENTRY APRSConnect(char * Call, char * Filter); +VOID APIENTRY APRSDisconnect(); +BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call); +BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port); +BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); +BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); +BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); +VOID APIENTRY APISendBeacon(); + + +int NewLine(HWND hWnd); +VOID ProcessBuff(HWND hWnd, MESSAGE * buff,int len,int stamp); +int TogglePort(HWND hWnd, int Item, int mask); +VOID SendFrame(UCHAR * buff, int txlen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int KissDecode(UCHAR * inbuff, int len); +//void UpdateStation(char * Call, char * Path, char * Comment, double V_Lat, double V_Lon, double V_SOG, double V_COG, int iconRow, int iconCol); +VOID FindStationsByPixel(int MouseX, int MouseY); +void RefreshStation(struct STATIONRECORD * ptr); +void RefreshStationList(); +void RefreshStationMap(); +BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station); +VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station); +BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y); +VOID APRSPoll(); +VOID OSMThread(); +VOID ResolveThread(); +VOID RefreshTile(char * FN, int Zoom, int x, int y); +int ProcessMessage(char * Payload, struct STATIONRECORD * Station); +VOID APRSSecTimer(); +double myBearing(double laa, double loa); + +BOOL CreatePipeThread(); + +VOID SendWeatherBeacon(); +VOID DecodeWXPortList(); + + +VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) +{ + UCHAR * ptr = strchr(WX, '_'); + char Type; + int Val; + + if (ptr == 0) + return; + + sockptr->WindDirn = atoi(++ptr); + ptr += 4; + sockptr->WindSpeed = atoi(ptr); + ptr += 3; +WXLoop: + + Type = *(ptr++); + + if (*ptr =='.') // Missing Value + { + while (*ptr == '.') + ptr++; + + goto WXLoop; + } + + Val = atoi(ptr); + + switch (Type) + { + case 'c': // = wind direction (in degrees). + + sockptr->WindDirn = Val; + break; + + case 's': // = sustained one-minute wind speed (in mph). + + sockptr->WindSpeed = Val; + break; + + case 'g': // = gust (peak wind speed in mph in the last 5 minutes). + + sockptr->WindGust = Val; + break; + + case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. + + sockptr->Temp = Val; + break; + + case 'r': // = rainfall (in hundredths of an inch) in the last hour. + + sockptr->RainLastHour = Val; + break; + + case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. + + sockptr->RainLastDay = Val; + break; + + case 'P': // = rainfall (in hundredths of an inch) since midnight. + + sockptr->RainToday = Val; + break; + + case 'h': // = humidity (in %. 00 = 100%). + + sockptr->Humidity = Val; + break; + + case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). + + sockptr->Pressure = Val; + break; + + default: + + return; + } + while(isdigit(*ptr)) + { + ptr++; + } + + if (*ptr != ' ') + goto WXLoop; +} + +static char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; +//char Header[] = "Accept: */*\r\nHost: tile.openstreetmap.org\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; + +char APRSMsg[300]; + +Dll struct STATIONRECORD * APIENTRY APPLFindStation(char * Call, BOOL AddIfNotFount) +{ + // Called from APRS Appl + + struct STATIONRECORD * Stn; + + GetSemaphore(&Semaphore, 12); + Stn = FindStation(Call, AddIfNotFount) ; + FreeSemaphore(&Semaphore); + + return Stn; +} + +Dll struct APRSMESSAGE * APIENTRY APRSGetMessageBuffer() +{ + struct APRSMESSAGE * ptr = MessageRecordPool; + + if (ptr) + { + MessageRecordPool = ptr->Next; // Unchain + MessageCount++; + + ptr->Next = NULL; + + memset(ptr, 0, sizeof(struct APRSMESSAGE)); + } + + return ptr; +} + + +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) +{ + int i = 0; + struct STATIONRECORD * find; + struct STATIONRECORD * ptr; + struct STATIONRECORD * last = NULL; + int sum = 0; + + if (APRSActive == 0 || StationRecords == 0) + return FALSE; + + if (strlen(Call) > 9) + { + Debugprintf("APRS Call too long %s", Call); + Call[9] = 0; + } + + find = *StationRecords; + while(find) + { + if (strlen(find->Callsign) > 9) + { + Debugprintf("APRS Call in Station List too long %s", find->Callsign); + find->Callsign[9] = 0; + } + + if (strcmp(find->Callsign, Call) == 0) + return find; + + last = find; + find = find->Next; + i++; + } + + // Not found - add on end + + if (AddIfNotFount) + { + // Get first from station record pool + + ptr = StationRecordPool; + + if (ptr) + { + StationRecordPool = ptr->Next; // Unchain + StationCount++; + } + else + { + // Get First from Stations + + ptr = *StationRecords; + + if (ptr) + *StationRecords = ptr->Next; + } + + if (ptr == NULL) + return NULL; + + memset(ptr, 0, sizeof(struct STATIONRECORD)); + +// EnterCriticalSection(&Crit); + + if (*StationRecords == NULL) + *StationRecords = ptr; + else + last->Next = ptr; + +// LeaveCriticalSection(&Crit); + + // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); + + strcpy(ptr->Callsign, Call); + ptr->TimeLastUpdated = ptr->TimeAdded = time(NULL); + ptr->Index = i; + ptr->NoTracks = DefaultNoTracks; + + for (i = 0; i < 9; i++) + sum += Call[i]; + + sum %= 20; + + ptr->TrackColour = sum; + ptr->Moved = TRUE; + + return ptr; + } + else + return NULL; +} + +struct STATIONRECORD * ProcessRFFrame(char * Msg, int len, int * ourMessage) +{ + char * Payload; + char * Path = NULL; + char * Comment = NULL; + char * Callsign; + char * ptr; + int Port = 0; + + struct STATIONRECORD * Station = NULL; + + Msg[len - 1] = 0; + +// Debugprintf("RF Frame %s", Msg); + + Msg += 10; // Skip Timestamp + + Payload = strchr(Msg, ':'); // End of Address String + + if (Payload == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + ptr = strstr(Msg, "Port="); + + if (ptr) + Port = atoi(&ptr[5]); + + Payload++; + + if (*Payload != 0x0d) + return Station; + + *Payload++ = 0; + + Callsign = Msg; + + Path = strchr(Msg, '>'); + + if (Path == NULL) + { + Debugprintf("Invalid Header %s", Msg); + return Station; + } + + *Path++ = 0; + + ptr = strchr(Path, ' '); + + if (ptr) + *ptr = 0; + + // Look up station - create a new one if not found + + if (strcmp(Callsign, "AIS") == 0) + { + if (needAIS) + { + Payload += 3; + ProcessAISMessage(Payload, strlen(Payload)); + } + else + Debugprintf(Payload); + + return 0; + } + + Station = FindStation(Callsign, TRUE); + + strcpy(Station->Path, Path); + strcpy(Station->LastPacket, Payload); + Station->LastPort = Port; + + *ourMessage = DecodeAPRSPayload(Payload, Station); + Station->TimeLastUpdated = time(NULL); + + return Station; +} + + +/* +2E0AYY>APU25N,TCPIP*,qAC,AHUBSWE2:=5105.18N/00108.19E-Paul in Folkestone Kent {UIV32N} +G0AVP-12>APT310,MB7UC*,WIDE3-2,qAR,G3PWJ:!5047.19N\00108.45Wk074/000/Paul mobile +G0CJM-12>CQ,TCPIP*,qAC,AHUBSWE2:=/3&Rio94sg +M0HFC>APRS,WIDE2-1,qAR,G0MNI:!5342.83N/00013.79W# Humber Fortress ARC Look us up on QRZ +G8WVW-3>APTT4,WIDE1-1,WIDE2-1,qAS,G8WVW:T#063,123,036,000,000,000,00000000 +*/ + + +struct STATIONRECORD * DecodeAPRSISMsg(char * Msg) +{ + char * Payload; + char * Path = NULL; + char * Comment = NULL; + char * Callsign; + struct STATIONRECORD * Station = NULL; + +// Debugprintf(Msg); + + Payload = strchr(Msg, ':'); // End of Address String + + if (Payload == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + *Payload++ = 0; + + Callsign = Msg; + + Path = strchr(Msg, '>'); + + if (Path == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + *Path++ = 0; + + // Look up station - create a new one if not found + + if (strlen(Callsign) > 11) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + Station = FindStation(Callsign, TRUE); + + strcpy(Station->Path, Path); + strcpy(Station->LastPacket, Payload); + Station->LastPort = 0; + + DecodeAPRSPayload(Payload, Station); + Station->TimeLastUpdated = time(NULL); + + return Station; +} + +double Cube91 = 91.0 * 91.0 * 91.0; +double Square91 = 91.0 * 91.0; + +BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station) +{ + UCHAR SymChar; + char SymSet; + char NS; + char EW; + double NewLat, NewLon; + char LatDeg[3], LonDeg[4]; + char save; + + // Compressed has first character not a digit (it is symbol table) + + // /YYYYXXXX$csT + + if (Payload[0] == '!') + return FALSE; // Ultimeter 2000 Weather Station + + if (!isdigit(*Payload)) + { + int C, S; + + SymSet = *Payload; + SymChar = Payload[9]; + + NewLat = 90.0 - ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 380926.0; + + Payload += 4; + + NewLon = -180.0 + ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 190463.0; + + C = Payload[6] - 33; + + if (C >= 0 && C < 90 ) + { + S = Payload[7] - 33; + + Station->Course = C * 4; + Station->Speed = (pow(1.08, S) - 1) * 1.15077945; // MPH; + } + + + + } + else + { + // Standard format ddmm.mmN/dddmm.mmE? + + NS = Payload[7] & 0xdf; // Mask Lower Case Bit + EW = Payload[17] & 0xdf; + + SymSet = Payload[8]; + SymChar = Payload[18]; + + if (SymChar == '_') // WX + { + if (strlen(Payload) > 50) + strcpy(Station->LastWXPacket, Payload); + } + + memcpy(LatDeg, Payload,2); + LatDeg[2]=0; + NewLat = atof(LatDeg) + (atof(Payload+2) / 60); + + if (NS == 'S') + NewLat = -NewLat; + else + if (NS != 'N') + return FALSE; + + memcpy(LonDeg,Payload + 9, 3); + + if (SymChar != '_' && Payload[22] == '/') // not if WX + { + Station->Course = atoi(Payload + 19); + Station->Speed = atoi(Payload + 23); + } + + LonDeg[3]=0; + + save = Payload[17]; + Payload[17] = 0; + NewLon = atof(LonDeg) + (atof(Payload+12) / 60); + Payload[17] = save; + + if (EW == 'W') + NewLon = -NewLon; + else + if (EW != 'E') + return FALSE; + } + + Station->Symbol = SymChar; + + if (SymChar > ' ' && SymChar < 0x7f) + SymChar -= '!'; + else + SymChar = 0; + + Station->IconOverlay = 0; + + if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) + { + SymChar += 96; + Station->IconOverlay = SymSet; + } + else + if (SymSet == '\\') + SymChar += 96; + + Station->iconRow = SymChar >> 4; + Station->iconCol = SymChar & 15; + + if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) + return TRUE; + + if (Station->Lat != NewLat || Station->Lon != NewLon) + { + time_t NOW = time(NULL); + time_t Age = NOW - Station->TimeLastTracked; + + if (Age > 15) // Don't update too often + { + // Add to track + + Station->TimeLastTracked = NOW; + +// if (memcmp(Station->Callsign, "ISS ", 4) == 0) +// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); + + Station->LatTrack[Station->Trackptr] = NewLat; + Station->LonTrack[Station->Trackptr] = NewLon; + Station->TrackTime[Station->Trackptr] = NOW; + + Station->Trackptr++; + Station->Moved = TRUE; + + if (Station->Trackptr == TRACKPOINTS) + Station->Trackptr = 0; + } + + Station->Lat = NewLat; + Station->Lon = NewLon; + Station->Approx = 0; + } + + + return TRUE; +} + +int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station) +{ + char * TimeStamp; + char * ObjName; + char ObjState; + struct STATIONRECORD * Object; + BOOL Item = FALSE; + char * ptr; + char * Callsign; + char * Path; + char * Msg; + char * context; + struct STATIONRECORD * TPStation; + + Station->Object = NULL; + + switch(*Payload) + { + case '`': + case 0x27: // ' + case 0x1c: + case 0x1d: // MIC-E + + Decode_MIC_E_Packet(Payload, Station); + return 0; + + case '$': // NMEA + Debugprintf(Payload); + break; + + case ')': // Item + +// Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); + + Item = TRUE; + ObjName = ptr = Payload + 1; + + while (TRUE) + { + ObjState = *ptr; + if (ObjState == 0) + return 0; // Corrupt + + if (ObjState == '!' || ObjState == '_') // Item Terminator + break; + + ptr++; + } + + *ptr = 0; // Terminate Name + + Object = FindStation(ObjName, TRUE); + Object->ObjState = *ptr++ = ObjState; + + strcpy(Object->Path, Station->Callsign); + strcat(Object->Path, ">"); + if (Object == Station) + { + char Temp[256]; + strcpy(Temp, Station->Path); + strcat(Object->Path, Temp); + Debugprintf("item is station %s", Payload); + } + else + strcat(Object->Path, Station->Path); + + strcpy(Object->LastPacket, Payload); + + if (ObjState != '_') // Deleted Objects may have odd positions + DecodeLocationString(ptr, Object); + + Object->TimeLastUpdated = time(NULL); + Station->Object = Object; + return 0; + + + case ';': // Object + + ObjName = Payload + 1; + ObjState = Payload[10]; // * Live, _Killed + + Payload[10] = 0; + Object = FindStation(ObjName, TRUE); + Object->ObjState = Payload[10] = ObjState; + + strcpy(Object->Path, Station->Callsign); + strcat(Object->Path, ">"); + if (Object == Station) + { + char Temp[256]; + strcpy(Temp, Station->Path); + strcat(Object->Path, Temp); + Debugprintf("Object is station %s", Payload); + } + else + strcat(Object->Path, Station->Path); + + + strcpy(Object->LastPacket, Payload); + + TimeStamp = Payload + 11; + + if (ObjState != '_') // Deleted Objects may have odd positions + DecodeLocationString(Payload + 18, Object); + + Object->TimeLastUpdated = time(NULL); + Station->Object = Object; + return 0; + + case '@': + case '/': // Timestamp, No Messaging + + TimeStamp = ++Payload; + Payload += 6; + + case '=': + case '!': + + Payload++; + + DecodeLocationString(Payload, Station); + + return 0; + + case '>': // Status + + strcpy(Station->Status, &Payload[1]); + + case '<': // Capabilities + case '_': // Weather + case 'T': // Telemetry + + break; + + case ':': // Message + + return ProcessMessage(Payload, Station); + + case '}': // Third Party Header + + // Process Payload as a new message + + // }GM7HHB-9>APDR12,TCPIP,MM1AVR*:=5556.62N/00303.55W>204/000/A=000213 http://www.dstartv.com + + Callsign = Msg = &Payload[1]; + Path = strchr(Msg, '>'); + + if (Path == NULL) + return 0; + + *Path++ = 0; + + Payload = strchr(Path, ':'); + + if (Payload == NULL) + return 0; + + *(Payload++) = 0; + + // Check Dup Filter + + if (CheckforDups(Callsign, Payload, (int)strlen(Payload))) + return 0; + + // Look up station - create a new one if not found + + TPStation = FindStation(Callsign, TRUE); + + strcpy(TPStation->Path, Path); + strcpy(TPStation->LastPacket, Payload); + TPStation->LastPort = 0; // Heard on RF, but info is from IS + + DecodeAPRSPayload(Payload, TPStation); + TPStation->TimeLastUpdated = time(NULL); + + return 0; + + default: + + // Non - APRS Message. If Payload contains a valid 6 char locator derive a position from it + + if (Station->Lat != 0.0 || Station->Lon != 0.0) + return 0; // already have position + + ptr = strtok_s(Payload, ",[](){} \n", &context); + + while (ptr && ptr[0]) + { + if (strlen(ptr) == 6) // could be locator + { + double Lat = 0.0, Lon = 0.0; + + if (FromLOC(ptr, &Lat, &Lon)) + { + if (Lat != 0.0 && Lon != 0.0) + { + // Randomise in locator square. + + Lat = Lat + ((rand() / 24.0) / RAND_MAX); + Lon = Lon + ((rand() / 12.0) / RAND_MAX); + Station->Lat = Lat; + Station->Lon = Lon; + Station->Approx = 1; + Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); + } + } + } + + ptr = strtok_s(NULL, ",[](){} \n", &context); + } + + return 0; + } + return 0; +} + +// Convert MIC-E Char to Lat Digit (offset by 0x30) +// 0123456789 @ABCDEFGHIJKLMNOPQRSTUVWXYZ +char MicELat[] = "0123456789???????0123456789 ???0123456789 " ; + +char MicECode[]= "0000000000???????111111111110???22222222222" ; + + +VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station) +{ + // Info is encoded in the Dest Addr (in Station->Path) as well as Payload. + // See APRS Spec for full details + + char Lat[10]; // DDMMHH + char LatDeg[3]; + char * ptr; + char c; + int i, n; + int LonDeg, LonMin; + BOOL LonOffset = FALSE; + char NS = 'S'; + char EW = 'E'; + UCHAR SymChar, SymSet; + double NewLat, NewLon; + int SP, DC, SE; // Course/Speed Encoded + int Course, Speed; + + // Make sure packet is long enough to have an valid address + + if (strlen(Payload) < 9) + return; + + ptr = &Station->Path[0]; + + for (i = 0; i < 6; i++) + { + n = (*(ptr++)) - 0x30; + c = MicELat[n]; + + if (c == '?') // Illegal + return; + + if (c == ' ') + c = '0'; // Limited Precision + + Lat[i] = c; + + } + + Lat[6] = 0; + + if (Station->Path[3] > 'O') + NS = 'N'; + + if (Station->Path[5] > 'O') + EW = 'W'; + + if (Station->Path[4] > 'O') + LonOffset = TRUE; + + n = Payload[1] - 28; // Lon Degrees S9PU0T,WIDE1-1,WIDE2-2,qAR,WB9TLH-15:`rB0oII>/]"6W}44 + + if (LonOffset) + n += 100; + + if (n > 179 && n < 190) + n -= 80; + else + if (n > 189 && n < 200) + n -= 190; + + LonDeg = n; + +/* + To decode the longitude degrees value: +1. subtract 28 from the d+28 value to obtain d. +2. if the longitude offset is +100 degrees, add 100 to d. +3. subtract 80 if 180 ˜ d ˜ 189 +(i.e. the longitude is in the range 100–109 degrees). +4. or, subtract 190 if 190 ˜ d ˜ 199. +(i.e. the longitude is in the range 0–9 degrees). +*/ + + n = Payload[2] - 28; // Lon Mins + + if (n > 59) + n -= 60; + + LonMin = n; + + n = Payload[3] - 28; // Lon Mins/100; + +//1. subtract 28 from the m+28 value to obtain m. +//2. subtract 60 if m ™ 60. +//(i.e. the longitude minutes is in the range 0–9). + + + memcpy(LatDeg, Lat, 2); + LatDeg[2]=0; + + NewLat = atof(LatDeg) + (atof(Lat+2) / 6000.0); + + if (NS == 'S') + NewLat = -NewLat; + + NewLon = LonDeg + LonMin / 60.0 + n / 6000.0; + + if (EW == 'W') // West + NewLon = -NewLon; + + + SP = Payload[4] - 28; + DC = Payload[5] - 28; + SE = Payload[6] - 28; // Course 100 and 10 degs + + Speed = DC / 10; // Quotient = Speed Units + Course = DC - (Speed * 10); // Remainder = Course Deg/100 + + Course = SE + (Course * 100); + + Speed += SP * 10; + + if (Speed >= 800) + Speed -= 800; + + if (Course >= 400) + Course -= 400; + + Station->Course = Course; + Station->Speed = Speed * 1.15077945; // MPH + +// Debugprintf("MIC-E Course/Speed %s %d %d", Station->Callsign, Course, Speed); + + SymChar = Payload[7]; // Symbol + SymSet = Payload[8]; // Symbol + + SymChar -= '!'; + + Station->IconOverlay = 0; + + if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) + { + SymChar += 96; + Station->IconOverlay = SymSet; + } + else + if (SymSet == '\\') + SymChar += 96; + + Station->iconRow = SymChar >> 4; + Station->iconCol = SymChar & 15; + + if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) + return; + + if (Station->Lat != NewLat || Station->Lon != NewLon) + { + time_t NOW = time(NULL); + time_t Age = NOW - Station->TimeLastUpdated; + + if (Age > 15) // Don't update too often + { + // Add to track + +// if (memcmp(Station->Callsign, "ISS ", 4) == 0) +// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); + + Station->LatTrack[Station->Trackptr] = NewLat; + Station->LonTrack[Station->Trackptr] = NewLon; + Station->TrackTime[Station->Trackptr] = NOW; + + Station->Trackptr++; + Station->Moved = TRUE; + + if (Station->Trackptr == TRACKPOINTS) + Station->Trackptr = 0; + } + + Station->Lat = NewLat; + Station->Lon = NewLon; + Station->Approx = 0; + } + + return; + +} + +/* + +INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ +// This processes messages from controls on the tab subpages + int Command; + +// int retCode, disp; +// char Key[80]; +// HKEY hKey; +// BOOL OK; +// OPENFILENAME ofn; +// char Digis[100]; + + int Port = PortNum[CurrentPage]; + + switch (message) + { + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hDlg); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + case WM_INITDIALOG: + OnChildDialogInit( hDlg); + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + if (Command == 2002) + return TRUE; + + switch (Command) + { +/* case IDC_FILE: + + memset(&ofn, 0, sizeof (OPENFILENAME)); + ofn.lStructSize = sizeof (OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = &FN[Port][0]; + ofn.nMaxFile = 250; + ofn.lpstrTitle = "File to send as beacon"; + ofn.lpstrInitialDir = GetBPQDirectory(); + + if (GetOpenFileName(&ofn)) + SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]); + + break; + + + case IDOK: + + GetDlgItemText(hDlg, IDC_UIDEST, &UIDEST[Port][0], 10); + + if (UIDigi[Port]) + { + free(UIDigi[Port]); + UIDigi[Port] = NULL; + } + + if (UIDigiAX[Port]) + { + free(UIDigiAX[Port]); + UIDigiAX[Port] = NULL; + } + + GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99); + + UIDigi[Port] = _strdup(Digis); + + GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255); + GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000); + + Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE); + + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIDEST[Port][0], strlen(&UIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Enabled", 0, REG_DWORD,(BYTE *)&UIEnabled[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, strlen(Digis)); + + RegCloseKey(hKey); + } + + SetupUI(Port); + + return (INT_PTR)TRUE; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case ID_TEST: + + SendBeacon(Port); + return TRUE; + + + + + } + break; + + } + return (INT_PTR)FALSE; +} + + + + +VOID WINAPI OnTabbedDialogInit(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); + DWORD dwDlgBase = GetDialogBaseUnits(); + int cxMargin = LOWORD(dwDlgBase) / 4; + int cyMargin = HIWORD(dwDlgBase) / 8; + + TC_ITEM tie; + RECT rcTab; + + int i, pos, tab = 0; + INITCOMMONCONTROLSEX init; + + char PortNo[60]; + struct _EXTPORTDATA * PORTVEC; + + hwndDlg = hDlg; // Save Window Handle + + // Save a pointer to the DLGHDR structure. + + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); + + // Create the tab control. + + + init.dwICC = ICC_STANDARD_CLASSES; + init.dwSize=sizeof(init); + i=InitCommonControlsEx(&init); + + pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, 100, 100, hwndDlg, NULL, hInst, NULL); + + if (pHdr->hwndTab == NULL) { + + // handle error + + } + + // Add a tab for each of the child dialog boxes. + + tie.mask = TCIF_TEXT | TCIF_IMAGE; + + tie.iImage = -1; + + for (i = 1; i <= GetNumberofPorts(); i++) + { + // Only allow UI on ax.25 ports + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntry(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + continue; + + sprintf(PortNo, "Port %2d", GetPortNumber(i)); + PortNum[tab] = GetPortNumber(i); + + tie.pszText = PortNo; + TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie); + + pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE"); + + } + + PageCount = tab; + + // Determine the bounding rectangle for all child dialog boxes. + + SetRectEmpty(&rcTab); + + for (i = 0; i < PageCount; i++) + { + if (pHdr->apRes[i]->cx > rcTab.right) + rcTab.right = pHdr->apRes[i]->cx; + + if (pHdr->apRes[i]->cy > rcTab.bottom) + rcTab.bottom = pHdr->apRes[i]->cy; + + } + + MapDialogRect(hwndDlg, &rcTab); + +// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; + +// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; + + // Calculate how large to make the tab control, so + + // the display area can accomodate all the child dialog boxes. + + TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); + + OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); + + // Calculate the display rectangle. + + CopyRect(&pHdr->rcDisplay, &rcTab); + + TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); + + // Set the size and position of the tab control, buttons, + + // and dialog box. + + SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); + + // Move the Buttons to bottom of page + + pos=rcTab.left+cxMargin; + + + // Size the dialog box. + + SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), + rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), + SWP_NOMOVE | SWP_NOZORDER); + + // Simulate selection of the first item. + + OnSelChanged(hwndDlg); + +} + +// DoLockDlgRes - loads and locks a dialog template resource. + +// Returns a pointer to the locked resource. + +// lpszResName - name of the resource + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) +{ + HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG); + HGLOBAL hglb = LoadResource(hInst, hrsrc); + + return (DLGTEMPLATE *) LockResource(hglb); +} + +//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. + +// OnSelChanged - processes the TCN_SELCHANGE notification. + +// hwndDlg - handle of the parent dialog box + +VOID WINAPI OnSelChanged(HWND hwndDlg) +{ + char PortDesc[40]; + int Port; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); + + // Destroy the current child dialog box, if any. + + if (pHdr->hwndDisplay != NULL) + + DestroyWindow(pHdr->hwndDisplay); + + // Create the new child dialog box. + + pHdr->hwndDisplay = CreateDialogIndirect(hInst, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); + + hwndDisplay = pHdr->hwndDisplay; // Save + + Port = PortNum[CurrentPage]; + // Fill in the controls + + GetPortDescription(PortNum[CurrentPage], PortDesc); + + SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc); + +// CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]); + +// SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE); + + SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIDEST[Port][0]); + SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIDigi[Port]); + + + +// SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]); +// SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]); + + ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); + +} + +//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. + +// OnChildDialogInit - Positions the child dialog box to fall + +// within the display area of the tab control. + +VOID WINAPI OnChildDialogInit(HWND hwndDlg) +{ + HWND hwndParent = GetParent(hwndDlg); + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); + + SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); +} + + +*/ + + +/* +VOID ProcessMessage(char * Payload, struct STATIONRECORD * Station) +{ + char MsgDest[10]; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = Messages; + char * TextPtr = &Payload[11]; + char * SeqPtr; + int n = 0; + char FromCall[10] = " "; + struct tm * TM; + time_t NOW; + + memcpy(FromCall, Station->Callsign, (int)strlen(Station->Callsign)); + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + SeqPtr = strchr(TextPtr, '{'); + + if (SeqPtr) + { + *(SeqPtr++) = 0; + if(strlen(SeqPtr) > 6) + SeqPtr[7] = 0; + } + + if (_memicmp(TextPtr, "ack", 3) == 0) + { + // Message Ack. See if for one of our messages + + ptr = OutstandingMsgs; + + if (ptr == 0) + return; + + do + { + if (strcmp(ptr->FromCall, MsgDest) == 0 + && strcmp(ptr->ToCall, FromCall) == 0 + && strcmp(ptr->Seq, &TextPtr[3]) == 0) + { + // Message is acked + + ptr->Retries = 0; + ptr->Acked = TRUE; +// if (hMsgsOut) +// UpdateTXMessageLine(hMsgsOut, n, ptr); + + return; + } + ptr = ptr->Next; + n++; + + } while (ptr); + + return; + } + + Message = malloc(sizeof(struct APRSMESSAGE)); + memset(Message, 0, sizeof(struct APRSMESSAGE)); + strcpy(Message->FromCall, Station->Callsign); + strcpy(Message->ToCall, MsgDest); + + if (SeqPtr) + { + strcpy(Message->Seq, SeqPtr); + + // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message + + if (SeqPtr[2] == '}') + { + struct APRSMESSAGE * ptr1; + int nn = 0; + + strcpy(Station->LastRXSeq, SeqPtr); + + ptr1 = OutstandingMsgs; + + while (ptr1) + { + if (strcmp(ptr1->FromCall, MsgDest) == 0 + && strcmp(ptr1->ToCall, FromCall) == 0 + && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) + { + // Message is acked + + ptr1->Acked = TRUE; + ptr1->Retries = 0; +// if (hMsgsOut) +// UpdateTXMessageLine(hMsgsOut, nn, ptr); + + break; + } + ptr1 = ptr1->Next; + nn++; + } + } + else + { + // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanger + + Station->SimpleNumericSeq = TRUE; + } + } + + if (strlen(TextPtr) > 100) + TextPtr[100] = 0; + + strcpy(Message->Text, TextPtr); + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (_stricmp(MsgDest, APRSCall) == 0 && SeqPtr) // ack it if it has a sequence + { + // For us - send an Ack + + char ack[30]; + + int n = sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); + PutAPRSMessage(ack, n); + } + + if (ptr == NULL) + { + Messages = Message; + } + else + { + n++; + while(ptr->Next) + { + ptr = ptr->Next; + n++; + } + ptr->Next = Message; + } + + if (strcmp(MsgDest, APRSCall) == 0) // to me? + { + } +} + +*/ + +VOID APRSSecTimer() +{ + + // Check Message Retries + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + int n = 0; + + if (SendWX) + SendWeatherBeacon(); + + + while (ptr) + { + if (ptr->Acked == FALSE) + { + if (ptr->Retries) + { + ptr->RetryTimer--; + + if (ptr->RetryTimer == 0) + { + ptr->Retries--; + + if (ptr->Retries) + { + // Send Again + + char Msg[255]; + APRSHEARDRECORD * STN; + + sprintf(Msg, ":%-9s:%s{%s", ptr->ToCall, ptr->Text, ptr->Seq); + + STN = FindStationInMH(ptr->ToCall); + + if (STN) + SendAPRSMessage(Msg, STN->rfPort); + else + { + if (memcmp(ptr->ToCall, "SERVER ", 9)) + SendAPRSMessage(Msg, -1); // All RF ports unless to SERVER + SendAPRSMessage(Msg, 0); // IS + } + ptr->RetryTimer = RetryTimer; + } + } + } + } + + ptr = ptr->Next; + n++; + } +} + +double radians(double Degrees) +{ + return M_PI * Degrees / 180; +} +double degrees(double Radians) +{ + return Radians * 180 / M_PI; +} + +double Distance(double laa, double loa, double lah, double loh, BOOL KM) +{ + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians +*/ + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + loh *= 1.60934; + + return loh; +} + + +double myDistance(double laa, double loa, BOOL KM) +{ + double lah, loh; + + GetAPRSLatLon(&lah, &loh); + + return Distance(laa, loa, lah, loh, KM); +} + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians + + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + loh *= 1.60934; + + return loh; +} +*/ + +double Bearing(double lat2, double lon2, double lat1, double lon1) +{ + double dlat, dlon, TC1; + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; +} + + +double myBearing(double lat2, double lon2) +{ + double lat1, lon1; + + GetAPRSLatLon(&lat1, &lon1); + + return Bearing(lat2, lon2, lat1, lon1); +} +/* + + + + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; +} +*/ + +// Weather Data + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +VOID SendWeatherBeacon() +{ + char Msg[256]; + char DD[3]=""; + char HH[3]=""; + char MM[3]=""; + char Lat[10], Lon[10]; + size_t Len; + int index; + char WXMessage[1024]; + char * WXptr; + char * WXend; + time_t WXTime; + time_t now = time(NULL); + FILE * hFile; + struct tm * TM; + struct stat STAT; + + WXCounter++; + + if (WXCounter < WXInterval * 60) + return; + + WXCounter = 0; + +// Debugprintf("BPQAPRS - Trying to open WX file %s", WXFileName); + + if (stat(WXFileName, &STAT)) + { + Debugprintf("APRS WX File %s stat() failed %d", WXFileName, GetLastError()); + return; + } + + WXTime = (now - STAT.st_mtime) /60; // Minutes + + if (WXTime > (3 * WXInterval)) + { + Debugprintf("APRS Send WX File %s too old - %d minutes", WXFileName, WXTime); + return; + } + + hFile = fopen(WXFileName, "rb"); + + if (hFile) + Len = fread(WXMessage, 1, 1024, hFile); + else + { + Debugprintf("APRS WX File %s open() failed %d", WXFileName, GetLastError()); + return; + } + + + if (Len < 30) + { + Debugprintf("BPQAPRS - WX file %s is too short - %d Chars", WXFileName, Len); + fclose(hFile); + return; + } + + WXMessage[Len] = 0; + + // see if wview format + +//04-09-13, 2245 +//TempIn 23 +//TempEx 18 +//WindHi 0 +//WindAv 0 +//WindDr 200 +//BarmPs 30167 +//HumdIn 56 +//HumdEx 100 +//RnFall 0.00 +//DailyRnFall 0.00 + + if (strstr(WXMessage, "TempIn")) + { + int Wind = 0; + int Gust = 0; + int Temp = 0; + int Winddir = 0; + int Humidity = 0; + int Raintoday = 0; + int Rain24hrs = 0; + int Pressure = 0; + + char * ptr; + + ptr = strstr(WXMessage, "TempEx"); + if (ptr) + Temp = (int)(atof(ptr + 7) * 1.8) + 32; + + ptr = strstr(WXMessage, "WindHi"); + if (ptr) + Gust = atoi(ptr + 7); + + ptr = strstr(WXMessage, "WindAv"); + if (ptr) + Wind = atoi(ptr + 7); + + ptr = strstr(WXMessage, "WindDr"); + if (ptr) + Winddir = atoi(ptr + 7); + + ptr = strstr(WXMessage, "BarmPs"); + if (ptr) + Pressure = (int)(atof(ptr + 7) * 0.338638866667); // Inches to 1/10 millbars + + ptr = strstr(WXMessage, "HumdEx"); + if (ptr) + Humidity = atoi(ptr + 7); + + ptr = strstr(WXMessage, "RnFall"); + if (ptr) + Rain24hrs = (int)(atof(ptr + 7) * 100.0); + + ptr = strstr(WXMessage, "DailyRnFall"); + if (ptr) + Raintoday = (int)(atof(ptr + 12) * 100.0); + + if (Humidity > 99) + Humidity = 99; + + sprintf(WXMessage, "%03d/%03dg%03dt%03dr%03dP%03dp%03dh%02db%05d", + Winddir, Wind, Gust, Temp, 0, Raintoday, Rain24hrs, Humidity, Pressure); + + } + + WXptr = strchr(WXMessage, 10); + + if (WXptr) + { + WXend = strchr(++WXptr, 13); + if (WXend == 0) + WXend = strchr(WXptr, 10); + if (WXend) + *WXend = 0; + } + else + WXptr = &WXMessage[0]; + + // Get DDHHMM from Filetime + + TM = gmtime(&STAT.st_mtime); + + sprintf(DD, "%02d", TM->tm_mday); + sprintf(HH, "%02d", TM->tm_hour); + sprintf(MM, "%02d", TM->tm_min); + + GetAPRSLatLonString(Lat, Lon); + + Len = sprintf(Msg, "@%s%s%sz%s/%s_%s%s", DD, HH, MM, Lat, Lon, WXptr, WXComment); + + Debugprintf(Msg); + + for (index = 0; index < 32; index++) + { + if (WXPort[index]) + SendAPRSMessageEx(Msg, index, WXCall, FALSE); + } + + fclose(hFile); +} + + +/* +Jan 22 2012 14:10 +123/005g011t031r000P000p000h00b10161 + +/MITWXN Mitchell IN weather Station N9LYA-3 {UIV32} +< previous + +@221452z3844.42N/08628.33W_203/006g007t032r000P000p000h00b10171 +Complete Weather Report Format — with Lat/Long position, no Timestamp +! or = Lat Sym Table ID Long Symbol Code _ Wind Directn/ Speed Weather Data APRS Software WX Unit uuuu + 1 8 1 9 1 7 n 1 2-4 +Examples +!4903.50N/07201.75W_220/004g005t077r000p000P000h50b09900wRSW +!4903.50N/07201.75W_220/004g005t077r000p000P000h50b.....wRSW + +*/ + +// Web Server Code + +// The actual HTTP socket code is in bpq32.dll. Any requests for APRS data are passed in +// using a Named Pipe. The request looks exactly like one from a local socket, and the respone is +// a fully pormatted HTTP packet + + +#define InputBufferLen 1000 + + +#define MaxSessions 100 + + +HANDLE PipeHandle; + +int HTTPPort = 80; +BOOL IPV6 = TRUE; + +#define MAX_PENDING_CONNECTS 5 + +BOOL OpenSockets6(); + +char HTDocs[MAX_PATH] = "HTML"; +char SpecialDocs[MAX_PATH] = "Special Pages"; + +char SymbolText[192][20] = { + +"Police Stn", "No Symbol", "Digi", "Phone", "DX Cluster", "HF Gateway", "Plane sm", "Mob Sat Stn", +"WheelChair", "Snowmobile", "Red Cross", "Boy Scout", "Home", "X", "Red Dot", "Circle (0)", +"Circle (1)", "Circle (2)", "Circle (3)", "Circle (4)", "Circle (5)", "Circle (6)", "Circle (7)", "Circle (8)", +"Circle (9)", "Fire", "Campground", "Motorcycle", "Rail Eng.", "Car", "File svr", "HC Future", + +"Aid Stn", "BBS", "Canoe", "No Symbol", "Eyeball", "Tractor", "Grid Squ.", "Hotel", +"Tcp/ip", "No Symbol", "School", "Usr Log-ON", "MacAPRS", "NTS Stn", "Balloon", "Police", +"TBD", "Rec Veh'le", "Shuttle", "SSTV", "Bus", "ATV", "WX Service", "Helo", +"Yacht", "WinAPRS", "Jogger", "Triangle", "PBBS", "Plane lrge", "WX Station", "Dish Ant.", + +"Ambulance", "Bike", "ICP", "Fire Station", "Horse", "Fire Truck", "Glider", "Hospital", +"IOTA", "Jeep", "Truck", "Laptop", "Mic-E Rptr", "Node", "EOC", "Rover", +"Grid squ.", "Antenna", "Power Boat", "Truck Stop", "Truck 18wh", "Van", "Water Stn", "XAPRS", +"Yagi", "Shelter", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "", "", + +"Emergency", "No Symbol", "No. Digi", "Bank", "No Symbol", "No. Diam'd", "Crash site", "Cloudy", +"MEO", "Snow", "Church", "Girl Scout", "Home (HF)", "UnknownPos", "Destination", "No. Circle", +"No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", +"Petrol Stn", "Hail", "Park", "Gale Fl", "No Symbol", "No. Car", "Info Kiosk", "Hurricane", + +"No. Box", "Snow blwng", "Coast G'rd", "Drizzle", "Smoke", "Fr'ze Rain", "Snow Shwr", "Haze", +"Rain Shwr", "Lightning", "Kenwood", "Lighthouse", "No Symbol", "Nav Buoy", "Rocket", "Parking ", +"Quake", "Restaurant", "Sat/Pacsat", "T'storm", "Sunny", "VORTAC", "No. WXS", "Pharmacy", +"No Symbol", "No Symbol", "Wall Cloud", "No Symbol", "No Symbol", "No. Plane", "No. WX Stn", "Rain", + +"No. Diamond", "Dust blwng", "No. CivDef", "DX Spot", "Sleet", "Funnel Cld", "Gale", "HAM store", +"No. Blk Box", "WorkZone", "SUV", "Area Locns", "Milepost", "No. Triang", "Circle sm", "Part Cloud", +"No Symbol", "Restrooms", "No. Boat", "Tornado", "No. Truck", "No. Van", "Flooding", "No Symbol", +"Sky Warn", "No Symbol", "Fog", "No Symbol", "No Symbol", "No Symbol", "", ""}; + +// All Calls (8 per line) + +//EI7IG-1 +//G7TKK-1 +//GB7GL-B +//GM1TCN +//GM8BPQ +//GM8BPQ-14 +//LA2VPA-9 +//LA3FIA-10 +//LA6JF-2LD4STM0CHK-7M0OZH-7MB7UFO-1MB7UNMM0DXE-15PA2AYX-9 +//PA3AQW-5PD1CPD5LWD-2PI1ECO + + +char * DoSummaryLine(struct STATIONRECORD * ptr, int n, int Width) +{ + static char Line2[80]; + int x; + char XCall[256]; + char * ptr1 = ptr->Callsign; + char * ptr2 = XCall; + + // Object Names can contain spaces + + while(*ptr1) + { + if (*ptr1 == ' ') + { + memcpy(ptr2, "%20", 3); + ptr2 += 3; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + } + + *ptr2 = 0; + + + // Object Names can contain spaces + + + sprintf(Line2, "%s", + XCall, ptr->Callsign); + + x = ++n/Width; + x = x * Width; + + if (x == n) + strcat(Line2, ""); + + return Line2; +} + +char * DoDetailLine(struct STATIONRECORD * ptr, BOOL LocalTime, BOOL KM) +{ + static char Line[512]; + double Lat = ptr->Lat; + double Lon = ptr->Lon; + char NS='N', EW='E'; + + char LatString[20], LongString[20], DistString[20], BearingString[20]; + int Degrees; + double Minutes; + char Time[80]; + struct tm * TM; + char XCall[256]; + + char * ptr1 = ptr->Callsign; + char * ptr2 = XCall; + + // Object Names can contain spaces + + while(*ptr1) + { + if (*ptr1 == ' ') + { + memcpy(ptr2, "%20", 3); + ptr2 += 3; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + } + + *ptr2 = 0; + + +// if (ptr->ObjState == '_') // Killed Object +// return; + + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (ptr->Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); + + sprintf(LatString,"%2d°%05.2f'%c", Degrees, Minutes, NS); + + Degrees = Lon; + +#pragma warning(pop) + + Minutes = Lon * 60 - 60 * Degrees; + + sprintf(LongString, "%3d°%05.2f'%c",Degrees, Minutes, EW); + + sprintf(DistString, "%6.1f", myDistance(ptr->Lat, ptr->Lon, KM)); + sprintf(BearingString, "%3.0f", myBearing(ptr->Lat, ptr->Lon)); + + sprintf(Line, " %s%s%s%s %s%s%s%s\r\n", + XCall, ptr->Callsign, + (strchr(ptr->Path, '*'))? "*": "", &SymbolText[ptr->iconRow << 4 | ptr->iconCol][0], LatString, LongString, DistString, BearingString, Time); + + return Line; +} + + +int CompareFN(const void *a, const void *b) +{ + const struct STATIONRECORD * x = a; + const struct STATIONRECORD * y = b; + + x = x->Next; + y = y->Next; + + return strcmp(x->Callsign, y->Callsign); + + /* strcmp functions works exactly as expected from + comparison function */ +} + + + +char * CreateStationList(BOOL RFOnly, BOOL WX, BOOL Mobile, char Objects, int * Count, char * Param, BOOL KM) +{ + char * Line = malloc(100000); + struct STATIONRECORD * ptr = *StationRecords; + int n = 0, i; + struct STATIONRECORD * List[1000]; + int TableWidth = 8; + BOOL LocalTime = DefaultLocalTime; + + if (strstr(Param, "time=local")) + LocalTime = TRUE; + else if (strstr(Param, "time=utc")) + LocalTime = FALSE; + + Line[0] = 0; + + if (Param && Param[0]) + { + char * Key, *Context; + + Key = strtok_s(Param, "=", &Context); + + if (_stricmp(Key, "width") == 0) + TableWidth = atoi(Context); + + if (TableWidth == 0) + TableWidth = 8; + } + + // Build list of calls + + while (ptr) + { + if (ptr->ObjState == Objects && ptr->Lat != 0.0 && ptr->Lon != 0.0) + { + if ((WX && (ptr->LastWXPacket[0] == 0)) || (RFOnly && (ptr->LastPort == 0)) || + (Mobile && ((ptr->Speed < 0.1) || ptr->LastWXPacket[0] != 0))) + { + ptr = ptr->Next; + continue; + } + + List[n++] = ptr; + + if (n > 999) + break; + + } + ptr = ptr->Next; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareFN); + + for (i = 0; i < n; i++) + { + if (RFOnly) + strcat(Line, DoDetailLine(List[i], LocalTime, KM)); + else + strcat(Line, DoSummaryLine(List[i], i, TableWidth)); + } + + *Count = n; + + return Line; + +} + +char * APRSLookupKey(struct APRSConnectionInfo * sockptr, char * Key, BOOL KM) +{ + struct STATIONRECORD * stn = sockptr->SelCall; + + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + return _strdup(LoppedAPRSCall); + + if (strcmp(Key, "##CALLSIGN##") == 0) + return _strdup(sockptr->Callsign); + + if (strcmp(Key, "##CALLSIGN_NOSSID##") == 0) + { + char * Call = _strdup(sockptr->Callsign); + char * ptr = strchr(Call, '-'); + if (ptr) + *ptr = 0; + return Call; + } + + if (strcmp(Key, "##MY_WX_CALLSIGN##") == 0) + return _strdup(LoppedAPRSCall); + + if (strcmp(Key, "##MY_BEACON_COMMENT##") == 0) + return _strdup(StatusMsg); + + if (strcmp(Key, "##MY_WX_BEACON_COMMENT##") == 0) + return _strdup(WXComment); + + if (strcmp(Key, "##MILES_KM##") == 0) + if (KM) + return _strdup("KM"); + else + return _strdup("Miles"); + + if (strcmp(Key, "##EXPIRE_TIME##") == 0) + { + char val[80]; + sprintf(val, "%d", ExpireTime); + return _strdup(val); + } + + if (strcmp(Key, "##LOCATION##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + char LatString[20]; + int Degrees; + double Minutes; + + if (Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); + + sprintf(LatString,"%2d°%05.2f'%c",Degrees, Minutes, NS); + + Degrees = Lon; + +#pragma warning(pop) + + Minutes = Lon * 60 - 60 * Degrees; + + sprintf(val,"%s %3d°%05.2f'%c", LatString, Degrees, Minutes, EW); + + return _strdup(val); + } + + if (strcmp(Key, "##LOCDDMMSS##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + char LatString[20]; + int Degrees; + double Minutes; + + // 48.45.18N, 002.18.37E + + if (Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); +// IntMins = Minutes; +// Seconds = Minutes * 60.0 - (60 * IntMins); + + sprintf(LatString,"%2d.%05.2f%c",Degrees, Minutes, NS); + + Degrees = Lon; + Minutes = Lon * 60.0 - 60 * Degrees; +// IntMins = Minutes; +// Seconds = Minutes * 60.0 - (60 * IntMins); + +#pragma warning(pop) + + sprintf(val,"%s, %03d.%05.2f%c", LatString, Degrees, Minutes, EW); + + return _strdup(val); + } + + if (strcmp(Key, "##LATLON##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + + // 58.5, -6.2 + + sprintf(val,"%f, %f", Lat, Lon); + return _strdup(val); + } + + if (strcmp(Key, "##STATUS_TEXT##") == 0) + return _strdup(stn->Status); + + if (strcmp(Key, "##LASTPACKET##") == 0) + return _strdup(stn->LastPacket); + + + if (strcmp(Key, "##LAST_HEARD##") == 0) + { + char Time[80]; + struct tm * TM; + time_t Age = time(NULL) - stn->TimeLastUpdated; + + TM = gmtime(&Age); + + sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + return _strdup(Time); + } + + if (strcmp(Key, "##FRAME_HEADER##") == 0) + return _strdup(stn->Path); + + if (strcmp(Key, "##FRAME_INFO##") == 0) + return _strdup(stn->LastWXPacket); + + if (strcmp(Key, "##BEARING##") == 0) + { + char val[80]; + + sprintf(val, "%03.0f", myBearing(sockptr->SelCall->Lat, sockptr->SelCall->Lon)); + return _strdup(val); + } + + if (strcmp(Key, "##COURSE##") == 0) + { + char val[80]; + + sprintf(val, "%03.0f", stn->Course); + return _strdup(val); + } + + if (strcmp(Key, "##SPEED_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%5.1f", stn->Speed); + return _strdup(val); + } + + if (strcmp(Key, "##DISTANCE##") == 0) + { + char val[80]; + + sprintf(val, "%5.1f", myDistance(sockptr->SelCall->Lat, sockptr->SelCall->Lon, KM)); + return _strdup(val); + } + + + + if (strcmp(Key, "##WIND_DIRECTION##") == 0) + { + char val[80]; + + sprintf(val, "%03d", sockptr->WindDirn); + return _strdup(val); + } + + if (strcmp(Key, "##WIND_SPEED_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->WindSpeed); + return _strdup(val); + } + + if (strcmp(Key, "##WIND_GUST_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->WindGust); + return _strdup(val); + } + + if (strcmp(Key, "##TEMPERATURE_F##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->Temp); + return _strdup(val); + } + + if (strcmp(Key, "##HUMIDITY##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->Humidity); + return _strdup(val); + } + + if (strcmp(Key, "##PRESSURE_HPA##") == 0) + { + char val[80]; + + sprintf(val, "%05.1f", sockptr->Pressure /10.0); + return _strdup(val); + } + + if (strcmp(Key, "##RAIN_TODAY_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainToday /100.0); + return _strdup(val); + } + + + if (strcmp(Key, "##RAIN_24_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainLastDay /100.0); + return _strdup(val); + } + + + if (strcmp(Key, "##RAIN_HOUR_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainLastHour /100.0); + return _strdup(val); + } + + if (strcmp(Key, "##MAP_LAT_LON##") == 0) + { + char val[256]; + + sprintf(val, "%f,%f", stn->Lat, stn->Lon); + return _strdup(val); + } + + if (strcmp(Key, "##SYMBOL_DESCRIPTION##") == 0) + return _strdup(&SymbolText[stn->iconRow << 4 | stn->iconCol][0]); + + +/* +##WIND_SPEED_MS## - wind speed metres/sec +##WIND_SPEED_KMH## - wind speed km/hour +##WIND_GUST_MPH## - wind gust miles/hour +##WIND_GUST_MS## - wind gust metres/sec +##WIND_GUST_KMH## - wind gust km/hour +##WIND_CHILL_F## - wind chill F +##WIND_CHILL_C## - wind chill C +##TEMPERATURE_C## - temperature C +##DEWPOINT_F## - dew point temperature F +##DEWPOINT_C## - dew point temperature C +##PRESSURE_IN## - pressure inches of mercury +##PRESSURE_HPA## - pressure hPa (mb) +##RAIN_HOUR_MM## - rain in last hour mm +##RAIN_TODAY_MM## - rain today mm +##RAIN_24_MM## - rain in last 24 hours mm +##FRAME_HEADER## - frame header of the last posit heard from the station +##FRAME_INFO## - information field of the last posit heard from the station +##MAP_LARGE_SCALE##" - URL of a suitable large scale map on www.vicinity.com +##MEDIUM_LARGE_SCALE##" - URL of a suitable medium scale map on www.vicinity.com +##MAP_SMALL_SCALE##" - URL of a suitable small scale map on www.vicinity.com +##MY_LOCATION## - 'Latitude', 'Longitude' in 'Station Setup' +##MY_STATUS_TEXT## - status text configured in 'Status Text' +##MY_SYMBOL_DESCRIPTION## - 'Symbol' that would be shown for our station in 'Station List' +##HIT_COUNTER## - The number of times the page has been accessed +##DOCUMENT_LAST_CHANGED## - The date/time the page was last edited + +##FRAME_HEADER## - frame header of the last posit heard from the station +##FRAME_INFO## - information field of the last posit heard from the station + +*/ + return NULL; +} + +VOID APRSProcessSpecialPage(struct APRSConnectionInfo * sockptr, char * Buffer, int FileSize, char * StationTable, int Count, BOOL WX, BOOL KM) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(250000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + size_t PrevLen; + size_t BytesLeft = FileSize; + size_t NewFileSize = FileSize; + char * StripPtr = ptr1; + int HeaderLen; + char Header[256]; + + if (WX && sockptr->SelCall && sockptr->SelCall->LastWXPacket) + { + DecodeWXReport(sockptr, sockptr->SelCall->LastWXPacket); + } + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + if (ptr2) + { + PrevLen = (ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = FileSize - (ptr1 - Buffer); + } + } + + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = StripPtr - Buffer; + + FileSize = (int)BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + size_t KeyLen; + char * NewText; + size_t NewTextLen; + + ptr3 += 2; + KeyLen = ptr3 - ptr2; + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + if (strcmp(Key, "##STATION_TABLE##") == 0) + { + NewText = _strdup(StationTable); + } + else + { + if (strcmp(Key, "##TABLE_COUNT##") == 0) + { + char val[80]; + sprintf(val, "%d", Count); + NewText = _strdup(val); + } + else + NewText = APRSLookupKey(sockptr, Key, KM); + } + + if (NewText) + { + NewTextLen = strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = Buffer + FileSize - ptr3; + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)NewFileSize); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, NewMessage, (int)NewFileSize, 0); + + free (NewMessage); + free(StationTable); + + return; +} + +VOID APRSSendMessageFile(struct APRSConnectionInfo * sockptr, char * FN) +{ + int FileSize = 0; + char * MsgBytes = 0; + char * SaveMsgBytes = 0; + + char MsgFile[MAX_PATH]; + FILE * hFile; + BOOL Special = FALSE; + int HeaderLen; + char Header[256]; + char * Param = 0; + struct stat STAT; + int Sent; + char * ptr; + + if (strchr(FN, '?')) + strtok_s(FN, "?", &Param); + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strcmp(FN, "/") == 0) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, SpecialDocs); + else + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory, APRSDir, SpecialDocs, &FN[5]); + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + // Try normal pages + + + if (strcmp(FN, "/") == 0) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, HTDocs); + else + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory,APRSDir, HTDocs, &FN[5]); + + // My standard page set is now hard coded + + + + MsgBytes = SaveMsgBytes = GetStandardPage(&FN[6], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + } + else + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + HeaderLen = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sockptr->sock, Header, HeaderLen, 0); + return; + } + } + } + else + Special = TRUE; + + if (FileSize == 0) // Not from internal template + { + if (stat(MsgFile, &STAT) == 0) + FileSize = STAT.st_size; + + MsgBytes = SaveMsgBytes = malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + } + + // if HTML file, look for ##...## substitutions + + if ((strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + // Build Station list, depending on URL + + int Count = 0; + BOOL RFOnly = !(strstr(_strlwr(FN), "rf") == NULL); // Leaves FN in lower case + BOOL WX =!(strstr(FN, "wx") == NULL); + BOOL Mobile = !(strstr(FN, "mobile") == NULL); + char Objects = (strstr(FN, "obj"))? '*' :0; + char * StationList; + BOOL KM = DefaultDistKM; + + if (Param == 0) + Param =""; + else + _strlwr(Param); + + if (strstr(Param, "dist=km")) + KM = TRUE; + else if (strstr(Param, "dist=miles")) + KM = FALSE; + + + StationList = CreateStationList(RFOnly, WX, Mobile, Objects, &Count, Param, KM); + + APRSProcessSpecialPage(sockptr, MsgBytes, FileSize, StationList, Count, WX, KM); + free (MsgBytes); + return; // ProcessSpecial has sent the reply + } + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0) + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: image\r\n\r\n", FileSize); + else + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", FileSize); + + send(sockptr->sock, Header, HeaderLen, 0); + + Sent = send(sockptr->sock, MsgBytes, FileSize, 0); +// printf("Send %d %d\n", FileSize, Sent); + + while (Sent < FileSize) + { + FileSize -= Sent; + MsgBytes += Sent; + Sent = send(sockptr->sock, MsgBytes, FileSize, 0); +// printf("Send %d %d\n", FileSize, Sent); + if (Sent == -1) + { + Sleep(10); + Sent = 0; + } + } + + free (SaveMsgBytes); +} + +char WebHeader[] = "" + "" + "APRS Messaging" + "" + "" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" + "

%s's Messages

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

Message Sent by %s

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

APRS Message Input

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

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

" + "

BPQ32 APRS Server

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



Message Cancelled

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

Invalid Callsign

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

No Message

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



Message Sent

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + } + + URL = &MsgPtr[4]; + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + if (_stricmp(URL, "/APRS") == 0) + { + // Return APRS Index Page + + OutputLen = sprintf(OutBuffer, APRSIndexPage, ""); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + + if (_memicmp(URL, "/aprs/msgs/entermsg", 19) == 0 || _memicmp(URL, "/aprs/entermsg", 14) == 0) + { + char * To = strchr(URL, '='); + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "
Not authorized - please return to Node Menu and sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(OutputLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, OutBuffer, OutputLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + + if (To) + { + To++; + UndoTransparency(To); + strlop(To, '&'); + } + else + To = ""; + + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + + } + + else if (_memicmp(URL, "/aprs/msgs", 10) == 0) + { + // Return Messages Received Page + + struct APRSMESSAGE * ptr = SMEM->Messages; + int n = 0; + char BaseCall[10]; + char BaseFrom[10]; + char * MsgCall = LoppedAPRSCall; + BOOL OnlyMine = TRUE; + BOOL AllSSID = TRUE; + BOOL OnlySeq = FALSE; + BOOL ShowBulls = TRUE; + + // Parse parameters + + // ?call=g8bpq&bulls=true&seqonly=true&onlymine=true + + char * params = strchr(URL, '?'); + + if (params) + { + char * param, * context; + + param = strtok_s(++params, "&", &context); + + while (param) + { + char * val = strlop(param, '='); + + if (val) + { + strlop(val, ' '); + if (_stricmp(param, "call") == 0) + MsgCall = _strupr(val); + else if (_stricmp(param, "bulls") == 0) + ShowBulls = !_stricmp(val, "true"); + else if (_stricmp(param, "onlyseq") == 0) + OnlySeq = !_stricmp(val, "true"); + else if (_stricmp(param, "onlymine") == 0) + OnlyMine = !_stricmp(val, "true"); + else if (_stricmp(param, "AllSSID") == 0) + AllSSID = !_stricmp(val, "true"); + } + param = strtok_s(NULL,"&", &context); + } + } + if (AllSSID) + { + memcpy(BaseCall, MsgCall, 10); + strlop(BaseCall, '-'); + } + + OutputLen = sprintf(OutBuffer, WebHeader, MsgCall, MsgCall); + + while (ptr) + { + char ToLopped[11] = ""; + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + if (memcmp(ToLopped, "BLN", 3) == 0) + if (ShowBulls == TRUE) + goto wantit; + + if (strcmp(ToLopped, MsgCall) == 0) // to me? + goto wantit; + + if (strcmp(ptr->FromCall, MsgCall) == 0) // to me? + goto wantit; + + if (AllSSID) + { + memcpy(BaseFrom, ToLopped, 10); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + + memcpy(BaseFrom, ptr->FromCall, 10); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + + } + + if (OnlyMine == FALSE) // Want All + if (OnlySeq == FALSE || ptr->Seq[0] != 0) + goto wantit; + + // ignore + + ptr = ptr->Next; + continue; + wantit: + OutputLen += sprintf(&OutBuffer[OutputLen], WebLine, + ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, + ptr->FromCall, ptr->ToCall, ptr->Text); + + ptr = ptr->Next; + + if (OutputLen > 99000) + break; + + } + + OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer); + + HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, OutBuffer, OutputLen); + + return; + + } + + else if (_memicmp(URL, "/aprs/txmsgs", 12) == 0) + { + // Return Messages Received Page + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + char * MsgCall = LoppedAPRSCall; + + char Retries[10]; + + + OutputLen = sprintf(OutBuffer, WebTXHeader, MsgCall, MsgCall); + + while (ptr) + { + char ToLopped[11] = ""; + + if (ptr->Acked) + strcpy(Retries, "A"); + else if (ptr->Retries == 0) + strcpy(Retries, "F"); + else + sprintf(Retries, "%d", ptr->Retries); + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + OutputLen += sprintf(&OutBuffer[OutputLen], WebTXLine, + ptr->ToCall, ptr->Seq, ptr->Time, Retries, ptr->Text); + ptr = ptr->Next; + + if (OutputLen > 99000) + break; + + } + + OutputLen += sprintf(&OutBuffer[OutputLen], WebTrailer); + + HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, OutBuffer, OutputLen); + + return; + + } + + + if (_memicmp(URL, "/aprs/find.cgi?call=", 20) == 0) + { + // return Station details + + char * Call = &URL[20]; + BOOL RFOnly, WX, Mobile, Object = FALSE; + struct STATIONRECORD * stn; + char * Referrer = strstr(ptr + 1, "Referer:"); + + // Undo any % transparency in call + + char * ptr1 = Call; + char * ptr2 = Key; + char c; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + if (Referrer) + { + ptr = strchr(Referrer, 13); + if (ptr) + { + *ptr = 0; + RFOnly = !(strstr(Referrer, "rf") == NULL); + WX = !(strstr(Referrer, "wx") == NULL); + Mobile = !(strstr(Referrer, "mobile") == NULL); + Object = !(strstr(Referrer, "obj") == NULL); + + if (WX) + strcpy(URL, "/aprs/infowx_call.html"); + else if (Mobile) + strcpy(URL, "/aprs/infomobile_call.html"); + else if (Object) + strcpy(URL, "/aprs/infoobj_call.html"); + else + strcpy(URL, "/aprs/info_call.html"); + } + } + + if (Object) + { + // Name is space padded, and could have embedded spaces + + int Keylen = (int)strlen(Key); + + if (Keylen < 9) + memset(&Key[Keylen], 32, 9 - Keylen); + } + + stn = FindStation(Key, FALSE); + + if (stn == NULL) + strcpy(URL, "/aprs/noinfo.html"); + else + sockptr->SelCall = stn; + } + + + strcpy(sockptr->Callsign, Key); + + APRSSendMessageFile(sockptr, URL); + + return; +} + +// Code for handling APRS messages within BPQ32/LinBPQ instead of GUI + + +int ProcessMessage(char * Payload, struct STATIONRECORD * Station) +{ + char MsgDest[10]; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = SMEM->Messages; + char * TextPtr = &Payload[11]; + char * SeqPtr; + int n = 0; + char FromCall[10] = " "; + struct tm * TM; + time_t NOW; + char noSeq[] = ""; + int ourMessage = 0; + + memcpy(FromCall, Station->Callsign, strlen(Station->Callsign)); + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + if (strcmp(MsgDest, CallPadded) == 0) // to me? + { + SMEM->NeedRefresh = 255; // Flag to control Msg popup + ourMessage = 1; + } + else + SMEM->NeedRefresh = 1; + + SeqPtr = strchr(TextPtr, '{'); + + if (SeqPtr) + { + *(SeqPtr++) = 0; + if(strlen(SeqPtr) > 6) + SeqPtr[7] = 0; + } + else + SeqPtr = noSeq; + + if (_memicmp(TextPtr, "ack", 3) == 0) + { + // Message Ack. See if for one of our messages + + ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return ourMessage; + + do + { + if (strcmp(ptr->FromCall, MsgDest) == 0 + && strcmp(ptr->ToCall, FromCall) == 0 + && strcmp(ptr->Seq, &TextPtr[3]) == 0) + { + // Message is acked + + ptr->Retries = 0; + ptr->Acked = TRUE; + + return ourMessage; + } + ptr = ptr->Next; + n++; + + } while (ptr); + + return ourMessage; + } + + // See if we already have this message + + ptr = SMEM->Messages; + + while(ptr) + { + if (strcmp(ptr->ToCall, MsgDest) == 0 + && strcmp(ptr->FromCall, FromCall) == 0 + && strcmp(ptr->Seq, SeqPtr) == 0 + && strcmp(ptr->Text, TextPtr) == 0) + + // Duplicate + + return ourMessage; + + ptr = ptr->Next; + } + + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + return ourMessage; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + memset(Message->FromCall, ' ', 9); + memcpy(Message->FromCall, Station->Callsign, strlen(Station->Callsign)); + strcpy(Message->ToCall, MsgDest); + + if (SeqPtr) + { + strcpy(Message->Seq, SeqPtr); + + // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message + + if (SeqPtr[2] == '}') + { + struct APRSMESSAGE * ptr1; + int nn = 0; + + strcpy(Station->LastRXSeq, SeqPtr); + + ptr1 = SMEM->OutstandingMsgs; + + while (ptr1) + { + if (strcmp(ptr1->FromCall, MsgDest) == 0 + && strcmp(ptr1->ToCall, FromCall) == 0 + && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) + { + // Message is acked + + ptr1->Acked = TRUE; + ptr1->Retries = 0; + + break; + } + ptr1 = ptr1->Next; + nn++; + } + } + else + { + // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanges + + Station->SimpleNumericSeq = TRUE; + } + } + + if (strlen(TextPtr) > 100) + TextPtr[100] = 0; + + strcpy(Message->Text, TextPtr); + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (_stricmp(MsgDest, CallPadded) == 0 && SeqPtr) // ack it if it has a sequence + { + // For us - send an Ack + + char ack[30]; + APRSHEARDRECORD * STN; + + sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); + + if (memcmp(Message->FromCall, "SERVER ", 9) == 0) + { + SendAPRSMessage(ack, 0); // IS + } + else + { + STN = FindStationInMH(Message->ToCall); + + if (STN) + SendAPRSMessage(ack, STN->rfPort); + else + { + SendAPRSMessage(ack, -1); // All RF ports + SendAPRSMessage(ack, 0); // IS + } + } + } + + ptr = SMEM->Messages; + + if (ptr == NULL) + { + SMEM->Messages = Message; + } + else + { + n++; + while(ptr->Next) + { + ptr = ptr->Next; + n++; + } + ptr->Next = Message; + } + return ourMessage; +} + +BOOL InternalSendAPRSMessage(char * Text, char * Call) +{ + char Msg[255]; + size_t len = strlen(Call); + APRSHEARDRECORD * STN; + struct tm * TM; + time_t NOW; + + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + return FALSE; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + strcpy(Message->FromCall, CallPadded); + + memset(Message->ToCall, ' ', 9); + memcpy(Message->ToCall, Call, len); + + Message->ToStation = FindStation(Call, TRUE); + + if (Message->ToStation == NULL) + return FALSE; + + SMEM->NeedRefresh = TRUE; + + if (Message->ToStation->LastRXSeq[0]) // Have we received a Reply-Ack message from him? + sprintf(Message->Seq, "%02X}%c%c", ++Message->ToStation->NextSeq, Message->ToStation->LastRXSeq[0], Message->ToStation->LastRXSeq[1]); + else + { + if (Message->ToStation->SimpleNumericSeq) + sprintf(Message->Seq, "%d", ++Message->ToStation->NextSeq); + else + sprintf(Message->Seq, "%02X}", ++Message->ToStation->NextSeq); // Don't know, so assume message-ack capable + } + + if (strlen(Text) > 100) + Text[100] = 0; + + strcpy(Message->Text, Text); + Message->Retries = RetryCount; + Message->RetryTimer = RetryTimer; + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + + // Chain to Outstanding Queue + + if (ptr == NULL) + { + SMEM->OutstandingMsgs = Message; + } + else + { + while(ptr->Next) + { + ptr = ptr->Next; + } + ptr->Next = Message; + } + + sprintf(Msg, ":%-9s:%s{%s", Call, Text, Message->Seq); + + if (strcmp(Call, "SERVER") == 0) + { + SendAPRSMessage(Msg, 0); // IS + return TRUE; + } + + STN = FindStationInMH(Message->ToCall); + + if (STN && STN->MHTIME > (time(NULL) - 900)) // Heard in last 15 mins + SendAPRSMessage(Msg, STN->rfPort); + else + { + SendAPRSMessage(Msg, -1); // All RF ports + SendAPRSMessage(Msg, 0); // IS + } + return TRUE; +} + + + + + +extern BOOL APRSReconfigFlag; +extern struct DATAMESSAGE * REPLYBUFFER; +extern char COMMANDBUFFER[81]; +extern char OrigCmdBuffer[81]; + +BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr); + +VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // APRS Subcommands. Default for compatibility is APRSMH + + // Others are STATUS ENABLEIGATE DISABLEIGATE RECONFIG + + APRSHEARDRECORD * MH = MHDATA; + int n = MAXHEARDENTRIES; + char * ptr; + char * Pattern, * context; + int Port = -1; + char dummypattern[] =""; + + if (memcmp(CmdTail, "? ", 2) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "APRS Subcommmands:\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "STATUS SEND MSGS SENT ENABLEIGATE DISABLEIGATE RECONFIG\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "Default is Station list - Params [Port] [Pattern]\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "RECONFIG ", 5) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check failed - will continue with old config"); + } + else + { + APRSReconfigFlag=TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfiguration requested\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + if (memcmp(CmdTail, "ENABLEIGATE ", 6) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + IGateEnabled = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "DISABLEIGATE ", 6) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + IGateEnabled = FALSE; + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "STATUS ", 7) == 0) + { + if (IGateEnabled == FALSE) + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled "); + if (APRSISOpen) + Bufferptr = Cmdprintf(Session, Bufferptr, "and connected to %s\r", RealISHost); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "but not connected\r"); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "MSGS ", 5) == 0) + { + struct APRSMESSAGE * ptr = SMEM->Messages; + char Addrs[32]; + + Bufferptr = Cmdprintf(Session, Bufferptr, + "\rTime Calls Seq Text\r"); + + while (ptr) + { + char ToLopped[11] = ""; + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %s\r", + ptr->Time, Addrs, ptr->Seq, ptr->Text); + + ptr = ptr->Next; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "SENT ", 5) == 0) + { + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + char Addrs[32]; + + Bufferptr = Cmdprintf(Session, Bufferptr, + "\rTime Calls Seq State Text\r"); + + while (ptr) + { + char ToLopped[11] = ""; + char Retries[10]; + + if (ptr->Acked) + strcpy(Retries, "A"); + else if (ptr->Retries == 0) + strcpy(Retries, "F"); + else + sprintf(Retries, "%d", ptr->Retries); + + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %-2s %s\r", + ptr->Time, Addrs, ptr->Seq, Retries, ptr->Text); + + ptr = ptr->Next; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "SEND ", 5) == 0) + { + // Send Message. Params are Call and Message + + char * Call = strtok_s(&CmdTail[5], " \r", &context); + char * Text = strtok_s(NULL, " \r", &context); + int len = 0; + + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + if (Call) + len = (int)strlen(Call); + + if (len < 3 || len > 9) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Callsign\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Text == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "No Message Text\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + // Replace command tail with original (before conversion to upper case + + Text = Text + (OrigCmdBuffer - COMMANDBUFFER); + + InternalSendAPRSMessage(Text, Call); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // DISPLAY APRS HEARD LIST + + // APRS [Port] [Pattern] + + Pattern = strtok_s(CmdTail, " \r", &context); + + if (Pattern && (int)strlen(Pattern) < 3) + { + // could be port number + + if (isdigit(Pattern[0]) && (Pattern[1] == 0 || isdigit(Pattern[1]))) + { + Port = atoi(Pattern); + Pattern = strtok_s(NULL, " \r", &context); + } + } + + // Param is a pattern to match + + if (Pattern == NULL) + Pattern = dummypattern; + + if (Pattern[0] == ' ') + { + // Prepare Pattern + + char * ptr1 = Pattern + 1; + char * ptr2 = Pattern; + char c; + + do + { + c = *ptr1++; + *(ptr2++) = c; + } + while (c != ' '); + + *(--ptr2) = 0; + } + + strlop(Pattern, ' '); + _strupr(Pattern); + + *(Bufferptr++) = 13; + + while (n--) + { + if (MH->MHCALL[0] == 0) + break; + + if ((Port > -1) && Port != MH->rfPort) + { + MH++; + continue; + } + + ptr = FormatAPRSMH(MH); + + MH++; + + if (ptr) + { + if (Pattern[0] && strstr(ptr, Pattern) == 0) + continue; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", ptr); + } + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon) +{ + struct STATIONRECORD * Station; + + Station = FindStation(Call, FALSE); + + if (Station) + { + *Lat = Station->Lat; + *Lon = Station->Lon; + + return 1; + } + + return 0; +} + +// Station Name Font + +const unsigned char ASCII[][5] = { +//const u08 ASCII[][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00} // 20 + ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! + ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " + ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # + ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ + ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % + ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & + ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' + ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( + ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) + ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * + ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , + ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - + ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . + ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / + ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 + ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 + ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 + ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 + ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 + ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 + ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 + ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 + ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 + ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 + ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : + ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; + ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < + ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = + ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > + ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? + ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ + ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A + ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B + ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C + ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D + ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E + ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F + ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G + ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H + ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I + ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J + ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K + ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L + ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M + ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N + ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O + ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P + ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q + ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R + ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S + ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T + ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U + ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V + ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W + ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X + ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y + ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z + ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ + ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c + ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] + ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ + ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ + ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` + ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a + ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b + ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c + ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d + ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e + ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f + ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g + ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h + ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i + ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j + ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k + ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l + ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m + ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n + ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o + ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p + ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q + ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r + ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s + ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t + ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u + ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v + ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w + ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x + ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y + ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z + ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { + ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | + ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } + ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ + ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL +}; + + +// APRS Web Map Code + +// Not sure yet what is best way to do station icons but for now build and cache any needed icons + +extern int IconDataLen; +extern unsigned long long IconData[]; // Symbols as a png image.& + +// IconData is a png image, so needs to be uncompressed to an RGB array + + +// Will key cached icons by IconRow, IconCol, Overlay Char - xxxxA + +int cachedIconCount = 0; + +// We need key, icon data, icon len for each. Maybe a simple linked list - we never remove any + +struct iconCacheEntry +{ + struct iconCacheEntry * Next; + char key[8]; + int pngimagelen; + int pngmalloclen; + unsigned char * pngimage; +}; + +struct iconCacheEntry * iconCache = NULL; + + +// Each icon has to be created as an RGB array, then converted to a png image, as +// Leaflet icons need a png file + +#include "mypng.h" + + +struct png_info_struct * info_ptr = NULL; + +unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon); + +void createIcon(char * Key, int iconRow, int iconCol, char Overlay) +{ + int i, j, index, mask; + int row; + int col; // First row + unsigned char * rgbData = malloc(68 * 22); // 1323 + unsigned char * ptr = rgbData; + png_color colour = {0, 0, 0}; + int Pointer; + char c; + int bit; + struct iconCacheEntry * Icon = zalloc(sizeof(struct iconCacheEntry)); + + strcpy(Icon->key, Key); + + // icon data is in info_ptr->row_pointers (we have 255 of them) + + row = iconRow * 21; + col = iconCol * 21 * 3; + + for (j = 0; j < 22; j++) + { + memcpy(ptr, info_ptr->row_pointers[row + j] + col, 22 * 3); // One scan line + ptr += 68; // Rounded up to mod 4 + } + + + // This code seems to assume an icon is 16 pixels, but image is 22 x 22 ??? + +// j = ptr->iconRow * 21 * 337 * 3 + ptr->iconCol * 21 * 3 + 9 + 337 * 9; +// for (i = 0; i < 16; i++) +// { +// memcpy(nptr, &iconImage[j], 16 * 3); +// nptr += 6144; +// j += 337 * 3; +// } + + + // If an overlay is specified, add it + + if (Overlay) + { + Pointer = 68 * 7 + 7 * 3; // 7th row, 7th col + mask = 1; + + for (index = 0 ; index < 7 ; index++) + { + rgbData[Pointer++] = 255; // Blank line above chars + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + + Pointer = 68 * 8 + 7 * 3; // 8th row, 7th col + + for (i = 0; i < 7; i++) + { + rgbData[Pointer++] = 255; // Blank col + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[Overlay - 0x20][index]; // Font data + bit = c & mask; + + if (bit) + { + rgbData[Pointer++] = 0; + rgbData[Pointer++] = 0; + rgbData[Pointer++] = 0; + } + else + { + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + } + + rgbData[Pointer++] = 255; // Blank col + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + + mask <<= 1; + Pointer += 47; + } + for (index = 0 ; index < 7 ; index++) + { + rgbData[Pointer++] = 255; // Blank line above chars + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + } + + // Encode + + PngEncode(rgbData, 22, 22, Icon); + + if (iconCache) + Icon->Next = iconCache; + + iconCache = Icon; + +} + +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL) +{ + char Key[8] = ""; + struct iconCacheEntry * Icon = iconCache; + + memcpy(Key, &NodeURL[5], 5); + + while (Icon) + { + if (strcmp(Icon->key, Key) == 0) // Have it + { + memcpy(_REPLYBUFFER, Icon->pngimage, Icon->pngimagelen); + return Icon->pngimagelen; + } + Icon = Icon->Next; + } + + return 0; +} + +char * doHTMLTransparency(char * string) +{ + // Make sure string doesn't contain forbidden XML chars (<>"'&) + + char * newstring = malloc(5 * strlen(string) + 1); // If len is zero still need null terminator + + char * in = string; + char * out = newstring; + char c; + + c = *(in++); + + while (c) + { + switch (c) + { + case '<': + + strcpy(out, "<"); + out += 4; + break; + + case '>': + + strcpy(out, ">"); + out += 4; + break; + + case '"': + + strcpy(out, """); + out += 6; + break; + + case '\'': + + strcpy(out, "'"); + out += 6; + break; + + case '&': + + strcpy(out, "&"); + out += 5; + break; + + case ',': + + strcpy(out, ","); + out += 5; + break; + + case '|': + + strcpy(out, "|"); + out += 5; + break; + + default: + + *(out++) = c; + } + c = *(in++); + } + + *(out++) = 0; + return newstring; +} + +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb) +{ + struct STATIONRECORD * ptr = *StationRecords; + int n = 0, Len = 0; + struct tm * TM; + time_t NOW = time(NULL); + char popup[65536] = ""; + char Msg[2048]; + int LocalTime = 0; + int KM = DefaultDistKM; + char * ptr1; + + while (ptr && aprs) + { + if (ptr->Lat != 0.0 && ptr->Lon != 0.0) + { + if (ptr->Lat > S && ptr->Lat < N && ptr->Lon > W && ptr->Lon < E) + { + // See if we have the Icon - if not build + + char IconKey[6]; + struct iconCacheEntry * Icon = iconCache; + + sprintf(IconKey, "%02X%02X ", ptr->iconRow, ptr->iconCol); + + if (ptr->IconOverlay) + IconKey[4] = ptr->IconOverlay; + else + IconKey[4] = '@'; + + while (Icon) + { + if (strcmp(Icon->key, IconKey) == 0) // Have it + break; + + Icon = Icon->Next; + } + + if (Icon == NULL) + createIcon(IconKey, ptr->iconRow, ptr->iconCol, ptr->IconOverlay); + + popup[0] = 0; + + if (ptr->Approx) + { + sprintf(Msg, "Approximate Position From Locator
"); + strcat(popup, Msg); + } + ptr1 = doHTMLTransparency(ptr->Path); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + ptr1 = doHTMLTransparency(ptr->LastPacket); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + ptr1 = doHTMLTransparency(ptr->Status); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d
", + TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); + + strcat(popup, Msg); + + sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f° Speed %3.1f
", + myDistance(ptr->Lat, ptr->Lon, KM), + myBearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); + strcat(popup, Msg); + + if (ptr->LastWXPacket[0]) + { + //display wx info + + struct APRSConnectionInfo temp; + + memset(&temp, 0, sizeof(temp)); + + DecodeWXReport(&temp, ptr->LastWXPacket); + + sprintf(Msg, "Wind Speed %d MPH
", temp.WindSpeed); + strcat(popup, Msg); + + sprintf(Msg, "Wind Gust %d MPH
", temp.WindGust); + strcat(popup, Msg); + + sprintf(Msg, "Wind Direction %d\xC2\xB0
", temp.WindDirn); + strcat(popup, Msg); + + sprintf(Msg, "Temperature %d\xC2\xB0 F
", temp.Temp); + strcat(popup, Msg); + + sprintf(Msg, "Pressure %05.1f
", temp.Pressure / 10.0); + strcat(popup, Msg); + + sprintf(Msg, "Humidity %d%%
", temp.Humidity); + strcat(popup, Msg); + + sprintf(Msg, "Rainfall Last Hour/Last 24H/Today %5.2f, %5.2f, %5.2f (inches)", + temp.RainLastHour / 100.0, temp.RainLastDay / 100.0, temp.RainToday / 100.0); + + ptr1 = doHTMLTransparency(Msg); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + } + + Len += sprintf(&Buffer[Len],"A,%.4f,%.4f,%s,%s,%s,%d\r\n|", + ptr->Lat, ptr->Lon, ptr->Callsign, popup, IconKey, + NOW - ptr->TimeLastUpdated); + + if (ptr->TrackTime[0] && ptr->TrackTime[1]) // Have trackpoints + { + int n = ptr->Trackptr; + int i; + double lastLat = 0; + + // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros + + Len += sprintf(&Buffer[Len],"T,"); + + for (i = 0; i < TRACKPOINTS; i++) + { + if (ptr->TrackTime[n]) + { + Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); + lastLat = ptr->LatTrack[n]; + } + + n++; + if (n == TRACKPOINTS) + n = 0; + } + if (lastLat != ptr->Lat) + Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->Lat, ptr->Lon); //Add current position to end of track + else + Len += sprintf(&Buffer[Len],"\r\n|", ptr->Lat, ptr->Lon); + } + } + } + + ptr = ptr->Next; + } + return Len; +} + + + + + /* The png_jmpbuf() macro, used in error handling, became available in + * libpng version 1.0.6. If you want to be able to run your code with older + * versions of libpng, you must define the macro yourself (but only if it + * is not already defined by libpng!). + */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->png_jmpbuf) +#endif +/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() + * returns zero if the image is a PNG and nonzero if it isn't a PNG. + * + * The function check_if_png() shown here, but not used, returns nonzero (true) + * if the file can be opened and is a PNG, 0 (false) otherwise. + * + * If this call is successful, and you are going to keep the file open, + * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once + * you have created the png_ptr, so that libpng knows your application + * has read that many bytes from the start of the file. Make sure you + * don't call png_set_sig_bytes() with more than 8 bytes read or give it + * an incorrect number of bytes read, or you will either have read too + * many bytes (your fault), or you are telling libpng to read the wrong + * number of magic bytes (also your fault). + * + * Many applications already read the first 2 or 4 bytes from the start + * of the image to determine the file type, so it would be easiest just + * to pass the bytes to png_sig_cmp() or even skip that if you know + * you have a PNG file, and call png_set_sig_bytes(). + */ + +unsigned char * user_io_ptr = 0; + +void __cdecl user_read_fn(png_struct * png, png_bytep Buffer, png_size_t Len) +{ + unsigned char ** ptr = png->io_ptr; + unsigned char * ptr1; + + ptr1 = ptr[0]; + + memcpy(Buffer, ptr1, Len); + ptr[0]+= Len; +} + + +// This is based on example https://www1.udel.edu/CIS/software/dist/libpng-1.2.8/example.c + +// This decodes a png encoded image from memory + +int read_png(unsigned char *bytes) +{ + png_structp png_ptr; + unsigned int sig_read = 0; + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also supply the + * the compiler header file version, so that we know if the application + * was compiled with a compatible version of the library. REQUIRED + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + { + return (0); + } + /* Allocate/initialize the memory for image information. REQUIRED. */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return (0); + } + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + + user_io_ptr = (unsigned char *)&IconData; + + png_set_read_fn(png_ptr, (void *)&user_io_ptr,(png_rw_ptr)user_read_fn); + + png_set_sig_bytes(png_ptr, sig_read); + + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, NULL); + + // Data is in info->row_pointers. Can we use it from there ?? + +// printf("%d %d %d\n", info_ptr->width, info_ptr->height, info_ptr->valid); + + return TRUE; +} + +void Myabort() +{} + +// This is based on pngfile.c + +//------------------------------------- +// PNGFILE.C -- Image File Functions +//------------------------------------- + +// Copyright 2000, Willem van Schaik. For conditions of distribution and +// use, see the copyright/license/disclaimer notice in png.h + +// Encodes pDiData to png format in memory + + + +void my_png_write_data(struct png_struct_def * png_ptr, png_bytep data, png_size_t length) +{ + struct iconCacheEntry * Icon = png_ptr->io_ptr; + + if (Icon->pngimagelen + (int)length > Icon->pngmalloclen) + { + Icon->pngmalloclen += length; + Icon->pngimage = realloc(Icon->pngimage, Icon->pngmalloclen); + } + + memcpy(&Icon->pngimage[Icon->pngimagelen], data, length); + Icon->pngimagelen += length; +} + + // io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); + // Area png_uint_32 check; + + + +static void png_flush(png_structp png_ptr) +{ +} + +unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon) +{ + const int ciBitDepth = 8; + const int ciChannels = 3; + png_structp png_ptr; + png_infop info_ptr = NULL; + png_uint_32 ulRowBytes; + static png_byte **ppbRowPointers = NULL; + int i; + + // Set up image array and pointer. First allocate a buffer as big as the original + // in the unlikely event of it being too small write_data will realloc it + + Icon->pngmalloclen = iWidth * iHeight * 3; + Icon->pngimage = malloc(Icon->pngmalloclen); + Icon->pngimagelen = 0; + + // prepare the standard PNG structures + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + { + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + return FALSE; + } + + { + // initialize the png structure + + png_set_write_fn(png_ptr, Icon, my_png_write_data, png_flush); + + png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + // write the file header information + + png_write_info(png_ptr, info_ptr); + + // row_bytes is the width x number of channels + + ulRowBytes = iWidth * ciChannels; + + // we can allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL) + Debugprintf( "Visualpng: Out of memory"); + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < iHeight; i++) + ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2); + + // write out the entire image data in one call + + png_write_image (png_ptr, ppbRowPointers); + + // write the additional chunks to the PNG file (not really needed) + + png_write_end(png_ptr, info_ptr); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + // clean up after the write, and free any memory allocated + + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + + // yepp, done + } + + return Icon->pngimage; +} + diff --git a/APRSIconData.c b/APRSIconData.c new file mode 100644 index 0000000..1716af9 --- /dev/null +++ b/APRSIconData.c @@ -0,0 +1,428 @@ +int IconDataLen = 11434; +unsigned long long IconData[] = { + 0xa1a0a0d474e5089, 0x524448490d000000, 0xff00000051010000, 0x15c97f0000000308, + 0x4d4167040000005a, 0x8a0537c8af000041, 0x58457419000000e9, 0x72617774666f5374, + 0x2065626f64410065, 0x6165526567616d49, 0x3c65c9717964, 0xffff45544c500003, + 0xfffffff7f7ffffff, 0xfff7f7ffffeffff7, 0xf7effff7eff7f7f7, 0xdeffeff79cfffff7, + 0xefefeff7efeffff7, 0xefdeefefe6f7efe6, 0xe6efe6e6f7e6efff, 0xefe6def7e6dee6e6, + 0xfffff7e6d6e6e6de, 0xe6f7dedeffff9c00, 0xdededee6dededede, 0xe6bde6ded6efded6, + 0xc5e6d6dededed6ff, 0xefd6cef7d6cef7de, 0xd6cef7d6c5d6d6d6, 0xd6d6d6ceefd6c5de, + 0xdeced6e6d6c5e6ce, 0xced6d6d6c5ded6c5, 0xced6cececed6c5ce, 0xe6ceb5cecec5cece, + 0xc5b5cebdcec5c5c5, 0xbddec5b5f7ce9ce6, 0xdec5adf7c5a5c5c5, 0xbdbdbdc5b5c5bdc5, + 0xb5debdadbdbdbdc5, 0xbdb5bddebda5c5bd, 0xadb5c5b5b5f7b5a5, 0xa5f7b59cb5b5b5e6, + 0xdeb59ce6adaddeb5, 0xb5a5d6b59cefada5, 0x94efb58ce6ada5bd, 0xadadad319cffd6b5, + 0xad8cd6ad94adada5, 0xceada5a5b5a5a5d6, 0xa9a59ca5a5a59494, 0x9c9ccea584cea58c, + 0x7ba594a5949c9c9c, 0x94949cde9484ce9c, 0x9494e6947ba59494, 0x73d68c84de8c8494, + 0x9c8c8cc59473ce94, 0x8c8c948c8cc5946b, 0x52f79452ffbd008c, 0x63ffc58c6bef94, + 0x8484c58c6384848c, 0x848c8484bd8c6394, 0xde737bc584638484, 0x7373ce737bd6737b, + 0x737b8473848473e2, 0xbd845a7b7b84d673, 0x8442847b7b8c846b, 0x738473847b7b7bef, + 0x7b7b7300ce00da6b, 0x7b52737b73ff9c00, 0x778473737b7377bd, 0x7b6b7b6b73737373, + 0xbd006b736b777367, 0x4a736b7331639c00, 0x736b6b6b6b73b573, 0x6b6bde6b42b57342, + 0x63736b63636b6b6b, 0xb56b42ff00ff6b6b, 0x6b3a6b63676b6b5a, 0x73636363ce5a52b5, + 0x5a635a635a6b635a, 0xff635a636b5a63, 0xad63315a5a639c, 0x635a5a6b5a5a009c, + 0x4a4aad63295a5a5a, 0x3a5a5a52ce4a4ad6, 0x525a4aad5a29d64a, 0x52524a5a4a4a525a, + 0x52ad5a21524e5a5a, 0x4a5252a55a215252, 0x3a4a52524ac53a52, 0x52d63a42008400ce, + 0xce3a42bd3a4a524a, 0x4a52a552210000ff, 0x3ad63a3a424a524a, 0xc53a3aa55219ce3a, + 0x4a194242520000ef, 0x4242424a4a424aa5, 0x4242420000de4a42, 0x423a2100c542423a, + 0x42423a420000ce3a, 0x3163423a3a3a3a, 0x3a3a3a3a3a006300, 0x293a313a423a3131, + 0x3131313a3131313a, 0x31213129313a3129, 0x2931292929293131, 0x2921292929212929, + 0x9c0031212121, 0x19ce0000de0000ff, 0x1910109c0000210c, 0x147b000000840000, + 0x4449302900005c89, 0x980b9decda785441, 0x8a6766ab80555724, 0x2f20ac1bba2264c4, + 0x14616a09fa3442f9, 0xe0a020be3e11a404, 0xc621e50458166a22, 0xcf8bf5a421a11e, + 0x544051198a2896da, 0x5f0420c1318f891e, 0x8aacb222611647c4, 0x6fb4ac2ef48ec63d, + 0xcf7df78f64cd27b7, 0xe86ceecf7755567d, 0xf8f5d5b5bd55d3db, 0x53def73dce7b9cfb, + 0xe24092d7328b0249, 0xac9029e663796729, 0x1f177f262f8b81c, 0x12cf7e2f27b7ee59, + 0x3be9f958b64c104d, 0xe5a271edfbc6f2ad, 0x271bc11c8e01eea4, 0xb76367803afca956, + 0x5140515efa2898b, 0xdf7949479df17a21, 0xf3b94547ddfebf93, 0x744e78a40689e8e2, + 0x4e88b7850d44be6c, 0x92ca94495a371526, 0xb113ae84969a7a20, 0xff65138d23540d04, + 0xaf8a3288cba1745b, 0xb1d8f444cee7b2cd, 0xd9bff537369cd563, 0xe5106a1d569ee0e7, + 0x48d11c8e9e4755e2, 0x110cca4c9cacd131, 0x5130ce7faa23ed3d, 0x2df464c0383392f7, + 0x442e607a22b57659, 0x31a06a6e89c624c1, 0x4b6e24ad68e2e2a2, 0x11c8f44d5b1a9513, + 0x68922b174a20d484, 0xf2a0602b0ca42996, 0x91f3bd1394640db0, 0xabbaef443a7026df, + 0xbe31d8ec79652882, 0xf8a90a9b7d828832, 0xb44c15fb2aca2510, 0x5588f44c32c1eb34, + 0x2510f3344d33443c, 0xa88750b79149cd67, 0x295cb8bfd13947a8, 0x6663d128aef44bbd, + 0x49490488adc14441, 0x415290a809944636, 0xaeef24bb90404bb4, 0x27f48e328ca0d390, + 0x89a21ea3d66a7291, 0x2908e79e3d5550a8, 0xa3760b4498b49e28, 0x7a4fe5e8cb49db05, + 0x4fd6af41fadf5beb, 0x44a96311157c144b, 0xa21c741833a89d55, 0xae2f15109eb4a97a, + 0xf1aecdcddd00eaf8, 0x9bf142fbeca051e3, 0xbb31e52e41406511, 0x5f89b73005a268a8, + 0x7ee43c17eefa248a, 0xde89f83fd57d25c4, 0xb48a228ab0344fc5, 0x58ac9b44556a25de, + 0xcd1063ae1e875e35, 0x528cb947a8ccd124, 0x60e6dfba5d00eaa2, 0x944a54755dd749b0, + 0xb6d7ad6d49a8b4de, 0x9ca2fe49c615125e, 0x23299c9e0dc7aea8, 0xa22a90a94477005a, + 0x37adf21f63d17dbf, 0x85de8c9087f37cdf, 0xa63fbb696351254b, 0x2536c9ee5aa02238, + 0xa895993274415f9, 0x85128fb5728875a0, 0x23d0fd13402d1382, 0xa5cda3f7e5e7719e, + 0x2875c1445e6fbc47, 0xd1344e8935074463, 0x536812a1b99eb469, 0xc8e882972eaa17ad, + 0xddecd6ff87b8f474, 0xe0715e522af8df47, 0xdfac3e1c0289235, 0xb613de17b7dca211, + 0x13f5a8e7daa8b9f5, 0x44cb536ea65b9441, 0xd932ca9d014cd0fd, 0x408820e97a073eb6, + 0xff3349e78df465ce, 0x5302cd87a24ac7e4, 0x4ce88b19d339a3f8, 0xd3dec329ef566996, + 0xe89296713de2e64, 0x565be8f3b92f96e8, 0xf79c57a87dca3e6e, 0x99ce688dce77a3c1, + 0xf0f47364ad3de4ce, 0x12c27c3c5ce80333, 0x3a1ff5a16ba4f04, 0xf81d27fcfa22e22f, + 0x63fbf5a7c8bfc7ae, 0x9b44c35417959544, 0x15a19a66d399f87b, 0xe450ad74e818959a, + 0x2a023460147ab28e, 0x62bc9488890d74a4, 0x1719f9bd15c445e9, 0x8c2df93d17c9eabe, + 0x3ba64cdb44ab7ee8, 0x259d0ad2e69ef61a, 0x7eb4438910f19252, 0x92465dbbf0f6c4a4, + 0xbfcfa3d1244eb216, 0xf11e8bc6ff8f21e8, 0x4da3cef9be6f4723, 0x448cf465c47b3289, + 0xcaf7fb0f3466ce4a, 0xc4cb535f289ee51f, 0xfb9e3be8bcb7d09c, 0xd193392910cdf79a, + 0x9b81fa5fc31e3dbc, 0x533f8622caf572ad, 0xda3d9255917248af, 0xe7773d079f28b22b, + 0x443a215d9205c6f6, 0xec6f98cb56bed45, 0x43d8da8a8e636a2a, 0x371b1027d346d45, + 0x51df1e2a34f1b515, 0x2abd6e27eff6b935, 0x2d05242d7ac70f56, 0x2b23746f05aaf223, + 0x58290b438744a2a7, 0x8290b1d3a1b05540, 0xa43280b05219405, 0x161b1b8d8968ed89, + 0x96181726ea80b052, 0x216d61d10885138a, 0x5660f13cf62b48d3, 0x9966512a2315ba5e, + 0x5036037e27890a44, 0x40d82914a06c148a, 0x2361b1bae790d229, 0xfc6580206ca81b05, + 0x93289cd84232a21d, 0x9582bc6f3d8d51be, 0xf25e5fd44ba222e, 0x2a20de2f2a21ccd1, + 0x98dece7b894459b6, 0x88346c5449bafca8, 0x451351be51166d8a, 0x907303b594813dad, + 0xacb0e5a32983f738, 0xf46e083a3b1a8a22, 0xc3e07fad6a8804c9, 0xe5c97baeebecffee, + 0xc1e5aa21ccd136cb, 0xa89b5668929b3443, 0x288aa603aa20d3dc, 0x46d194d5d859fb2f, + 0x2ae804351d401344, 0x1fb7e0bdbaaeaeac, 0x5e0bc6e5dbf6e562, 0x3e47c4ff1f65e07e, + 0xa25e6688b72f9efe, 0x60ca22deb4b2b503, 0x9600028f5a3d44ba, 0x54cd1112f544bbd6, + 0x4e549b2348df23df, 0x3c3e3d008c853721, 0xe1d944182887a5e, 0xfb0b44e500418eb5, + 0x67d192a8f5176feb, 0x87c771ddb771fd3d, 0x7ad6aa5e2fe3eaff, 0xd2ba24a65744fc5e, + 0xca208ae8804ae890, 0xb44381f35c069ed4, 0x950e03b94459ec37, 0x4aa02d1190b8a144, + 0xe8fcaf99f27e9746, 0x5d7a3e8fe3bb6ffb, 0x37bd0a8f5da89356, 0x9efad1e44bdd51ea, + 0x3d456802a25deb4f, 0x797d7252133d68ea, 0xa2138f2fad4dcc3e, 0x2dd1156ce8cd4852, + 0xb4d5ba6fda04a43b, 0xedbe1fb4fb3f2bf5, 0xb1fb6ff5edb98f31, 0xd68859cf05445a95, + 0x347a9ccd13eb47a8, 0x28870f9174a0501f, 0x152242c316804ce2, 0xe426ace93bb327b6, + 0x7416c293e823bf26, 0x73ca40deb7af8b9d, 0x7b6fa3f2bf5b4cb6, 0x3ad9fb4fc3fa253d, + 0x2759547aaacf1544, 0xa89c65ea88727bd7, 0x5c544d72f1951eb7, 0x7b4027c31453931f, + 0x5deb029027b761dd, 0xa221ac5de06f2d95, 0x86f0ca97da24a59c, 0xf75df744590e526d, + 0xfa3ffbdb71dff789, 0x69ef2d4ae4fe67ca, 0xa512d3a04a88a670, 0xaf6794a20a88885, + 0xebeca671e8a9962, 0x8289c53f4e73099, 0xbf92f4ba251d749e, 0x8f6dc771df0fabfb, + 0x9d339eb67cb3fa7b, 0x3ef5a46d11f27bc6, 0x72027ad5c9ef1ea2, 0x642060dfe80e1838, + 0xf94425d9cf4bbd74, 0x214fc8d0c2a32907, 0xa5215016d6eb64a2, 0xbf972689f952e96b, + 0x765fc7c4f91f13e7, 0x9b53b448cc17e5e0, 0xd689aa4fe1e7cf68, 0xbb4874415009f0f4, + 0x32884e38a15474f9, 0xd58998eea8f5d521, 0xab590b492336eda3, 0x7d6eb74bcb45b86a, + 0xe5fc3da55d5715e9, 0xffbecf75dd7c5fcb, 0xfd86ea8569e81ec3, 0x856ad6887a856859, + 0x32ed1110e912a272, 0x3d3e1ebd44d5da2, 0x64fb8a3fb479fa81, 0x452b0379cf13c6d8, + 0xa258a9c853fcda97, 0xc564a46ce74b38bd, 0x2681ef151908948a, 0x231a42a51294444a, + 0xf083364ad28922ba, 0xd0f7637eca606f9e, 0x28f3460fbb77d88d, 0x5bae8822f2955a51, + 0x6770ce16a999da25, 0xba8edcbc5b53cf63, 0xdcff0c45e711173d, 0x4595f7bdb628ba24, + 0x4b445befeb42ddcf, 0xed8bc7da54ebccab, 0x28f4976e649563b9, 0x27fd58d447054780, + 0x1e628b1f5f411fdc, 0x6671651b063728b3, 0x4b688a5ace63da88, 0x44f6944529bc346e, + 0x191129b7a89845a2, 0xe8bf0a3046a06f1, 0xe3c744d9ed1262a2, 0x95765481333c232c, + 0xd7ab4bcbd06f7994, 0x66cb3f2fa50e9cd4, 0x279896830f8f6a22, 0x38b6347b6f8342d4, + 0x579df2e4b4e3d135, 0x107a129a88b4ca8d, 0x34e2944e647eb513, 0x814bce7125a01289, + 0x3c499445ab3c1f54, 0x6afed3c0587e7b17, 0xf835a57dd7a3b88c, 0x5a6cbb2ecbe0f7b6, + 0xafe6f247094844a2, 0x8bb3d70680ae71aa, 0x64f17289c742c637, 0x1a0b250498fac750, + 0xbe3eadb513a3592, 0x43fd09162f622513, 0x2f2570a325a80a0d, 0x22a8cbd365e8c860, + 0x89bc0a20ad19339a, 0x9ed13075aca89803, 0x31f5f35ca2211755, 0x121a6ea5a83284a9, + 0x5448686d96186da0, 0x775ab1d11d7d601c, 0x9a8691a0a0713dd6, 0x5e68c95594cb7ea6, + 0x6e7d51258bc71441, 0xa8a89fa922bed44d, 0x5add4b2795706e63, 0x8fba646e18334026, + 0x1ac0eba26bd696af, 0x23a79ad97b4e0ca, 0xc7ea887df4a46828, 0xd12f5197e27c3321, + 0x9cf0401471e7c944, 0xf67251394815011d, 0x24876a229fc8f41e, 0xc5728871901e5134, + 0x1a6dda0cd00944d7, 0xcae64a38a2236c00, 0x264b9e1a40d441a8, 0x146e21e37ca40a81, + 0xcef19213d44f4d0e, 0xbe94415239404364, 0x214f7c3f02b51300, 0xb06e892a43279c9b, + 0x5647ae0a2686cf18, 0xe4130612eaa26887, 0xe9b805195e4f7232, 0x4a21d52dc89bc1e0, + 0x982894d6515c44af, 0x34474e994a20c144, 0x182db11299b08b19, 0xd0f75b8607b9ef0e, + 0x6fb0ae8f53af5c32, 0xc53da90c09e5518f, 0xd5053e8f593bd6cb, 0x3209e673c967c4a3, + 0x375bcf9441543d71, 0xd6b8a75c223f1e89, 0x5b59271c5b93ff43, 0xd68cb21dbd686a32, + 0x1cc7650247690ca3, 0x5099ee7bd1281a7e, 0x8ed68723d6dcd0a4, 0x94dc2888f5b0d611, + 0x6e5193e4cb5104df, 0xadfd8bb11a35549b, 0x7a910d74f5353ead, 0x40ea51eb75130934, + 0x9b2600aa8d029d15, 0xcb1e94455292a940, 0xa951fc8da45d47f2, 0x4da8f5ba8459c2f5, + 0xedeb63d44b4f8f4, 0xa3aa876a93de1b77, 0x441430d5a8a671eb, 0xd57a1c9336ea6b61, + 0x7ea329932d24cdb0, 0x4a26af3465b146a2, 0xd1fcf932d8a230a, 0xd62a274511231d12, + 0x9ea15b9760ac608b, 0x439ebfe89ef0bd76, 0x75c2268cb9a73c3e, 0xb81336a288a993ca, + 0xd9be6b942b66351d, 0xe6dc6eb86486a7ae, 0x4948c5debd4c9316, 0xe8efaea22353d8ed, + 0x9bd2a606897bb3d, 0x108a64d5324a4514, 0x459b1b7abcd1a33d, 0x726738fe9d4442f3, + 0xb3a8f347f4ebc361, 0x45fc31179c445dd6, 0x5a16ee7a2ca2ddcf, 0x3444cc51636767f, + 0xedd49267aefd0209, 0x8dd2aed8f979ab11, 0x4a2576da7271eee9, 0xdf5f3b6ed9ec62d0, + 0xd0288061dec12566, 0xabed98046e74fc61, 0x7b44ab740a0c0d04, 0xbe3e63c7ec5a92c4, + 0xd1bbc09c02fe71c9, 0x579dd58afde326cd, 0xd155b744626f495a, 0x833955cf05440294, + 0x7888c289b1f44f07, 0x89b8e623d5a823be, 0x875c6c663d63f65e, 0xfc9e236e413539a8, + 0x116a7449e9ebb60d, 0x3cecbe86586940a5, 0x3d29136dedd13642, 0x17278b8144bc177b, + 0xa24ca12990c2d554, 0x23de34c4a675c46c, 0x7861e31fb3f12166, 0x93149a005b6a1ae3, + 0xb86e1ba36da3eb89, 0x21e77557ac3b3cc1, 0x6e88b104a56965a0, 0x22a91b5119485b6f, + 0x6688885eac2682a, 0x4b48e54418273722, 0x41ced1345112f44d, 0x947d9c7eeb25c711, + 0xe1ff28830ac847a8, 0x445a932744b81833, 0x6781d5da0a564903, 0xd36cad9901196631, + 0x76bb7548c895cb35, 0x2e51eb2a22e01e2a, 0xc98934461f44c6a4, 0x4e4a91490f496128, + 0xb9ef42ed5f23acfd, 0xb20ab7fb65bf3079, 0x5938606cc73da9f, 0x76b0d4543bb7935d, + 0x191d42eca32da804, 0xfac927d64c9f5f5d, 0x1111ba13b06a11ba, 0x32892d62bed4d09d, + 0x344e6df9aec8bc9c, 0xa4bd2ed115b74413, 0xd144c0522aaa26c2, 0xe70a21c8e8835414, + 0x4eaf572768880293, 0x447d87a26dc9d3bb, 0x3488823b517bb0d9, 0x60145f5761206291, + 0xceba22255e9f68cb, 0xbc8cd128b0cef288, 0x8ab6da20b5b3ca88, 0x8daa6bb44416944d, + 0x4d5033c512600a26, 0xc94827ce144d99a2, 0x56d3a69e31a8c902, 0xc4899d3d120fcf44, + 0xb376e945c764ec70, 0x7e5544d85d8a28e3, 0x2133491c81ee7bda, 0x3ac8dcf0411a89c9, + 0x4e4a2217ca28d2a8, 0x1cc7e0b151361623, 0x8c4d116a8aeaa471, 0x35485cf9e88057d8, + 0xda9f24e047400ed1, 0xd290a0a26ccd1971, 0x2d1242af9cae3e0b, 0x5faa139da5192c0, + 0x8c4a25ea3d4f5218, 0xc4c4cb5ea26b7b3c, 0x6a27ebb485ca21d3, 0x8fc528835946bd6b, + 0x8d447c5147287689, 0x5e157ad63bab947a, 0x41a802a24b8f44a6, 0xbac4cb9443a8ca54, + 0x5a182a012ae70de1, 0x437757288868c96f, 0x7ef509961ad235eb, 0x2f6eb4a3d46b4458, + 0x9a328ca8922cd194, 0x8e74f051eb7265b6, 0x9f3d6a6eed946587, 0xb2b344da0ed3ace5, + 0x73dc44cb2378a882, 0xb547f693de421481, 0xb0a86036792c6c87, 0xa5209447d47f7ef5, + 0xf8fe8e3a7bc7a8f5, 0x4a5195103521e275, 0x5c8bed52248c413, 0x8fed9d4522b48951, + 0x485027bcb6df8532, 0x585fe1e142ff0f6d, 0x455a3f9544cb9445, 0x21693ad2a3f8ea14, + 0x6d08009d19628133, 0x1e265b12cc43ffe6, 0x785526dca22d79a2, 0x324342b58d2360f8, + 0x44bcc47b46ac67aa, 0x58b27a8568367bbd, 0x8356076db8bf84eb, 0x9a4aac7036874415, + 0xa5d9f5f134448055, 0x804a078a856f14e3, 0x5e49ee84948e1a42, 0xda1ebec65b681e56, + 0x259c6cc5c4a469b3, 0x77aa22b484737674, 0x30948f1a2fb544c2, 0x79b48e4f6668cd21, + 0x2d9b3beeacb38f34, 0xb61af4f511bb79a, 0x279a2867812b512, 0x188bce222d7e0ea9, + 0x10b773d16ee7a2fe, 0xe6bd44d39fd685b2, 0x1c0695cb3623d432, 0x1e89d1a3af37dcef, + 0xf79e17f2fe273ff5, 0xfd9fbc78e3724b55, 0x721cce1e3f7fb291, 0x363511f9c2ba787e, + 0x9e0bfcb1e77462dc, 0x1b492ca51279a13e, 0x7e888d082ce397aa, 0xfcfebbe77c6e07ff, + 0x1398bf87bdf1ffaf, 0xb8fb5bd5fb5f4fdd, 0x60c1a3e839632822, 0x73f944c16f446bb4, + 0x1dac7b75b95d3c40, 0xbd4242a65ec132a2, 0xde45938f0e504e5e, 0xf75f448693ee44cb, + 0xf03f27d7fd4ea7e7, 0x2a2314ff07f1bf2b, 0xa46a8125c8285ca3, 0xbb6d593d8a21dd60, + 0x2b13e544c16f447a, 0xd6d2346d5f68cb6a, 0xb24387ccd15289c7, 0x689eedb31f6ffa26, + 0xff5f2e5dd2b7dfa3, 0xf67e7fdbfecfa327, 0xee88287f75f7ffbf, 0x51505c1cfd1510f7, + 0x144954736fc47abb, 0x544c16f44daaa665, 0xf2708c13da25523f, 0x435498e9d3b4463f, + 0xaca2109a811e9344, 0xcdf46d9fb441a802, 0xeffa7fcbf45fc679, 0xfaffd3e2713eef27, + 0xd28f5da888e3f13, 0x522540c079c11b20, 0x7e694452210b8c9c, 0x401801b87442e827, + 0x453dac0c17945115, 0x54a52fbbe4b4532, 0xdfc944235418ed1, 0xb40df7f16db5d687, + 0xf177186feb344d8, 0x753e5fe67fdff37a, 0x11ef8efedc4f9fe2, 0xed1160d10f3da069, + 0xbea1132a17ce0cae, 0xc04c39697c8e91cd, 0x82d86401bab2ca20, 0x144c16e54c0f719b, + 0x10e85e0ca81b5a27, 0x755f7e972aac3a2a, 0xf2962542f4500f39, 0xb740bb4a21c56513, + 0xfc679e2904a201da, 0x6a7c4f67edfc77c8, 0x1be9bf6f53e9feb, 0x65a05288b4dcdd13, + 0xe291ca04c03bfaa9, 0xcd944394188909a7, 0x88f40d496e48821f, 0x338ca33ca9701a22, + 0x8bf41fd9f726ad11, 0xa95b83a74baa9c24, 0x27d6c375b5b5ba26, 0x9f637cdbc483a06f, + 0x49ffbf416c9fbf1e, 0x9e6bd4febf23b9f2, 0x7cffedd37cefa8f0, 0x89c85fbbf53f33e2, + 0xa21d360c1d01803e, 0x3997991d1119c7b4, 0xb5703118f9630142, 0xa5a9904f5e50cb3e, + 0x79a88aa12c344e50, 0x36374420a24e082a, 0x12fbf45f1c1d1fe8, 0x47ad61190a4f48c9, + 0x2217b4e944a2f629, 0xe206ca26816e74a6, 0x7f37d0f9ef9f778b, 0xe9bf87abf9df0bdf, + 0x67e97f37f6bcae6b, 0xca4128875a32412, 0x34cd0d5847bd80d2, 0x4c90c87a21e93c69, + 0xa4b7281ff2645678, 0xe0a0cd7525115401, 0xa2502069d447c2cb, 0x1953d2ad7251bc85, + 0x1f59deb3a4d1ebc5, 0xb36851ea83b2e90e, 0x67ca269dc728df99, 0xfb5febfedf8db1de, + 0xadd9fd9fe3743f85, 0xa7aefe1e13e17f6f, 0xaafe5019eb5cd26, 0x3188f467ce9eaa2, + 0x89aa00d25b91805d, 0xed4a81af10addf92, 0x9d166fbd144e05, 0x63bd744c3424847d, + 0x7f2da01b7388acbe, 0x4f908b262be13eba, 0x5fc1efbab63e92fc, 0xfe3ffdfa6fc3c4f3, + 0xd4d259edcb7ef7d8, 0xba66668ca6a22132, 0xa22ae9de0d544e49, 0xa00d25b99824a33c, + 0x6f4e28cb6074648, 0x4fb2c0a6a0fb464a, 0x16a261a12d673c8, 0xdcdb93e836ee48d, + 0xfb783f25bd512671, 0x1f2beb56c4fa1f0b, 0x317e6fe03de7d2fd, 0xa3fb1fb4f3b944c1, + 0xf5544fdeb4c4f7ae, 0x574136e1267ee9c0, 0x4c6803496e6f779b, 0x285a2336f279c146, + 0x9342c74d6253e4a2, 0x967468134b44bc87, 0x8dd13ea5af9f16d0, 0xcbe2beaf5b7bfd5f, + 0x4e77e03d97d97c1e, 0xd799047ad449817e, 0xd375a0fa28c88fc3, 0x882a5bfc3d84e268, + 0x3495fbe173511616, 0x9d949d1289dc64d1, 0x349cbd72f5efe4b2, 0x6bf1ff03fa3e856f, + 0x7dcff9fa0fa5fcdf, 0x5f3750f9a8cb6245, 0x5937a1a28f50342b, 0x6bd44bc5b8115ef3, + 0xc9cfc6c4f62177eb, 0xffef7baf929174d7, 0x44924a7b9f73fadd, 0x6d7ad809291bd465, + 0x8bcd1b4ddce79c28, 0x2c479bcd1ce6f885, 0x8deb77ef70bf01f3, 0x783f63ba32251fdc, + 0xc445fc3cf87409d0, 0xca2ddcf45fc31179, 0x3d12ee7f5a174642, 0x3a8f843e29655fc4, + 0x8cd45d1a67a9db1e, 0x102f77d73d05175f, 0x403d95643af00df5, 0xf67a26e639cbd414, + 0xed3497bda343c574, 0xeb6c6a0017c6629e, 0xa8c81a113460d8d1, 0x6b79d334494e638c, + 0x809c6f7bb01eda78, 0x6fd3d96b9e4c7db5, 0xfd57d75f0089e9e8, 0xd7c86ca01c6771ae, + 0xd116a4e349293a24, 0x2944fc5935d10eac, 0x4c44dc618b4eb1cf, 0x8f1445b3eafa6546, + 0xa1ad1185e5130bc9, 0xdeb2eb6ac7271cb6, 0xad659d8d7f1c8d73, 0x3c066fa373ba288a, + 0xa5a891cf8233aa37, 0x8a9d656760a22125, 0x875490a88a5175a8, 0x61a44a89a4994aa8, + 0x46c4e0bac0ebbaa2, 0x88f1a88dcfac9382, 0x39ea891985196ac6, 0xec744fd8f1d1167b, + 0x6cbfc1a565481510, 0xac044369d4d04d10, 0xd0a5893eb799b6e6, 0x152285fe9375120c, + 0x8a3215f5b3d663de, 0xf2897b1e3a20d46e, 0xc954caf16cba6b8d, 0x575f1cc544985b28, + 0xa488028fdbb38d19, 0x11f4db27f512f4d0, 0xa3215f271ad31c0d, 0x8ab71def128a2525, + 0xb47fa630a26a8f53, 0xd5052cd38c78da80, 0x850077510f7ad4d2, 0x6a2073a32bc1b33b, + 0x72d1084e88dcce88, 0x9d66cc7bcca89b52, 0x755e30ab27798801, 0xbc305118fc585464, + 0xb3bd6a6ef3426728, 0x4ac9cb4486cf6473, 0x18986d45eb6da884, 0x35d6e9d7a8a73d, + 0x399a20d5ba7c9349, 0x30b1a65f27bdc996, 0x4654414d3de033d1, 0x4cb2a89713447dcb, + 0xa5004d36a35a8bde, 0x3f71f0f0ccd1309f, 0x3ecf6553d93de98d, 0x6959f5a151d1bb51, + 0xf7adb123348be8a5, 0x54c47aabe29a145f, 0x4502360081a34512, 0x32bd444bdaf44d94, + 0x1b2b34044f5b21a, 0x86aca2e068fed51, 0x31a8e4e5cdaaa221, 0x2ef76533d73cbddd, + 0x27d49f9d8801e0d1, 0x21cc4a46334cdb8a, 0x34c948fddd12d399, 0x79a2267f5afa9344, + 0x466bcd1833a7bd35, 0x1b0bdfc31171b174, 0xd685bb9e8b6d7d17, 0x391d93efad12de7f, + 0x1d2e34cba23c6d62, 0xaf8672a7bd664b77, 0xed6a1f4fd7c6467b, 0x289b351d7afae159, + 0xf8f15a4097929943, 0xfbb9d6ea724ff039, 0x7cdd7e8d035eb850, 0xf3b9cc5680781ece, + 0xa42634539ef64f78, 0x69022d281c8dca28, 0x8c96bedf816961fc, 0xe4b2fae19aa1a7f2, + 0xeaef7d774fc6b2cb, 0x45a264bf3cfbc9eb, 0xa9ece815a79dce60, 0x2a689a992026946f, + 0x6d28b1909d44955e, 0x89957e0ae28b4ae3, 0xa688adea9b0201d5, 0x42a25d1356592614, + 0x3140d5aaaf3bd6ba, 0xa91bcee7358b45dd, 0x362ac47a24369130, 0x12c1c324f5196ad1, + 0xb3174b8091520e9a, 0xa4311e6e9fe4094b, 0x5def6530bd555965, 0x89add823d1e95cf, + 0x5125316c9a313823, 0xd890b1d15ca58960, 0xd03464850d129b40, 0x5548c5d164947a81, + 0x48a4e0d197b01d27, 0x7ae9fe197e2992d3, 0xd62b5ef757981357, 0x64c2802a8c120366, + 0xaca6836eaa4296a7, 0x9d4455a44d3dcc4b, 0xa8f517598f11f590, 0x41d195cc47bc6887, + 0xb7754c488dd289aa, 0xa4f29e1356e79e67, 0x63d9c328987fa985, 0x33d0bcf6bdefaf48, + 0x3a9d80f43b446325, 0xab748fbd2bceb944, 0xffd445ae0914098, 0x9da272ee868f0b4b, + 0x535cf02898f5a381, 0x5268c94c3fb8d8ec, 0x690f5aea0c6ba321, 0x47cbbd1250b735bd, + 0xe9d344abb88499d4, 0x741d225483060e88, 0x961f8ad44bca8c84, 0x7443bd6db5338651, + 0x2f75796cf0a17af9, 0x2a26a6bbd1149962, 0xa9eb429d225cf87e, 0x94f4d53d68d3a68c, + 0x1a44a88b50094d01, 0x2a265a8f954d47a9, 0xe14bf7a24b602f9f, 0xeb4a9d0389c940de, + 0xbcf523a65b6b4483, 0x652094422658f27, 0x6db5130fada54d34, 0x5e894536dc4830bd, + 0x1bd232e4f64946bf, 0x94a0132c69d00608, 0x9aa7bd8601e7d172, 0x40d039071bd53d44, + 0x2a3f98eb15da3fb1, 0x3569744034857cbc, 0xb14d0eb4d5e975e5, 0x33513e6367bb2a3f, + 0xe1ed33cb951389ef, 0xbb76374a02899473, 0xdac6ed7182d93844, 0x8c90d225ef70894c, + 0xc94667af43f329a6, 0x5443af79e12f7a24, 0xead54442a2537d2b, 0x284b3e522942b74c, + 0x6cba8d1a3bb176d5, 0x6ca5f5daab76ea39, 0x3f3bc28810075689, 0x4dd283946589e332, + 0x5a48f23050ada6a6, 0x42e4684a47aecd4e, 0x4782298c5112bb4c, 0x6a4bd1523fa36fa5, + 0xdcd278d252319d10, 0xb4434cab4379a35c, 0xec0bdef709c0299c, 0x79f79a3f53a22b5d, + 0x622f7b15f526321c, 0xc5bb9e8b77398bf8, 0x38df36882fd685c6, 0xa895ef8ec3acb9c1, + 0xe8cdf227ba8bed18, 0xe9be91bd5d8cfc1, 0xaf73d75ea21d2a2f, 0x34cf5c8c911e346d, + 0xaa90b4af2bcf5c35, 0x7c89ce3539fe898c, 0x52154d263c587a33, 0x2e3557e4f29fcc9e, + 0x9938db1ef2ef4eb, 0x354066f1cf599a6d, 0x5148a0adb140a052, 0x3d19be941d3aca0e, + 0xc451d236ca46a802, 0x5124e7c72c98e694, 0x429a376745c35251, 0xee89c6a93cfea23d, + 0x40e7aed15ef40d05, 0xf51fcc7b5c3e42e5, 0x74d195dcf2efe630, 0x5702918a6981db82, + 0x2028ca9b2e524a7a, 0x1e257c960a89a0a2, 0xbb9ea12c7e74c8e5, 0x150148f0744e9a32, + 0xc90c4dcceb44e348, 0x193a22513c744ba8, 0xc6daf65033bd44db, 0xfd63b74d139b64c9, + 0x7ad95435f2512002, 0x447a1b547a9e44e7, 0x3dbc72cb8e88c274, 0xf694bb7a892ac2a1, + 0x53df3469a32446e0, 0x289ff5d44ee3f630, 0x65289ca3d70d7442, 0x54454e882d7ea74c, + 0x44d1344d4b449500, 0x5eb4fa8877ad25cf, 0x4cde4c9d14ff3d2b, 0xd6858e8f595025eb, + 0xea324265b2e7a233, 0x347f1d688cd4cb5, 0x79f5110fba559544, 0xc86894da43281a4f, + 0x4cb8d3ae9511f51f, 0x1eba8f57d47f4dce, 0xe451a8fe03448cfe, 0xb97393444ac7c5a3, + 0xb80a15a7265964cd, 0x57574815191f3f59, 0x5528068b1d0adc4b, 0xa44a442e7f5da778, + 0x589a40a8fed13a3f, 0x252332d9c0969291, 0x303a99bc89f6a201, 0xf19261e1dfd8d551, + 0x6be79a2e73d1171a, 0x37913dd5e68a3533, 0xe57923279a3f8c9b, 0xee7a2f3888bce222, + 0xfd685b210b773d16, 0x22acb42b8dea255e, 0x8d4a9f54a8ab00e, 0xd4281d754c7624d0, + 0x4bc5e45b4d2b4291, 0xe7a7c4924966098d, 0xd33e47d03f483c1f, 0x2ce79cf014d7fd35, + 0x3f914a510d32a0ed, 0x2ddba9cc23fc494e, 0x2acba5dda8eedd46, 0x64b405e87fae09a2, + 0x1c35ad70897c92d3, 0xa743949473ce3c69, 0x10f878a116bf9030, 0x1b7080e556e9ca43, + 0xd39deffcc2b82d1d, 0x514d10548169e682, 0x92d7f22d5b29a951, 0x574ce4f25571b62b, + 0x918bc19c777c9ef5, 0x7fd915c350e6cf64, 0x2dcc6747cdd7d8b, 0x2ab4b4a6262d4d7a, + 0xca510579a2319827, 0x9be6c5ab57f612f4, 0xd8e4629bff35da37, 0x928c4052884694a4, + 0x64f7aa248a83b441, 0xf252a469b20e80bf, 0x95389a7b5237fa5a, 0x226510eb4a6be3d6, + 0xd1960f06a8c7138a, 0xa27674647a0f07c3, 0x1060a26968513569, 0xa20b489144d97ba5, + 0x32d0b77d187248e4, 0x9b38dca9ed19e42a, 0x7fa75445ca243428, 0xe89fa8f5950a2254, + 0xe94657597aa24d66, 0x4658a472812a271f, 0x2993cdaa269f0941, 0xfe8aeb9acb7d02a3, + 0xf595f7ae52042fc1, 0x4529a84a288918e8, 0x86b44e0b872f5714, 0x499444cf19327a8c, + 0xaae90ca02a8f5d93, 0xb43ca3c476bacd23, 0x5697f4315f5a51bd, 0x4c7eb6155bf35a9f, + 0x52d0b80945025365, 0xf041edea21ec3d13, 0x7b2ed7a8859e3265, 0x4f77a88fb82dfa89, + 0x96aebc822841a7bf, 0x6e4e9711ef50fad0, 0x34c0991d90f413e8, 0xdad13eb2059c344a, + 0x7aca8ca8e55628f5, 0xe9edf5197eb8d511, 0xef0d5254642a7b3d, 0xe90fd5015cd02a29, + 0x510331688b428d13, 0x6913eb63aab00f90, 0xeb65884cca3d5403, 0x33d4caa3d504d135, + 0x2a064f6fbd6a8e6d, 0x398e09194cc49851, 0xb963150104cf7a51, 0x409f3ba4ab4bd288, + 0x9a1c99d11eb4db64, 0xd3d110d633d5eb41, 0xe030a8ca574fdf42, 0x565fe5265af7ad96, + 0x5eeeafce89eef512, 0x593d9ef5b0c09635, 0xf14aa2631b332a2d, 0xb0ae1e8ca987a20d, + 0x8774421df53d44c, 0xba3694fa4c741dd1, 0x5b6a888d3ba3d5f0, 0xd465c726b49ef21f, + 0x4cf113400bea7b7, 0x18b6ea2264f7869a, 0x2764bd13570f44c3, 0x1ba71d92bd44b9d9, + 0xe1efd47afea22a7b, 0xa9eef51eaaa8872b, 0x340988f6e52ba140, 0xba88b93de15b4bad, + 0x53539cdca995706d, 0xee5bf120212f464b, 0x9bece9de793bc9d9, 0x9d0ad7deb6bd4652, + 0xb16ccbd8502bd6d1, 0x9d42b701423793da, 0x46435a22ad111d3d, 0xa9fe2e225c979e4d, + 0x1d6cdd52937ccd9d, 0x4a45eec958cd4442, 0xfd93854cbdb5119a, 0x204d111d47f31c4, + 0xc1cfb28ca45275ab, 0x554f8be63b25c948, 0xdafc84dcf2913d6d, 0x25ce80b1291d5d43, + 0xa6889496a217379a, 0x3dadf0f4df34ca52, 0x75c5281ab60d111d, 0xaf4f735136eba32e, + 0x3a01ccdd20977d6, 0xd72478f6f34656d7, 0x2ce2b53d7d6b2872, 0xa2fe188b7da4bc73, + 0x98db5f7773d16ee7, 0x4f442e6ee7a2ca7a, 0x79f4771da8899c9e, 0x72d8d337b6e0c15a, + 0x4e49fb27b8d5fc47, 0xc48f86a71332ada7, 0x134fb59513c2d395, 0xd7ddae38cc566dd, + 0x492c3fb488770912, 0xdcc648805944d5a6, 0x772fae93a0be5d91, 0xd9e63c7eef2a9796, + 0xfeb4463c1b3f40e, 0x2bafa3498b3c1344, 0x4a955b7445b6eda2, 0x650c64294c9da0a2, + 0x3aa69a2515723ba, 0x4a5e593967688ac4, 0xb1a271d27c940253, 0x2b18906751bd4a12, + 0x4b07f5a25d1af2a2, 0x41ebf2b449859e72, 0xe05108d226dbdba2, 0x1054d1141505c9e2, + 0x80f5f56c67d96b65, 0xf883c3e8ff4b0f1e, 0xa493de593a444b9e, 0x7ded32a46a8a0173, + 0x20d48e5030307b2a, 0x7465b4f29442ce9a, 0x944a5216dbdba26a, 0x34151302e54d144d, + 0xd17f38a94b9443c1, 0x182a2629277eb84e, 0x5cf6a4a2336c67d3, 0xca5d799664a9165a, + 0xca9268027be4e0da, 0x26994709a27e0ad1, 0x44bbbdb9f26513d3, 0x3d6530a513195c75, + 0xc0a4ebb4423485ca, 0x90a882264c7fd31a, 0xebc3ceca893a0bd1, 0x962aa5273927ba52, + 0x92b1ae526658d9e2, 0x8b3c65196a744e51, 0xa268529368beb5e9, 0x5214d4465236dbdb, + 0x8a289b50a455544d, 0xb1631fb13c618cfe, 0xa27138cc2688a4bc, 0x3cdeb4474f5f281c, + 0x3afead12e2724e1d, 0x144f4c52f270c2e5, 0x7aac4f3bc3ba20c0, 0x3d6748307dd44fd4, + 0x12b1d1043fc944d5, 0xa996ba89c6935a53, 0x9ad1369f2e325cf6, 0x31d72980aed34f5f, + 0x271dca2014cb5ea2, 0x236ca22a9377d6cb, 0x2ac6497eb5af231, 0x4d164a24d65d3cab, + 0x1459a26511331689, 0xa159680b54415162, 0xfb631d51bea27ef5, 0x228f547bdcc0ab71, + 0xd6a553de529249ef, 0x5a9c925eb50d4fd3, 0xacb465aa9963deb, 0xbee394f8a89f54cb, + 0xbbc529124b05ceb3, 0x69e89c7d9b799dac, 0xf13923132d0da62e, 0x212a8907d6da9d12, + 0xc7214486a7bc2b2d, 0xb517c5d44b4b91bd, 0x3c61ff1a32d2f2e8, 0x959448ce8fe6383b, + 0x1a3b864f780d1239, 0x903211ad4403f0f7, 0x4a5bcf5b31d74678, 0x314f783a0bfd5c4, + 0xfad2d47f6a892a06, 0x833eba5519b7449a, 0xda89fa856d1968fe, 0x967fe8ed2330514d, + 0x25a5d0eb43a9ab69, 0x2a856a1a21c9463c, 0x9dcf5a66a65949e7, 0xd5aa4da944d4c9d1, + 0x71291e6cb2d52d0a, 0xa9236a497b802852, 0xdfad41ab2553d74b, 0x553c064cc4c94885, + 0xa52523f54e3b84a4, 0x722703cd19aa7b19, 0x629c93444156c914, 0x995626f3442cf969, + 0x79a369d1fd279a22, 0x8987bf8622f3888b, 0x2ef7af6ee7a2ca2e, 0xff4dcdd11aec902f, + 0xeddb2a6d00fb2c60, 0x75472af3bab3bdb8, 0x7a0f952adf57ced8, 0xabc9fc67dd491199, + 0x5a5b20fb97123631, 0xce3fbc11a9ad3744, 0x53f3ca9d4e013d71, 0xdbbbb43e4f58d0a7, + 0x9fe93ceee1233d15, 0x7bcc0e89816e38f1, 0xf4cf09a9d3b44e6a, 0x3113126990f35347, + 0x89b2f61d98f83d7c, 0x41fbe501a483da6e, 0xa7a45281453b800f, 0xd4d1300e816478d4, + 0x7d0924e3a7279dd7, 0x204328a260524070, 0x1d2882b4bfea9a23, 0x6c7047133681a829, + 0x4a2587494528987a, 0xea222acb542520a4, 0xb4f40069f9d584a4, 0x510e053d12540a35, + 0xc4646aa9e77708b6, 0x51352c595512624a, 0x25f4622688ab4af6, 0xd7551054a863fbba, + 0xe4f196958490174d, 0x4444f6795239bbe3, 0xb18872d375190c84, 0x484aa22906b8d6c4, + 0x52a249e7766a3243, 0x29464b05191e4746, 0x4c42a321ad3574c9, 0x5a5fd8c7dbe4a20d, + 0xa89c45f122841441, 0xa8aad3d288a3279e, 0x55c0c7530417fd25, 0xbb819be7f9e955e9, + 0xa3d71b54f44c29f6, 0x8658bc9e776f5126, 0xb465a4cb2d47aed4, 0xca0d119bed435106, + 0xe73cd139f60d5da8, 0x52928b2a5d25f944, 0xaf56abd21e023e08, 0x52a22c2b54f01f3a, + 0xeb647061a7ad9548, 0xf9d73dde7a79ddbd, 0x52d4d294426fad98, 0x2c3fb458b1fd6b6a, + 0xa5c9d3b406512ea3, 0x5ad725d52f3dd290, 0x4aba32652369b9bf, 0xa213b2878055e95e, + 0x23bc66ffb7a88723, 0x27465d520ee0ee50, 0x6a42a59cf5b37347, 0x4b8a0f835062dd11, + 0x65863a8c96267884, 0x952280fe9de68940, 0x8d7a5e973a75f744, 0x83bcf442242eca40, + 0x4e00fcf548241de7, 0x9dd3d44e27243f54, 0xfad88922d2150a87, 0x6a1669aa8844a32d, + 0x866d8ec7a323807e, 0x882fb443bd6c8689, 0x5f9e5f74c25131f6, 0x9bd698d305c3e97b, + 0x623dbd44624c280f, 0x3522b4dca53ceebe, 0x13546575f5b1deb5, 0x3af058f443df3695, + 0x9eb52baa6ba8fe43, 0xdae975e4cb317d94, 0xdfaa65849c93f0a7, 0x3f98d09080bf50ec, + 0xa26d4e085f45145a, 0x9f27b54b6489eb58, 0x265a8900f1490e9a, 0x94b5409ef31f8a5, + 0x88f4d12563678caf, 0x8b550d2aa9979a89, 0x25d68f53a856ec8e, 0x5b98fadfa5381f4a, + 0x513b34fb1c3896c5, 0xd4690b52efb4f79f, 0xa7a20c06df997c3, 0x336cd2a430511895, + 0x44bafada8a75c225, 0xe88a5ba24a21879d, 0xe1e3d06adea26356, 0x82ce3a8876afa26b, + 0xe7f1a44d70d02b12, 0x41b45d85b2094a60, 0xb3689bcc9f5dab0b, 0xada856bea21d48a0, + 0x90a32c129122fbf5, 0x328c542782b159e0, 0xc88e947a8a8caf06, 0xbeb48532c75a26ae, + 0x78fcebd44bc4a466, 0x9af384435ed72523, 0x3458eaf35e84856b, 0xf3476b3a1a9cb00f, + 0x90aa88a2550846fc, 0xa25124515d3ce252, 0xed79a230379a367b, 0x6beddbbbeefbba1f, + 0xc466cfb757eef2c8, 0xe36c6744e8670479, 0xd16ee7317f0c45be, 0x773d177d3ac7b773, + 0xe4dd71cceea241f7, 0xa4a44cbda5479919, 0x29eeea89ee79ddd5, 0x307bb51262c8a3b3, + 0xa2559d7916fde4e8, 0x3ea0906ec9e6a396, 0xba8886940a547c0d, 0x5018bc039a899ce9, + 0x46bb78c1a409595e, 0xeceb5271a75ce2d9, 0x4faf70dc80c7e3a9, 0x624e893bca179e16, + 0x3272948449e804a2, 0x44b8b26b4e08f9e7, 0x6a2107301765f13, 0x2e45291dbb06912a, + 0xfaeb6109151fe2a6, 0x2a27194621a6fea3, 0x2c91e718c6a48b66, 0x69438a32596e8c86, + 0xd6b1ad08691ae88a, 0x9b6289b5193d5ae2, 0xcddf9c0941ba888a, 0x206601442bb1214c, + 0x80ca3d5548191a3a, 0x225445a90ca688fa, 0x4e6cb94918d74465, 0xf8356c37d33813d4, + 0xb4e993cfb5144ee6, 0x116a224160944bde, 0xca252902a26d4865, 0x40c7ad5197a2131e, + 0x40b758a3b75f5125, 0xcb56cc7235139b2c, 0x62996dc15a887828, 0x4c8d18df16730e4f, + 0x44034854e581eaa4, 0xe8d5492cd44e5229, 0xbaa455074c526b59, 0x240260ea3b76f51e, + 0xe8e4a77512f265a6, 0x1de4ef04019967e7, 0x435134603afbd6d7, 0x1fd95110d34483eb, + 0x137d4483483a3749, 0xc93b07d8db1d3ab7, 0x7a88c495b4494628, 0x902ca13243798da7, + 0x89aa77443a847cd6, 0x93e9ba261bb48ab2, 0x8c98d0247688e9d1, 0x9c67567ca968abb2, + 0x728947de85eb5468, 0x35d6f464ab4d09, 0xafad1a75ba7c9349, 0xb6aad5b46425b44d, + 0xae62b78d232eb64c, 0x9dfad9f5120fad1e, 0x3cd0527a24aa4954, 0x8829e2a8fe251069, + 0x585469464c675d5a, 0x8eb96dd1309fa500, 0x7cd6d1156902a3fb, 0xb0d8f75813803d6c, + 0x1544ae3d68eb44a6, 0x7804d1042d1142d1, 0x8ad635bac4fe85bf, 0xa807ccdec6b15ac9, + 0xf119fd6956812a20, 0x7511eecd966b1347, 0x49b4a1cf3012ceba, 0x680aa22aaf54e14c, + 0xd12eed6a68d5a884, 0xbb440347f1ea3206, 0x468a286c1a6fbdda, 0xa9a316d108d03544, + 0x4948ad688b67f79a, 0x94474d32d4a532c8, 0xd1291bbd680c35ce, 0xdce3144fc4a4653c, + 0x9e68bd68944948cd, 0xb4474c3dad44cdbf, 0xf5afcf3450b8fcee, 0x888ba21d495be14a, + 0x51744579ff8622f3, 0x7ba20b28b773dde6, 0xb8c9000300bffcbb, 0x816dca717973, + 0x42ae444e45490000, 0x8260, 0x0, 0x0, +}; +int DummyDataLen = 2032; +unsigned long long DummyData[254] = { + 0x464a1000e0ffd8ff, 0x6400000201004649, 0x1100ecff00006400, 0x100796b637544, + 0xff00001e00000004, 0x65626f64410e00ee, 0xff01000000c06400, 0xb0b0b10008400db, + 0xf17100c0c100b0c, 0x141010141b170f0d, 0x1f17171717171f1b, 0x1e171a1a1a1a171e, + 0x2f1e23252725231e, 0x4040402f2f33332f, 0x4040404040404040, 0xf0f110140404040, + 0x1415121215111311, 0x1616141a14111411, 0x1a1a1c1a1a261a14, 0x231e1e1e1e233026, + 0x2b2e2727272e2b30, 0x4040353530303535, 0x404040404040403f, 0xc0ff4040404040, + 0x103000100010811, 0x111030111020022, 0x30001008000c4ff, 0x10101, + 0x504000000000000, 0x101010101030206, 0x1, 0x201000000000000, + 0x401020200011004, 0xf0004060101, 0x1104030201000000, 0x5141063105131221, + 0x1523427114322261, 0x834333a26252a181, 0x113616456434b3a3, 0x1050100010101, + 0x0, 0x815141f012110100, 0x1030c00daff2102, 0x3f001103110200, + 0x8073e800d8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x6387ad181b0c3ee3, 0x56f2d81db762731d, + 0x9f6e6b8e79e2592d, 0x785ca6e0f3c5ed20, 0x8ec5bb9bcbb5926f, 0xaced36c7f66b116d, + 0x7ad67386e7239ef5, 0xe91bde7da87f57af, 0x9c63f1b43f2cb6e0, 0x6bbbcd14bbd39a54, + 0xbbbe159e9f39e235, 0x577ab735fd3a066f, 0x3eae972cfb3a065b, 0xef98366da59556fb, + 0xba3ccbfc1c3fefb5, 0x1f390337bd1ed7f6, 0x3adbfc2db6a66255, 0x4d3295262fbe2c7e, + 0xe86e2dd624d730b6, 0x1d4f6d9e48939c99, 0x9fd6bf373fbdde7e, 0xd7d8f9affbefcefc, + 0xc12b4ff633e15b66, 0xec3f7db119fbf790, 0x8bb13afbe6e546bd, 0xd7fa7c6fbdd71c26, + 0xea9e15f1bc63acd8, 0xdc33afe7637edbf1, 0x346a9bbdb9bfc5bc, 0x797b9d66e9700d27, + 0x7a6dad946cb6f662, 0xeede9e89289e1df3, 0x98c5b43a1dcfcf2f, 0xa03eedb96beffacb, + 0xebcfd414b1adcdd7, 0xcc4aedadf6da9eeb, 0x248ff41dafe5f166, 0xcff2c76f2723265a, + 0xdb5c4d4db3b747cb, 0x78cb760c1e672259, 0x9ee36913694d5baf, 0xcf8c7accd225f43a, + 0x7698da7c166379d3, 0xbc656f04779fb1ab, 0xd9ed99dc636a0e5b, 0x716a733cabbd468b, + 0x6d7f0d2bf1c4131f, 0xd915718cab17679f, 0x8c93c596ee7e9bdc, 0x5e3371cc465bc175, + 0xfff6d31d295eaec9, 0xf85cc6d962293100, 0xd1921f57b279f630, 0xe9b7d7bda27d1f8f, + 0xfdd994fb99f979a5, 0x9bb7adad611f7dde, 0xb0f505d772ee3757, 0xe638f1f2bdc6c64c, + 0x697b26fbf43daf7b, 0x9992cba7d8474cf6, 0xb1d46f1757713b3b, 0xedf963bea74d765b, + 0xbfc88c39f756bdf, 0x2ff84c71b4e5b3f4, 0xede1b2ad641a2697, 0xcdd19dda448be3f2, + 0x4de5f898631dab7b, 0xc63e6f517fde3dee, 0x2646db448b625b6d, 0x63328ed3da706a63, + 0xf676f3e76911afb5, 0xd3f0e94e77c77c44, 0x7ca92bef4d7ddb8d, 0xd9d632f926e9bf58, + 0xfef8568a15cfd83f, 0x1a3d4f7c9ceefe7e, 0x9b670f83cf65d098, 0xdaf7f1182df97125, + 0x999f579a7e7bdd2b, 0xecafecbf27dbb99f, 0x53678f919a6ffd35, 0xe7384f8ad9d561dd, + 0xdbeb9963ebee2e2e, 0xcfa4f8d9afe7f43c, 0xce0e7f97adaf58d5, 0x937ca5c797178b9f, + 0xbadb8a36c7d58c5d, 0xc7d39a23de1c2766, 0x350ee5ca5fafb36f, 0x3d06638bcf601ca3, + 0x8ec9b59ac5ee368c, 0x896775475b734cdc, 0x50edf0eb, 0x7510000000000000, + 0xb000ff9ebded673d, 0x3efdc300fffe77f2, 0x67ae7e9a343c3bde, 0x9ef4af5da1fb799e, + 0x6cfe6cbe763d782c, 0x4f93bd92c7da155a, 0xe61773b4d5227735, 0xc8c42ff01fbf459c, + 0x87e9f477343550bb, 0xd28ae0e93bd60e5b, 0x235ec79b6d86cbb5, 0xd2f19c8ecb6372b6, + 0xebbe9f9f79fcc727, 0x937fbd99f5767fd6, 0x1d7f6fa7feece6c3, 0xff354f00ffad97, + 0x57756fec0c4eef27, 0x538cd6bb17871ed4, 0x792d53cc69f9da9a, 0xcde9a5d6d6b4ed98, + 0x1331e1cf4a33d162, 0x3d9bc3e6b6bf7dfc, 0x2e4fde1e5f2376f4, 0xa5b5e6c56f1527c5, + 0xf3766bdb6b6286af, 0xcf3c1fafe778f13d, 0x96a6a0bf4ed5860c, 0x7d5dc55886ceaee6, + 0xe28beff48ec9d368, 0xb78f2d4db459c68b, 0x4cf875acdb3ccf24, 0x5a7b5dc1bf3f5af6, + 0x9ede6a268bcb8bb1, 0x779c26b95e31bd48, 0xa7e79923d6744744, 0x3f9abba80d1994d8, + 0x7499564bb1a7cfb8, 0x4b33dfa9b548cef2, 0x5eb3ba2752f2d8d2, 0x13251df1fd3cb7e8, + 0xd888edbd2da6a10f, 0xb59582a9addb15cb, 0x4db13d9bdfbc75a2, 0xe9e9bf38f9e668ad, + 0x9c7e15d5860cb530, 0x38debed5aebffdd8, 0xb0988ecd6273ebc1, 0x39279fe7e224e378, + 0xcc331fc7f7adf96f, 0xf75feb00ff3e4e4f, 0xffea9fee4ff93f, 0xcbcc4bff4bffee0, + 0x3d19fbd22f7bd686, 0xd97c5dd11b557f5e, 0x5ef2d93d9ce52c27, 0x527f7292cf397972, + 0x1caf233d2b62ce7e, 0xf7ecc3dba8afccc4, 0x14df6d69dda68e7d, 0x5331564a3e4c0e5b, + 0x58773fd35272f9b3, 0x43865a4c7a629e8f, 0x5787add8d32fa76a, 0xb0c7ee6b5bd33c16, + 0xeb4e36dfacd496cb, 0xdc3d731c3fdedd5e, 0xfaea1d8e8848c773, 0xce667d35865dafad, + 0xe2b3985a9b957a3c, 0xde5af3a67da7d69c, 0x9fb93bbd5e7e7199, 0x54006dc830ebbef3, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x3e00000000000000, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0xd9ff070000000000}; + diff --git a/APRSStdPages.c b/APRSStdPages.c new file mode 100644 index 0000000..7cb38e1 --- /dev/null +++ b/APRSStdPages.c @@ -0,0 +1,3719 @@ + +#include +#include + +#ifndef WIN32 + +#define _stricmp stricmp +#define _strdup strdup + +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); + +#endif + +unsigned char aisblue_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x23, 0xde, 0xb7, 0xa6, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, + 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, + 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, + 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x03, 0x7a, 0x0e, 0x22, 0xdf, + 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, + 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, + 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, + 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, + 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, + 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, + 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, + 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aisblue_png_len = 227; +unsigned char aisgreen_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x50, 0x4c, 0x54, 0x45, 0x61, 0x62, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x13, 0xfe, 0x00, 0x1b, 0x46, 0xca, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, + 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, + 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, + 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, + 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x11, 0x89, + 0xb7, 0x53, 0x97, 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, + 0xd7, 0x63, 0x60, 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, + 0xa1, 0x22, 0x1d, 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, + 0xad, 0x9a, 0x80, 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, + 0x41, 0xa6, 0x96, 0x21, 0xf3, 0xc4, 0x56, 0x2f, 0x05, 0x51, 0x52, 0xab, + 0x56, 0x12, 0xa2, 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, + 0x06, 0xc5, 0x08, 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, + 0x1a, 0x1a, 0x1a, 0xc0, 0x00, 0x00, 0xd2, 0x45, 0x24, 0x46, 0xf4, 0x40, + 0x4e, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82 +}; +unsigned int aisgreen_png_len = 230; +unsigned char yellowplane_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x02, + 0xf7, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x38, 0x44, 0x47, 0x44, + 0x45, 0x46, 0x3b, 0x4c, 0x51, 0x42, 0x4b, 0x4e, 0x3f, 0x4d, 0x51, 0x43, + 0x4e, 0x51, 0x44, 0x4e, 0x51, 0x44, 0x51, 0x54, 0x4b, 0x50, 0x52, 0x42, + 0x54, 0x5a, 0x43, 0x54, 0x5a, 0x4c, 0x52, 0x53, 0x48, 0x53, 0x56, 0x4a, + 0x53, 0x56, 0x4d, 0x53, 0x55, 0x46, 0x57, 0x5c, 0x4b, 0x56, 0x59, 0x4a, + 0x58, 0x5c, 0x4b, 0x58, 0x5b, 0x4f, 0x58, 0x5b, 0x4d, 0x59, 0x5c, 0x4e, + 0x59, 0x5d, 0x53, 0x58, 0x59, 0x50, 0x59, 0x5c, 0x49, 0x5b, 0x5f, 0x4e, + 0x5b, 0x5e, 0x4e, 0x5c, 0x60, 0x52, 0x5b, 0x5e, 0x49, 0x5e, 0x65, 0x4c, + 0x5e, 0x63, 0x52, 0x5d, 0x60, 0x55, 0x5d, 0x5f, 0x53, 0x5e, 0x61, 0x55, + 0x5f, 0x62, 0x53, 0x60, 0x64, 0x54, 0x60, 0x63, 0x54, 0x61, 0x65, 0x55, + 0x61, 0x64, 0x57, 0x61, 0x64, 0x5c, 0x60, 0x60, 0x58, 0x61, 0x64, 0x5a, + 0x61, 0x63, 0x52, 0x63, 0x69, 0x57, 0x62, 0x66, 0x5c, 0x61, 0x63, 0x56, + 0x63, 0x67, 0x5d, 0x62, 0x63, 0x59, 0x63, 0x66, 0x56, 0x64, 0x68, 0x5c, + 0x63, 0x66, 0x5c, 0x64, 0x67, 0x61, 0x65, 0x67, 0x63, 0x65, 0x65, 0x5d, + 0x67, 0x6a, 0x61, 0x66, 0x68, 0x5b, 0x68, 0x6c, 0x63, 0x66, 0x67, 0x5c, + 0x68, 0x6c, 0x59, 0x69, 0x6d, 0x61, 0x67, 0x69, 0x59, 0x69, 0x6f, 0x5e, + 0x68, 0x6b, 0x61, 0x69, 0x6b, 0x61, 0x6a, 0x6d, 0x5a, 0x6c, 0x71, 0x63, + 0x6a, 0x6b, 0x64, 0x6a, 0x6c, 0x66, 0x68, 0x7e, 0x5d, 0x6c, 0x70, 0x66, + 0x6a, 0x6b, 0x65, 0x6b, 0x6d, 0x5b, 0x6f, 0x74, 0x5c, 0x70, 0x75, 0x68, + 0x6d, 0x6e, 0x64, 0x6e, 0x71, 0x5e, 0x70, 0x75, 0x62, 0x6f, 0x73, 0x67, + 0x6e, 0x70, 0x69, 0x6e, 0x6f, 0x69, 0x6e, 0x70, 0x66, 0x6f, 0x72, 0x66, + 0x71, 0x75, 0x6b, 0x70, 0x71, 0x67, 0x71, 0x74, 0x6e, 0x70, 0x70, 0x62, + 0x74, 0x7a, 0x66, 0x74, 0x78, 0x70, 0x72, 0x72, 0x6d, 0x73, 0x75, 0x66, + 0x75, 0x7a, 0x68, 0x75, 0x79, 0x71, 0x73, 0x74, 0x72, 0x74, 0x75, 0x72, + 0x76, 0x77, 0x6c, 0x78, 0x7b, 0x72, 0x77, 0x78, 0x72, 0x77, 0x79, 0x73, + 0x78, 0x79, 0x72, 0x79, 0x7b, 0x72, 0x7b, 0x7e, 0x78, 0x7a, 0x7a, 0x77, + 0x7b, 0x7d, 0x74, 0x7c, 0x7e, 0x75, 0x7c, 0x7e, 0x76, 0x7d, 0x80, 0x77, + 0x7e, 0x80, 0x77, 0x80, 0x82, 0x7c, 0x7f, 0x7f, 0x72, 0x82, 0x87, 0x76, + 0x81, 0x85, 0x7b, 0x80, 0x81, 0x76, 0x83, 0x87, 0x7e, 0x85, 0x87, 0x83, + 0x84, 0x85, 0x7d, 0x87, 0x89, 0x7b, 0x88, 0x8c, 0x7b, 0x89, 0x8e, 0x7e, + 0x8a, 0x8d, 0x7f, 0x8b, 0x8e, 0x88, 0x8b, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, + 0x8c, 0x8c, 0x88, 0x8e, 0x91, 0x85, 0x8f, 0x93, 0x8c, 0x8e, 0x8f, 0x8d, + 0x8f, 0x8f, 0x87, 0x91, 0x94, 0x88, 0x93, 0x97, 0x91, 0x94, 0x96, 0x93, + 0x94, 0x95, 0x8d, 0x96, 0x9a, 0x8d, 0x97, 0x9a, 0x90, 0x97, 0x99, 0x95, + 0x96, 0x96, 0x91, 0x98, 0x9b, 0x94, 0x98, 0x99, 0x91, 0x99, 0x9b, 0x96, + 0x97, 0xa4, 0x93, 0x99, 0x9b, 0x91, 0x9a, 0x9c, 0x98, 0x99, 0x99, 0x93, + 0x9b, 0x9d, 0x97, 0x9c, 0x9d, 0x93, 0x9d, 0xa1, 0x95, 0x9d, 0x9f, 0x96, + 0x9d, 0xa0, 0x98, 0xa0, 0xa3, 0x9d, 0x9f, 0xa0, 0x97, 0xa1, 0xa5, 0xa1, + 0xa1, 0xa1, 0x99, 0xa3, 0xa7, 0x9b, 0xa3, 0xa5, 0xa4, 0xa5, 0xa5, 0x9d, + 0xa7, 0xa9, 0xa6, 0xa8, 0xa8, 0xa2, 0xa9, 0xac, 0xa2, 0xab, 0xad, 0xaa, + 0xab, 0xab, 0xa6, 0xad, 0xb0, 0xa5, 0xaf, 0xb2, 0xab, 0xb3, 0xb6, 0xaf, + 0xb2, 0xb4, 0xae, 0xb5, 0xb8, 0xb4, 0xb4, 0xb4, 0xb0, 0xb6, 0xb8, 0xb1, + 0xb7, 0xba, 0xb2, 0xb7, 0xb9, 0xb4, 0xb7, 0xb9, 0xb2, 0xb8, 0xb9, 0xb2, + 0xb8, 0xba, 0xb3, 0xb8, 0xb9, 0xb3, 0xbb, 0xbe, 0xb4, 0xbb, 0xbd, 0xb5, + 0xbb, 0xbd, 0xb6, 0xbb, 0xbc, 0xb7, 0xbc, 0xbf, 0xb8, 0xbd, 0xbe, 0xbd, + 0xbe, 0xbf, 0xbc, 0xbf, 0xc0, 0xbc, 0xc1, 0xc2, 0xbd, 0xc1, 0xc2, 0xbb, + 0xc2, 0xc5, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xbe, 0xc5, 0xc7, 0xc2, + 0xc4, 0xc5, 0xc3, 0xc4, 0xc4, 0xc2, 0xc6, 0xc7, 0xc4, 0xc6, 0xc7, 0xc3, + 0xc7, 0xc8, 0xc3, 0xc7, 0xc9, 0xc3, 0xc8, 0xca, 0xc4, 0xc9, 0xcb, 0xc6, + 0xca, 0xcb, 0xc6, 0xcc, 0xce, 0xca, 0xcd, 0xce, 0xca, 0xce, 0xd0, 0xcb, + 0xce, 0xcf, 0xca, 0xcf, 0xd1, 0xcb, 0xcf, 0xd0, 0xcb, 0xd0, 0xd2, 0xcd, + 0xd0, 0xd1, 0xcf, 0xd1, 0xd2, 0xd0, 0xd1, 0xd1, 0xce, 0xd2, 0xd3, 0xd0, + 0xd2, 0xd3, 0xd0, 0xd3, 0xd4, 0xd2, 0xd4, 0xd5, 0xd4, 0xd5, 0xd8, 0xd2, + 0xd6, 0xd8, 0xd2, 0xd7, 0xd8, 0xd4, 0xd7, 0xd8, 0xd3, 0xd8, 0xd9, 0xd7, + 0xd7, 0xdd, 0xd6, 0xd8, 0xd9, 0xd6, 0xd8, 0xda, 0xd7, 0xd8, 0xd8, 0xd7, + 0xda, 0xdb, 0xd8, 0xdb, 0xdd, 0xd7, 0xdc, 0xde, 0xd9, 0xdc, 0xdd, 0xda, + 0xdc, 0xdc, 0xdb, 0xdd, 0xdd, 0xdb, 0xdd, 0xde, 0xdc, 0xdf, 0xe0, 0xdd, + 0xdf, 0xe0, 0xdd, 0xe0, 0xe0, 0xde, 0xe1, 0xe2, 0xe0, 0xe1, 0xe2, 0xe0, + 0xe2, 0xe2, 0xe1, 0xe3, 0xe3, 0xe1, 0xe3, 0xe4, 0xe2, 0xe3, 0xe3, 0xe1, + 0xe6, 0xe7, 0xe5, 0xe7, 0xe7, 0xe6, 0xe7, 0xe7, 0xe6, 0xe7, 0xe8, 0xe6, + 0xe8, 0xe9, 0xe7, 0xe8, 0xe9, 0xe4, 0xe9, 0xeb, 0xe6, 0xe9, 0xea, 0xe7, + 0xe9, 0xea, 0xe8, 0xe9, 0xea, 0xe6, 0xea, 0xeb, 0xe9, 0xeb, 0xec, 0xe8, + 0xec, 0xed, 0xea, 0xec, 0xed, 0xeb, 0xec, 0xed, 0xeb, 0xed, 0xed, 0xec, + 0xed, 0xee, 0xec, 0xef, 0xf1, 0xff, 0xff, 0x00, 0x0f, 0x9e, 0xcb, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, + 0x66, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, + 0xc4, 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00, + 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x37, + 0x0f, 0x41, 0x8e, 0x0c, 0x76, 0x00, 0x00, 0x01, 0xda, 0x49, 0x44, 0x41, + 0x54, 0x38, 0xcb, 0x63, 0x60, 0x20, 0x1b, 0xdc, 0x7a, 0x49, 0x94, 0xb2, + 0x0e, 0x67, 0xf5, 0x43, 0x44, 0x28, 0xdb, 0x14, 0xf7, 0xe7, 0xcf, 0x44, + 0xc2, 0xca, 0xde, 0x86, 0x14, 0xfd, 0xf9, 0x23, 0x7c, 0x90, 0xa0, 0xba, + 0x59, 0xd2, 0x7f, 0x80, 0x60, 0x29, 0x41, 0x75, 0x1b, 0xed, 0x41, 0xea, + 0x7a, 0x08, 0xaa, 0x5b, 0xe9, 0x07, 0x52, 0x17, 0x4e, 0x50, 0xdd, 0xea, + 0x60, 0x90, 0x3a, 0xd7, 0xc7, 0x84, 0xd4, 0x4d, 0x76, 0x06, 0xa9, 0xb3, + 0xb8, 0x4b, 0x48, 0xdd, 0x8a, 0x00, 0x90, 0xba, 0xa0, 0xe3, 0x78, 0x15, + 0xbd, 0x38, 0xb7, 0x25, 0xca, 0x1c, 0xa4, 0x4e, 0xa8, 0xa4, 0xbe, 0xf9, + 0x19, 0x4e, 0x65, 0xf7, 0x1d, 0xcd, 0x0c, 0x18, 0x35, 0x40, 0xea, 0x58, + 0x24, 0x9c, 0x04, 0xf3, 0x70, 0xaa, 0x3b, 0xa5, 0x35, 0xbb, 0x8a, 0x29, + 0x14, 0xa4, 0xce, 0x34, 0x62, 0x49, 0x53, 0xe0, 0x1b, 0x5c, 0xea, 0x76, + 0x59, 0xde, 0xfc, 0x83, 0x00, 0x46, 0xf7, 0x70, 0x28, 0xfb, 0x55, 0x20, + 0xbe, 0x1d, 0x49, 0x1d, 0x7b, 0xcd, 0x25, 0xec, 0xea, 0xe6, 0x08, 0xa4, + 0xee, 0x40, 0x52, 0xf7, 0x47, 0x21, 0x6c, 0x1b, 0x16, 0x55, 0x97, 0x36, + 0x8b, 0xc5, 0xee, 0xfe, 0x83, 0x02, 0xfc, 0x53, 0xa6, 0x9f, 0x44, 0x57, + 0x76, 0xdd, 0xd6, 0xa6, 0xed, 0xcc, 0x1f, 0x34, 0x90, 0x23, 0xe3, 0xb1, + 0xe0, 0x27, 0xb2, 0xaa, 0xcf, 0xab, 0x32, 0xdc, 0xe6, 0xfd, 0xc1, 0x04, + 0xd3, 0xf4, 0xe5, 0xf6, 0xbf, 0x43, 0x28, 0x7b, 0xd8, 0xcd, 0xfa, 0x07, + 0x07, 0xa8, 0x48, 0x6c, 0x38, 0x0c, 0x53, 0xb6, 0x2c, 0x1a, 0x22, 0x56, + 0x69, 0x2f, 0x22, 0xcd, 0xc1, 0x09, 0x62, 0xf1, 0x33, 0x67, 0x27, 0x4b, + 0x79, 0xd5, 0xf5, 0x02, 0x99, 0x53, 0x4c, 0xaa, 0x5f, 0x43, 0x94, 0x3d, + 0xd2, 0x86, 0x28, 0x73, 0xcf, 0x9a, 0x71, 0xe2, 0x68, 0x2e, 0x1f, 0x88, + 0xa9, 0x58, 0xfa, 0xe4, 0xc7, 0xfa, 0x49, 0x8d, 0x3a, 0x36, 0x20, 0x8e, + 0xe6, 0x3a, 0xa8, 0x79, 0x49, 0x20, 0x9e, 0x43, 0xf1, 0x9e, 0x4f, 0x40, + 0xf6, 0x8d, 0x48, 0x10, 0xc7, 0xe0, 0x08, 0x58, 0xe2, 0x74, 0xb9, 0x35, + 0x90, 0xc3, 0xb6, 0x15, 0xa2, 0xec, 0xae, 0xdd, 0x1f, 0x1e, 0xbd, 0x96, + 0x63, 0x10, 0xce, 0x15, 0x63, 0x90, 0x3a, 0x95, 0xe5, 0x50, 0x23, 0x76, + 0x1e, 0x6a, 0xfd, 0x53, 0x08, 0x65, 0x7f, 0x9d, 0xe0, 0xb9, 0xef, 0x01, + 0xcc, 0xad, 0x27, 0x44, 0x41, 0xea, 0x94, 0xe6, 0x23, 0x3c, 0x79, 0xe0, + 0x21, 0x9c, 0x79, 0x15, 0x21, 0xfa, 0x54, 0x15, 0xa4, 0x4e, 0x79, 0x2e, + 0xc1, 0x84, 0x9f, 0x09, 0x52, 0x67, 0x75, 0x9e, 0x90, 0xb2, 0xdb, 0xf1, + 0x20, 0x75, 0xdc, 0x8b, 0x09, 0x28, 0xfb, 0xbe, 0xd0, 0x0e, 0xa4, 0x4e, + 0xbe, 0x8c, 0x40, 0x06, 0x99, 0x6a, 0x98, 0x00, 0x52, 0x57, 0xcb, 0xeb, + 0xb3, 0x06, 0x9f, 0xb2, 0xad, 0x92, 0x31, 0x90, 0x30, 0x9f, 0xa9, 0x2b, + 0x7b, 0x16, 0xb7, 0xb2, 0xe7, 0xde, 0x69, 0xf0, 0x88, 0xcd, 0xef, 0xff, + 0x81, 0x53, 0x5d, 0x3b, 0x72, 0x0a, 0xe0, 0xda, 0x86, 0x53, 0xdd, 0x22, + 0x94, 0xa4, 0xf2, 0x11, 0xa7, 0xba, 0xbd, 0x6a, 0xe9, 0x7d, 0x6b, 0x2f, + 0x76, 0xb9, 0x6c, 0xf8, 0x70, 0xb9, 0xd3, 0xf7, 0x02, 0x1e, 0x7f, 0xbc, + 0xff, 0xf2, 0x0d, 0x48, 0x5e, 0x63, 0xf8, 0xcd, 0xf0, 0xea, 0x0e, 0x03, + 0x4d, 0x01, 0x00, 0x38, 0x16, 0x7f, 0x3f, 0xdc, 0xdc, 0x55, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int yellowplane_png_len = 1355; + +unsigned char greenplane_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x02, + 0xf7, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x38, 0x44, 0x47, 0x44, + 0x45, 0x46, 0x3b, 0x4c, 0x51, 0x42, 0x4b, 0x4e, 0x3f, 0x4d, 0x51, 0x43, + 0x4e, 0x51, 0x44, 0x4e, 0x51, 0x44, 0x51, 0x54, 0x4b, 0x50, 0x52, 0x42, + 0x54, 0x5a, 0x43, 0x54, 0x5a, 0x4c, 0x52, 0x53, 0x48, 0x53, 0x56, 0x4a, + 0x53, 0x56, 0x4d, 0x53, 0x55, 0x46, 0x57, 0x5c, 0x4b, 0x56, 0x59, 0x4a, + 0x58, 0x5c, 0x4b, 0x58, 0x5b, 0x4f, 0x58, 0x5b, 0x4d, 0x59, 0x5c, 0x4e, + 0x59, 0x5d, 0x53, 0x58, 0x59, 0x50, 0x59, 0x5c, 0x49, 0x5b, 0x5f, 0x4e, + 0x5b, 0x5e, 0x4e, 0x5c, 0x60, 0x52, 0x5b, 0x5e, 0x49, 0x5e, 0x65, 0x4c, + 0x5e, 0x63, 0x52, 0x5d, 0x60, 0x55, 0x5d, 0x5f, 0x53, 0x5e, 0x61, 0x55, + 0x5f, 0x62, 0x53, 0x60, 0x64, 0x54, 0x60, 0x63, 0x54, 0x61, 0x65, 0x55, + 0x61, 0x64, 0x57, 0x61, 0x64, 0x5c, 0x60, 0x60, 0x58, 0x61, 0x64, 0x5a, + 0x61, 0x63, 0x52, 0x63, 0x69, 0x57, 0x62, 0x66, 0x5c, 0x61, 0x63, 0x56, + 0x63, 0x67, 0x5d, 0x62, 0x63, 0x59, 0x63, 0x66, 0x56, 0x64, 0x68, 0x5c, + 0x63, 0x66, 0x5c, 0x64, 0x67, 0x61, 0x65, 0x67, 0x63, 0x65, 0x65, 0x5d, + 0x67, 0x6a, 0x61, 0x66, 0x68, 0x5b, 0x68, 0x6c, 0x63, 0x66, 0x67, 0x5c, + 0x68, 0x6c, 0x59, 0x69, 0x6d, 0x61, 0x67, 0x69, 0x59, 0x69, 0x6f, 0x5e, + 0x68, 0x6b, 0x61, 0x69, 0x6b, 0x61, 0x6a, 0x6d, 0x5a, 0x6c, 0x71, 0x63, + 0x6a, 0x6b, 0x64, 0x6a, 0x6c, 0x66, 0x68, 0x7e, 0x5d, 0x6c, 0x70, 0x66, + 0x6a, 0x6b, 0x65, 0x6b, 0x6d, 0x5b, 0x6f, 0x74, 0x5c, 0x70, 0x75, 0x68, + 0x6d, 0x6e, 0x64, 0x6e, 0x71, 0x5e, 0x70, 0x75, 0x62, 0x6f, 0x73, 0x67, + 0x6e, 0x70, 0x69, 0x6e, 0x6f, 0x69, 0x6e, 0x70, 0x66, 0x6f, 0x72, 0x66, + 0x71, 0x75, 0x6b, 0x70, 0x71, 0x67, 0x71, 0x74, 0x6e, 0x70, 0x70, 0x62, + 0x74, 0x7a, 0x66, 0x74, 0x78, 0x70, 0x72, 0x72, 0x6d, 0x73, 0x75, 0x66, + 0x75, 0x7a, 0x68, 0x75, 0x79, 0x71, 0x73, 0x74, 0x72, 0x74, 0x75, 0x72, + 0x76, 0x77, 0x6c, 0x78, 0x7b, 0x72, 0x77, 0x78, 0x72, 0x77, 0x79, 0x73, + 0x78, 0x79, 0x72, 0x79, 0x7b, 0x72, 0x7b, 0x7e, 0x78, 0x7a, 0x7a, 0x77, + 0x7b, 0x7d, 0x74, 0x7c, 0x7e, 0x75, 0x7c, 0x7e, 0x76, 0x7d, 0x80, 0x77, + 0x7e, 0x80, 0x77, 0x80, 0x82, 0x7c, 0x7f, 0x7f, 0x72, 0x82, 0x87, 0x76, + 0x81, 0x85, 0x7b, 0x80, 0x81, 0x76, 0x83, 0x87, 0x7e, 0x85, 0x87, 0x83, + 0x84, 0x85, 0x7d, 0x87, 0x89, 0x7b, 0x88, 0x8c, 0x7b, 0x89, 0x8e, 0x7e, + 0x8a, 0x8d, 0x7f, 0x8b, 0x8e, 0x88, 0x8b, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, + 0x8c, 0x8c, 0x88, 0x8e, 0x91, 0x85, 0x8f, 0x93, 0x8c, 0x8e, 0x8f, 0x8d, + 0x8f, 0x8f, 0x87, 0x91, 0x94, 0x88, 0x93, 0x97, 0x91, 0x94, 0x96, 0x93, + 0x94, 0x95, 0x8d, 0x96, 0x9a, 0x8d, 0x97, 0x9a, 0x90, 0x97, 0x99, 0x95, + 0x96, 0x96, 0x91, 0x98, 0x9b, 0x94, 0x98, 0x99, 0x91, 0x99, 0x9b, 0x96, + 0x97, 0xa4, 0x93, 0x99, 0x9b, 0x91, 0x9a, 0x9c, 0x98, 0x99, 0x99, 0x93, + 0x9b, 0x9d, 0x97, 0x9c, 0x9d, 0x93, 0x9d, 0xa1, 0x95, 0x9d, 0x9f, 0x96, + 0x9d, 0xa0, 0x98, 0xa0, 0xa3, 0x9d, 0x9f, 0xa0, 0x97, 0xa1, 0xa5, 0xa1, + 0xa1, 0xa1, 0x99, 0xa3, 0xa7, 0x9b, 0xa3, 0xa5, 0xa4, 0xa5, 0xa5, 0x9d, + 0xa7, 0xa9, 0xa6, 0xa8, 0xa8, 0xa2, 0xa9, 0xac, 0xa2, 0xab, 0xad, 0xaa, + 0xab, 0xab, 0xa6, 0xad, 0xb0, 0xa5, 0xaf, 0xb2, 0xab, 0xb3, 0xb6, 0xaf, + 0xb2, 0xb4, 0xae, 0xb5, 0xb8, 0xb4, 0xb4, 0xb4, 0xb0, 0xb6, 0xb8, 0xb1, + 0xb7, 0xba, 0xb2, 0xb7, 0xb9, 0xb4, 0xb7, 0xb9, 0xb2, 0xb8, 0xb9, 0xb2, + 0xb8, 0xba, 0x00, 0xff, 0x00, 0xb3, 0xb8, 0xb9, 0xb3, 0xbb, 0xbe, 0xb4, + 0xbb, 0xbd, 0xb5, 0xbb, 0xbd, 0xb6, 0xbb, 0xbc, 0xb7, 0xbc, 0xbf, 0xb8, + 0xbd, 0xbe, 0xbd, 0xbe, 0xbf, 0xbc, 0xbf, 0xc0, 0xbc, 0xc1, 0xc2, 0xbd, + 0xc1, 0xc2, 0xbb, 0xc2, 0xc5, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xbe, + 0xc5, 0xc7, 0xc2, 0xc4, 0xc5, 0xc3, 0xc4, 0xc4, 0xc2, 0xc6, 0xc7, 0xc4, + 0xc6, 0xc7, 0xc3, 0xc7, 0xc8, 0xc3, 0xc7, 0xc9, 0xc3, 0xc8, 0xca, 0xc4, + 0xc9, 0xcb, 0xc6, 0xca, 0xcb, 0xc6, 0xcc, 0xce, 0xca, 0xcd, 0xce, 0xca, + 0xce, 0xd0, 0xcb, 0xce, 0xcf, 0xca, 0xcf, 0xd1, 0xcb, 0xcf, 0xd0, 0xcb, + 0xd0, 0xd2, 0xcd, 0xd0, 0xd1, 0xcf, 0xd1, 0xd2, 0xd0, 0xd1, 0xd1, 0xce, + 0xd2, 0xd3, 0xd0, 0xd2, 0xd3, 0xd0, 0xd3, 0xd4, 0xd2, 0xd4, 0xd5, 0xd4, + 0xd5, 0xd8, 0xd2, 0xd6, 0xd8, 0xd2, 0xd7, 0xd8, 0xd4, 0xd7, 0xd8, 0xd3, + 0xd8, 0xd9, 0xd7, 0xd7, 0xdd, 0xd6, 0xd8, 0xd9, 0xd6, 0xd8, 0xda, 0xd7, + 0xd8, 0xd8, 0xd7, 0xda, 0xdb, 0xd8, 0xdb, 0xdd, 0xd7, 0xdc, 0xde, 0xd9, + 0xdc, 0xdd, 0xda, 0xdc, 0xdc, 0xdb, 0xdd, 0xdd, 0xdb, 0xdd, 0xde, 0xdc, + 0xdf, 0xe0, 0xdd, 0xdf, 0xe0, 0xdd, 0xe0, 0xe0, 0xde, 0xe1, 0xe2, 0xe0, + 0xe1, 0xe2, 0xe0, 0xe2, 0xe2, 0xe1, 0xe3, 0xe3, 0xe1, 0xe3, 0xe4, 0xe2, + 0xe3, 0xe3, 0xe1, 0xe6, 0xe7, 0xe5, 0xe7, 0xe7, 0xe6, 0xe7, 0xe7, 0xe6, + 0xe7, 0xe8, 0xe6, 0xe8, 0xe9, 0xe7, 0xe8, 0xe9, 0xe4, 0xe9, 0xeb, 0xe6, + 0xe9, 0xea, 0xe7, 0xe9, 0xea, 0xe8, 0xe9, 0xea, 0xe6, 0xea, 0xeb, 0xe9, + 0xeb, 0xec, 0xe8, 0xec, 0xed, 0xea, 0xec, 0xed, 0xeb, 0xec, 0xed, 0xeb, + 0xed, 0xed, 0xec, 0xed, 0xee, 0xec, 0xef, 0xf1, 0xbe, 0x3f, 0x0a, 0xac, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, + 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, + 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, + 0x0e, 0xc4, 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, + 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, + 0x36, 0x22, 0x1d, 0x4a, 0x61, 0x42, 0x00, 0x00, 0x01, 0xda, 0x49, 0x44, + 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x20, 0x1b, 0xdc, 0x7e, 0x45, 0x94, + 0xb2, 0x0e, 0x67, 0xf5, 0xc3, 0x44, 0x28, 0xdb, 0x1c, 0xb7, 0x7a, 0xf5, + 0x44, 0xc2, 0xca, 0xde, 0x85, 0x14, 0xad, 0x5e, 0x2d, 0x7c, 0x88, 0xa0, + 0xba, 0x59, 0xd2, 0xab, 0x81, 0x60, 0x29, 0x41, 0x75, 0x9b, 0xec, 0x41, + 0xea, 0x7a, 0x08, 0xaa, 0x5b, 0xe9, 0x07, 0x52, 0x17, 0x4e, 0x50, 0xdd, + 0x9a, 0x60, 0x90, 0x3a, 0xd7, 0x27, 0x84, 0xd4, 0x4d, 0x76, 0x06, 0xa9, + 0xb3, 0xb8, 0x47, 0x48, 0xdd, 0x8a, 0x00, 0x90, 0xba, 0xa0, 0x13, 0x78, + 0x15, 0xbd, 0x3c, 0xbf, 0x35, 0xca, 0x1c, 0xa4, 0x4e, 0xa8, 0xa4, 0xbe, + 0xf9, 0x39, 0x4e, 0x65, 0x0f, 0x1c, 0xcd, 0x0c, 0x18, 0x35, 0x40, 0xea, + 0x58, 0x24, 0x9c, 0x04, 0xf3, 0x70, 0xaa, 0x3b, 0xad, 0x35, 0xbb, 0x8a, + 0x29, 0x14, 0xa4, 0xce, 0x34, 0x62, 0x49, 0x53, 0xe0, 0x5b, 0x5c, 0xea, + 0x76, 0x5b, 0xde, 0x5a, 0x8d, 0x00, 0x46, 0xf7, 0x71, 0x28, 0xfb, 0x5d, + 0x20, 0xbe, 0x03, 0x49, 0x1d, 0x7b, 0xcd, 0x65, 0xec, 0xea, 0xe6, 0x08, + 0xa4, 0xee, 0x44, 0x52, 0xb7, 0x5a, 0x21, 0x6c, 0x3b, 0x16, 0x55, 0x97, + 0xb7, 0x88, 0xc5, 0xee, 0x59, 0x8d, 0x02, 0xfc, 0x53, 0xa6, 0x9f, 0x42, + 0x57, 0x76, 0xc3, 0xd6, 0xa6, 0xed, 0xec, 0x6a, 0x34, 0x90, 0x23, 0xe3, + 0xb1, 0xe0, 0x17, 0xb2, 0xaa, 0x2f, 0xab, 0x32, 0xdc, 0xe6, 0xad, 0xc6, + 0x04, 0xd3, 0xf4, 0xe5, 0x0e, 0xbc, 0x47, 0x28, 0x7b, 0xd4, 0xcd, 0xba, + 0x1a, 0x07, 0xa8, 0x48, 0x6c, 0x38, 0x02, 0x53, 0xb6, 0x2c, 0x1a, 0x22, + 0x56, 0x69, 0x2f, 0x22, 0xcd, 0xc1, 0x09, 0x62, 0xf1, 0x33, 0x67, 0x27, + 0x4b, 0x79, 0xd5, 0xf5, 0x02, 0x99, 0x53, 0x4c, 0xaa, 0xdf, 0x40, 0x94, + 0x3d, 0xd6, 0x86, 0x28, 0x73, 0xcf, 0x9a, 0x71, 0xf2, 0x58, 0x2e, 0x1f, + 0x88, 0xa9, 0x58, 0xfa, 0xf4, 0xe7, 0x86, 0x49, 0x8d, 0x3a, 0x36, 0x20, + 0x8e, 0xe6, 0x7a, 0xa8, 0x79, 0x49, 0x20, 0x9e, 0x43, 0xf1, 0xde, 0xcf, + 0x40, 0xf6, 0xcd, 0x48, 0x10, 0xc7, 0xe0, 0x28, 0x58, 0xe2, 0x4c, 0xb9, + 0x35, 0x90, 0xc3, 0xb6, 0x0d, 0xa2, 0xec, 0x9e, 0xdd, 0x6a, 0x1e, 0xbd, + 0x96, 0xe3, 0x10, 0xce, 0x55, 0x63, 0x90, 0x3a, 0x95, 0xe5, 0x50, 0x23, + 0x76, 0x1d, 0x6e, 0x5d, 0x5d, 0x08, 0x65, 0x7f, 0x9b, 0xe0, 0xb9, 0xff, + 0x21, 0xcc, 0xad, 0x27, 0x45, 0x41, 0xea, 0x94, 0xe6, 0x23, 0x3c, 0x79, + 0xf0, 0x11, 0x9c, 0x79, 0x0d, 0x21, 0xfa, 0x4c, 0x15, 0xa4, 0x4e, 0x79, + 0x2e, 0xc1, 0x84, 0x9f, 0x09, 0x52, 0x67, 0x75, 0x81, 0x90, 0xb2, 0x3b, + 0xf1, 0x20, 0x75, 0xdc, 0x8b, 0x09, 0x28, 0xfb, 0xb1, 0xd0, 0x0e, 0xa4, + 0x4e, 0xbe, 0x8c, 0x40, 0x06, 0x99, 0x6a, 0x98, 0x00, 0x52, 0x57, 0xcb, + 0xeb, 0xb3, 0x16, 0x9f, 0xb2, 0x6d, 0x92, 0x31, 0x90, 0x30, 0x9f, 0xa9, + 0x2b, 0x7b, 0x0e, 0xb7, 0xb2, 0x17, 0xde, 0x69, 0xf0, 0x88, 0xcd, 0xef, + 0xff, 0x89, 0x53, 0x5d, 0x3b, 0x72, 0x0a, 0xe0, 0xda, 0x8e, 0x53, 0xdd, + 0x22, 0x94, 0xa4, 0xf2, 0x09, 0xa7, 0xba, 0x7d, 0x6a, 0xe9, 0x7d, 0xeb, + 0x2e, 0x75, 0xb9, 0x6c, 0xfc, 0x78, 0xa5, 0xd3, 0xf7, 0x22, 0x1e, 0x7f, + 0x7c, 0xf8, 0xfa, 0x1d, 0x48, 0x5e, 0x67, 0xf8, 0xc3, 0xf0, 0xfa, 0x2e, + 0x03, 0x4d, 0x01, 0x00, 0xfc, 0xea, 0x2e, 0xea, 0xf1, 0xce, 0x06, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int greenplane_png_len = 1368; +unsigned char aisred_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0xf9, 0xfb, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, + 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, + 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, + 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x30, 0xc5, 0xde, 0x43, 0xc9, + 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, + 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, + 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, + 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, + 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, + 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, + 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, + 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aisred_png_len = 227; +unsigned char aistrans_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x8a, 0xa6, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x06, 0x50, 0x4c, 0x54, 0x45, 0x6f, 0x72, 0x74, 0x00, 0x00, 0x00, 0x3b, + 0x8a, 0x80, 0x24, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, + 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, + 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, + 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, + 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, + 0x01, 0x09, 0x0a, 0x36, 0x08, 0xc6, 0xf1, 0xa8, 0x94, 0x00, 0x00, 0x00, + 0x43, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0x00, 0x01, 0x05, + 0x34, 0x5c, 0x00, 0xc4, 0x01, 0x50, 0x7c, 0x03, 0x88, 0x3b, 0x10, 0x98, + 0xb1, 0x07, 0x88, 0x59, 0x10, 0x98, 0x99, 0x8d, 0x81, 0x81, 0x89, 0x09, + 0x8a, 0x95, 0x20, 0x34, 0x1b, 0x33, 0x03, 0x03, 0x0b, 0x23, 0x26, 0xe6, + 0x61, 0x6c, 0x60, 0xe0, 0x60, 0x40, 0x60, 0x09, 0x86, 0x03, 0x0c, 0x02, + 0x0c, 0x0e, 0x0c, 0xf2, 0xff, 0x0f, 0x00, 0x00, 0x1c, 0xa6, 0x0a, 0x23, + 0x27, 0x5d, 0x69, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aistrans_png_len = 208; +unsigned char plane_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, + 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x19, 0x1b, 0x27, 0x2c, + 0x30, 0x41, 0x37, 0x44, 0x49, 0x42, 0x46, 0x48, 0x39, 0x4c, 0x51, 0x41, + 0x4b, 0x4c, 0x40, 0x4d, 0x53, 0x43, 0x4c, 0x58, 0x44, 0x4e, 0x4f, 0x44, + 0x51, 0x56, 0x4c, 0x50, 0x52, 0x4d, 0x51, 0x53, 0x41, 0x55, 0x59, 0x4e, + 0x52, 0x54, 0x49, 0x54, 0x54, 0x4f, 0x53, 0x55, 0x4b, 0x56, 0x57, 0x44, + 0x58, 0x5c, 0x45, 0x59, 0x5e, 0x4b, 0x58, 0x5e, 0x53, 0x57, 0x5a, 0x4f, + 0x59, 0x5a, 0x47, 0x5b, 0x60, 0x4e, 0x5c, 0x61, 0x56, 0x5a, 0x5c, 0x47, + 0x5e, 0x68, 0x4a, 0x5e, 0x62, 0x52, 0x5c, 0x5d, 0x58, 0x5c, 0x5e, 0x53, + 0x5e, 0x5e, 0x54, 0x5f, 0x5f, 0x5a, 0x5e, 0x60, 0x53, 0x60, 0x66, 0x55, + 0x60, 0x60, 0x56, 0x60, 0x61, 0x57, 0x61, 0x62, 0x5c, 0x60, 0x62, 0x50, + 0x64, 0x68, 0x5d, 0x61, 0x63, 0x56, 0x63, 0x69, 0x5e, 0x62, 0x64, 0x59, + 0x64, 0x64, 0x5f, 0x63, 0x65, 0x61, 0x65, 0x67, 0x5c, 0x67, 0x68, 0x63, + 0x66, 0x63, 0x62, 0x66, 0x68, 0x5b, 0x68, 0x6e, 0x55, 0x6a, 0x6e, 0x5d, + 0x68, 0x69, 0x62, 0x67, 0x69, 0x5e, 0x69, 0x6a, 0x5f, 0x6a, 0x6b, 0x64, + 0x68, 0x7c, 0x5e, 0x6b, 0x71, 0x58, 0x6d, 0x71, 0x66, 0x6a, 0x6c, 0x67, + 0x6b, 0x6e, 0x68, 0x6c, 0x6f, 0x63, 0x6e, 0x6f, 0x6d, 0x6b, 0x6f, 0x5c, + 0x70, 0x75, 0x69, 0x6d, 0x70, 0x62, 0x6f, 0x75, 0x65, 0x70, 0x71, 0x6b, + 0x6f, 0x71, 0x64, 0x71, 0x77, 0x66, 0x71, 0x72, 0x6e, 0x70, 0x6d, 0x60, + 0x74, 0x79, 0x6e, 0x72, 0x75, 0x70, 0x72, 0x6f, 0x67, 0x75, 0x7b, 0x6f, + 0x73, 0x76, 0x70, 0x74, 0x77, 0x71, 0x75, 0x78, 0x6d, 0x78, 0x78, 0x73, + 0x77, 0x79, 0x74, 0x78, 0x7b, 0x78, 0x7a, 0x77, 0x71, 0x7c, 0x7d, 0x77, + 0x7b, 0x7e, 0x78, 0x7c, 0x7f, 0x79, 0x7d, 0x80, 0x7c, 0x7e, 0x7b, 0x7b, + 0x7f, 0x82, 0x75, 0x81, 0x81, 0x74, 0x82, 0x88, 0x7c, 0x81, 0x83, 0x75, + 0x83, 0x89, 0x7e, 0x82, 0x85, 0x7f, 0x83, 0x86, 0x80, 0x84, 0x87, 0x83, + 0x85, 0x82, 0x7a, 0x88, 0x8e, 0x7c, 0x88, 0x88, 0x7b, 0x89, 0x8f, 0x83, + 0x87, 0x8a, 0x85, 0x87, 0x84, 0x7e, 0x8a, 0x8a, 0x7f, 0x8b, 0x8b, 0x87, + 0x89, 0x86, 0x86, 0x8a, 0x8c, 0x87, 0x8b, 0x8e, 0x89, 0x8b, 0x88, 0x8a, + 0x8c, 0x89, 0x89, 0x8d, 0x90, 0x84, 0x90, 0x91, 0x8c, 0x8f, 0x8c, 0x8b, + 0x8f, 0x91, 0x8e, 0x90, 0x8d, 0x86, 0x92, 0x93, 0x85, 0x93, 0x99, 0x8e, + 0x92, 0x95, 0x90, 0x92, 0x8f, 0x91, 0x93, 0x90, 0x90, 0x94, 0x97, 0x8b, + 0x97, 0x98, 0x92, 0x96, 0x99, 0x94, 0x96, 0x93, 0x93, 0x98, 0x9a, 0x95, + 0x97, 0xa6, 0x8f, 0x9a, 0x9b, 0x94, 0x99, 0x9b, 0x97, 0x99, 0x96, 0x95, + 0x9a, 0x9c, 0x97, 0x9b, 0x9e, 0x91, 0x9d, 0x9d, 0x99, 0x9c, 0x98, 0x93, + 0x9e, 0x9f, 0x98, 0x9d, 0x9f, 0x9b, 0x9e, 0x9a, 0x9b, 0x9f, 0xa2, 0x96, + 0xa1, 0xa2, 0x9d, 0xa0, 0x9d, 0x9c, 0xa1, 0xa3, 0x9e, 0xa1, 0x9e, 0x9d, + 0xa2, 0xa4, 0x98, 0xa4, 0xa4, 0xa0, 0xa2, 0x9f, 0xa1, 0xa3, 0xa0, 0xa3, + 0xa5, 0xa2, 0x9c, 0xa8, 0xa8, 0xa4, 0xa6, 0xa3, 0xa5, 0xa7, 0xa4, 0xa4, + 0xa8, 0xab, 0xa6, 0xa8, 0xa5, 0xa0, 0xac, 0xad, 0xa9, 0xab, 0xa8, 0xa8, + 0xac, 0xaf, 0xa3, 0xaf, 0xb0, 0xad, 0xaf, 0xac, 0xac, 0xb1, 0xb3, 0xaf, + 0xb2, 0xae, 0xae, 0xb3, 0xb5, 0xb0, 0xb3, 0xaf, 0xb1, 0xb4, 0xb0, 0xb0, + 0xb5, 0xb7, 0xb2, 0xb5, 0xb2, 0xb1, 0xb6, 0xb8, 0xb2, 0xb7, 0xba, 0xb4, + 0xb7, 0xb3, 0xb4, 0xb8, 0xbb, 0xb6, 0xb8, 0xb5, 0xb6, 0xba, 0xbd, 0xb8, + 0xba, 0xb7, 0xb7, 0xbb, 0xbe, 0xb9, 0xbb, 0xb8, 0xb8, 0xbc, 0xbf, 0xbb, + 0xbd, 0xba, 0xba, 0xbf, 0xc1, 0xbd, 0xbf, 0xbc, 0xbc, 0xc1, 0xc3, 0xbd, + 0xc2, 0xc4, 0xbf, 0xc2, 0xbe, 0xc0, 0xc3, 0xbf, 0xbf, 0xc4, 0xc6, 0xc1, + 0xc4, 0xc1, 0xc3, 0xc5, 0xc2, 0xc1, 0xc6, 0xc8, 0xc4, 0xc6, 0xc3, 0xc5, + 0xc7, 0xc4, 0xc3, 0xc8, 0xca, 0xc5, 0xc9, 0xcc, 0xc7, 0xc9, 0xc6, 0xc6, + 0xca, 0xcd, 0xc9, 0xcb, 0xc8, 0xc7, 0xcc, 0xce, 0xca, 0xcc, 0xc9, 0xc9, + 0xce, 0xd0, 0xcb, 0xce, 0xca, 0xca, 0xcf, 0xd1, 0xcb, 0xd0, 0xd2, 0xcd, + 0xd0, 0xcc, 0xcc, 0xd1, 0xd4, 0xce, 0xd1, 0xce, 0xcd, 0xd2, 0xd5, 0xd0, + 0xd2, 0xcf, 0xcf, 0xd3, 0xd6, 0xd1, 0xd3, 0xd0, 0xd0, 0xd4, 0xd7, 0xd3, + 0xd6, 0xd2, 0xd2, 0xd6, 0xd9, 0xd5, 0xd7, 0xd4, 0xd3, 0xd8, 0xda, 0xda, + 0xd7, 0xdb, 0xd4, 0xd9, 0xdb, 0xd6, 0xd9, 0xd5, 0xd5, 0xda, 0xdc, 0xd7, + 0xda, 0xd6, 0xd6, 0xdb, 0xde, 0xd8, 0xdb, 0xd7, 0xd7, 0xdc, 0xdf, 0xd9, + 0xdc, 0xd9, 0xd8, 0xdd, 0xe0, 0xdb, 0xdd, 0xda, 0xdc, 0xde, 0xdb, 0xdb, + 0xe0, 0xe2, 0xde, 0xe0, 0xdd, 0xdf, 0xe1, 0xde, 0xdd, 0xe2, 0xe4, 0xe0, + 0xe3, 0xdf, 0xdf, 0xe4, 0xe6, 0xe1, 0xe4, 0xe0, 0xe2, 0xe5, 0xe1, 0xe1, + 0xe6, 0xe9, 0xe3, 0xe6, 0xe2, 0xe5, 0xe7, 0xe4, 0xe3, 0xe8, 0xeb, 0xe5, + 0xe9, 0xec, 0xe7, 0xe9, 0xe6, 0xe6, 0xea, 0xed, 0xe8, 0xea, 0xe7, 0xe7, + 0xec, 0xee, 0xe8, 0xed, 0xef, 0xea, 0xed, 0xe9, 0xe9, 0xee, 0xf0, 0xeb, + 0xee, 0xea, 0xea, 0xef, 0xf2, 0xed, 0xf0, 0xed, 0xec, 0xf1, 0xf4, 0xef, + 0xf1, 0xee, 0xf0, 0xf2, 0xef, 0xf1, 0xf3, 0xf0, 0xf2, 0xf4, 0xf1, 0xf3, + 0xf6, 0xf2, 0xf4, 0xf7, 0xf3, 0xf5, 0xf8, 0xf4, 0xf6, 0xf9, 0xf6, 0xf8, + 0xfa, 0xf7, 0xf9, 0xfb, 0xf8, 0xfa, 0xfc, 0xf9, 0xfb, 0xfd, 0xfa, 0xfc, + 0xff, 0xfb, 0xff, 0xff, 0xff, 0x17, 0x03, 0x0e, 0x9e, 0x00, 0x00, 0x00, + 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, + 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xc4, 0x00, 0x00, + 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00, 0x00, 0x07, 0x74, + 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x3b, 0x79, 0x21, + 0xc9, 0x82, 0x00, 0x00, 0x02, 0x78, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, + 0x63, 0x60, 0x20, 0x1b, 0x5c, 0x7c, 0x40, 0x94, 0xb2, 0x4a, 0x4b, 0x95, + 0x5d, 0x44, 0x28, 0x5b, 0xed, 0xd7, 0x13, 0xdc, 0x44, 0x58, 0xd9, 0x33, + 0x97, 0xd8, 0x3b, 0xd3, 0x44, 0x76, 0x10, 0x54, 0xd7, 0x2f, 0x31, 0xe9, + 0xd7, 0x21, 0xd5, 0x05, 0x04, 0xd5, 0xad, 0x34, 0x5d, 0xf2, 0xed, 0x90, + 0x7e, 0x35, 0x41, 0x75, 0x8b, 0xed, 0xb7, 0xfe, 0x38, 0xe3, 0xe4, 0x49, + 0x58, 0x9d, 0xd3, 0xce, 0xdf, 0x57, 0x22, 0xcd, 0x6f, 0x11, 0x52, 0xd7, + 0x6a, 0xb9, 0xf3, 0xf7, 0x83, 0x18, 0x83, 0x8b, 0x84, 0xd4, 0x2d, 0xb0, + 0xdf, 0xfb, 0xe3, 0x71, 0xba, 0xf3, 0x01, 0xbc, 0x8a, 0xee, 0x1f, 0x5f, + 0xeb, 0xa9, 0xbf, 0xf3, 0xd7, 0x83, 0x34, 0x91, 0xf8, 0x82, 0xb2, 0x7b, + 0x38, 0x95, 0xdd, 0xb0, 0xd0, 0xd5, 0x62, 0x56, 0xdd, 0xf6, 0xfb, 0x71, + 0x16, 0x9b, 0x98, 0x25, 0x7f, 0x04, 0x4e, 0x75, 0x07, 0x55, 0x27, 0x67, + 0xb1, 0xb8, 0x5e, 0xf8, 0xfd, 0x65, 0x89, 0x9e, 0xc7, 0xdc, 0x52, 0xc7, + 0x27, 0xb8, 0xd4, 0x6d, 0x34, 0xbe, 0xf0, 0xbc, 0x77, 0xf3, 0xe7, 0x7f, + 0x7f, 0x1f, 0xae, 0xd9, 0x76, 0x63, 0xae, 0xf6, 0x15, 0x1c, 0xca, 0xde, + 0x46, 0x89, 0x6e, 0xf8, 0xfa, 0xee, 0xfb, 0xdf, 0x7f, 0xff, 0xfe, 0xfd, + 0xf8, 0xfa, 0x79, 0x02, 0x67, 0xce, 0x69, 0xec, 0xea, 0x26, 0x09, 0x05, + 0x6c, 0xfe, 0xf5, 0x0f, 0x0a, 0xbe, 0x6d, 0x8d, 0x95, 0x73, 0x5b, 0x87, + 0x45, 0xd5, 0xe9, 0xd5, 0x22, 0xde, 0x9b, 0x3f, 0xfd, 0x83, 0x83, 0xaf, + 0xfb, 0x7b, 0xec, 0x03, 0xbb, 0x0f, 0xa2, 0x2b, 0x3b, 0x6f, 0x60, 0x5c, + 0x71, 0xec, 0x0b, 0x50, 0x1e, 0x64, 0x2d, 0x98, 0xf8, 0xf3, 0x78, 0x55, + 0x98, 0x8c, 0xdd, 0xf4, 0x77, 0x28, 0xa9, 0x69, 0x71, 0x90, 0xe5, 0xd4, + 0x07, 0x7f, 0xfe, 0xfc, 0x7e, 0xf3, 0xf8, 0xf9, 0x37, 0xa0, 0x59, 0x0f, + 0x6f, 0xbf, 0xfc, 0xfa, 0xfb, 0xc7, 0xe7, 0x9d, 0x3d, 0x9a, 0xd2, 0x5b, + 0x9e, 0x21, 0x94, 0xdd, 0xaa, 0x61, 0xcf, 0xdb, 0xfc, 0xf2, 0xcb, 0x93, + 0xcd, 0xb3, 0xea, 0x8a, 0x4b, 0x4f, 0xfd, 0xfe, 0xbc, 0x2d, 0x33, 0x29, + 0xbd, 0xa5, 0x77, 0xff, 0xbb, 0xcf, 0x67, 0x96, 0x66, 0xf8, 0x15, 0xee, + 0x81, 0xc7, 0x95, 0xa7, 0x7d, 0xe6, 0x89, 0xdb, 0x87, 0x96, 0x64, 0x98, + 0x8a, 0x49, 0x70, 0x71, 0xcf, 0xf8, 0xfa, 0xbd, 0x57, 0x80, 0x35, 0xdc, + 0x5f, 0xc2, 0x3a, 0xbf, 0x76, 0xe7, 0xcd, 0x25, 0x1d, 0x3a, 0xd9, 0xd0, + 0x80, 0xbc, 0xa5, 0x26, 0x39, 0x75, 0xfd, 0x8a, 0x09, 0x0a, 0x96, 0x21, + 0x5d, 0x07, 0xf6, 0x45, 0xf0, 0x2f, 0xf9, 0xfc, 0x65, 0xa2, 0x7c, 0xe2, + 0xdd, 0xd7, 0xcb, 0x9b, 0x4b, 0x35, 0x8c, 0xd3, 0x7a, 0xfa, 0xf2, 0x55, + 0x96, 0x42, 0xcd, 0xf3, 0x8f, 0xeb, 0x29, 0xe2, 0x31, 0x4d, 0x00, 0xbb, + 0xe4, 0xbc, 0xfb, 0xac, 0xcf, 0x5f, 0xfa, 0xb4, 0xf6, 0x41, 0x22, 0x28, + 0xd5, 0xd0, 0xc4, 0x52, 0x96, 0x73, 0x2d, 0x34, 0x1f, 0x1a, 0xa9, 0xf3, + 0x69, 0x94, 0x41, 0x64, 0x18, 0xce, 0x6a, 0xcd, 0xfc, 0xfa, 0x69, 0x82, + 0x12, 0x2c, 0xe1, 0x6f, 0xdc, 0x55, 0x2e, 0x1c, 0x0b, 0x65, 0xbf, 0x6a, + 0xb0, 0xdb, 0x72, 0x1d, 0xe6, 0xd6, 0x03, 0x62, 0x93, 0x3e, 0x7d, 0xed, + 0x51, 0x9a, 0x86, 0xf0, 0xe4, 0x76, 0x44, 0x9a, 0x3d, 0x8b, 0x10, 0xbd, + 0xab, 0xd8, 0xf1, 0xfc, 0xd7, 0x44, 0xc5, 0x29, 0x04, 0x13, 0x7e, 0x70, + 0xf2, 0xed, 0x4f, 0x8d, 0x06, 0xc7, 0x09, 0x16, 0x1a, 0xbe, 0x21, 0x57, + 0xbe, 0x94, 0xf0, 0xce, 0x23, 0xa0, 0xec, 0xf5, 0x6c, 0xa3, 0xe8, 0x8b, + 0x5f, 0xeb, 0xe5, 0x52, 0x08, 0x64, 0x90, 0x36, 0x0d, 0xbf, 0x65, 0x9f, + 0xfe, 0x5d, 0xca, 0xe3, 0xb7, 0x5b, 0x8a, 0x4f, 0xd9, 0x5a, 0x71, 0xaf, + 0x25, 0x1f, 0xff, 0xfd, 0xfe, 0x72, 0x6a, 0x82, 0xba, 0x14, 0x1e, 0x27, + 0x3e, 0xb0, 0x09, 0x3a, 0xf1, 0xe9, 0xce, 0xde, 0x8d, 0x87, 0x5e, 0x9e, + 0x5a, 0x1d, 0xd9, 0xfc, 0x1a, 0x77, 0x91, 0x26, 0xd7, 0xb7, 0x6b, 0x4e, + 0xba, 0xad, 0x8e, 0x75, 0xde, 0x8c, 0xb9, 0xa1, 0xbc, 0xeb, 0x70, 0xaa, + 0x9b, 0x2d, 0x1b, 0xed, 0xcf, 0xcf, 0xc1, 0xc4, 0xa8, 0x3d, 0xcd, 0xc7, + 0x4a, 0x20, 0xe6, 0x19, 0x4e, 0x75, 0x5b, 0x94, 0x03, 0x1b, 0x96, 0x9e, + 0xac, 0x32, 0x5b, 0xf9, 0xec, 0x74, 0xb5, 0xc3, 0x49, 0x7c, 0xa5, 0xdf, + 0x8b, 0x57, 0x40, 0xf2, 0x1c, 0xc3, 0x5b, 0x86, 0xfb, 0x17, 0x19, 0x68, + 0x0a, 0x00, 0x52, 0xa4, 0x31, 0x22, 0xfa, 0xbb, 0x4c, 0x44, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int plane_png_len = 1522; +unsigned char helicopter_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, + 0x04, 0x03, 0x00, 0x00, 0x00, 0xed, 0x80, 0x0b, 0x0a, 0x00, 0x00, 0x00, + 0x15, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, + 0xf8, 0xff, 0xff, 0x00, 0x00, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0xff, + 0xff, 0xff, 0x96, 0xba, 0xb8, 0x46, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, + 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, + 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, + 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x2f, 0x63, 0xfb, 0x1d, 0xff, 0x00, 0x00, + 0x00, 0x70, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x9d, 0xd1, 0xc1, 0x0d, + 0x80, 0x20, 0x0c, 0x05, 0x50, 0x2e, 0xde, 0x6d, 0x52, 0x16, 0x20, 0x6e, + 0x00, 0x0b, 0x54, 0x3a, 0x80, 0x3d, 0xb8, 0xff, 0x2a, 0x62, 0x89, 0x26, + 0x68, 0x7b, 0xd0, 0x7f, 0x21, 0x7d, 0x49, 0x39, 0xfc, 0x06, 0x18, 0x33, + 0x87, 0x3f, 0x10, 0xf7, 0x3b, 0x62, 0x83, 0x26, 0xca, 0xe3, 0x0f, 0x0f, + 0x90, 0x3c, 0x40, 0x66, 0xd2, 0xc7, 0x81, 0x36, 0xd6, 0xc2, 0xa4, 0x6a, + 0x02, 0x72, 0xcd, 0x39, 0x17, 0xd6, 0x35, 0x0b, 0xfa, 0x7c, 0x0a, 0x81, + 0x0d, 0x97, 0x14, 0x02, 0x0f, 0xba, 0xf4, 0x0d, 0x07, 0x00, 0xd7, 0x94, + 0x16, 0x02, 0x1f, 0x5a, 0x89, 0xdb, 0xa4, 0x05, 0x7a, 0xa0, 0x24, 0x43, + 0x85, 0x2f, 0xf8, 0x74, 0xca, 0x03, 0x65, 0xe5, 0x3e, 0xf8, 0xf3, 0x53, + 0x78, 0x63, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82 +}; +unsigned int helicopter_png_len = 242; +unsigned char aisnavaid_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x93, 0xd9, 0x37, 0xd5, 0x00, 0x00, 0x00, + 0x06, 0x50, 0x4c, 0x54, 0x45, 0x63, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x0b, + 0x25, 0x74, 0xbd, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, + 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, + 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, + 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, + 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, + 0x01, 0x09, 0x0a, 0x22, 0x0e, 0x01, 0x3c, 0xda, 0xf4, 0x00, 0x00, 0x00, + 0x16, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff, 0x07, + 0x03, 0x0c, 0x3f, 0x28, 0xb0, 0xc0, 0x8a, 0x49, 0x54, 0x03, 0x00, 0x6b, + 0xd8, 0x2d, 0x07, 0x1f, 0xad, 0xa8, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aisnavaid_png_len = 163; + +unsigned char aiswhite_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, + 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x38, 0x24, 0x6b, 0x47, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, + 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, + 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, + 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, + 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, + 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x16, 0x3c, 0xfe, 0x95, 0xf7, + 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, + 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, + 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, + 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, + 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, + 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, + 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, + 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int aiswhite_png_len = 227; +char * get_info_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about\r\n" + "##CALLSIGN##
\r\n" + "Click the callsign to look it up on qrz.com
\r\n" + "Location: ##LOCATION##
\r\n" + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Status: ##STATUS_TEXT##
\r\n" + "Last heard ##LAST_HEARD## ago

\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_infomobile_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about\r\n" + "##CALLSIGN##
\r\n" + "Click the callsign to look it up on qrz.com
\r\n" + "Location: ##LOCATION##
\r\n" + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Course ##COURSE## degrees, speed ##SPEED_MPH##mph
" + "Status: ##STATUS_TEXT##
" + + "Last heard ##LAST_HEARD## ago

\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_infoobj_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about Object ##CALLSIGN##

" + + "Location: ##LOCATION##
\r\n" + + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Last Report: ##LASTPACKET##
" + + "Last heard ##LAST_HEARD## ago

\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + + +char * get_infowx_call() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

Information about\r\n" + "##CALLSIGN##
\r\n" + "Click the callsign to look it up on qrz.com
\r\n" + "Location: ##LOCATION##
\r\n" + "The bearing from ##MY_CALLSIGN## to ##CALLSIGN## is\r\n" + "##BEARING## degrees, the distance is ##DISTANCE## ##MILES_KM##

\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Last posit: ##FRAME_HEADER##
##FRAME_INFO##
\r\n" + + "

Status: ##STATUS_TEXT##
\r\n" + "Last heard ##LAST_HEARD## ago

\r\n" + + "

Weather Data

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
MeasurementImperial
Wind speed##WIND_SPEED_MPH## mph
Wind gust##WIND_GUST_MPH## mph
Wind direction##WIND_DIRECTION## deg
Temperature##TEMPERATURE_F##F
Pressure##PRESSURE_HPA## hPa/mb
Humidity##HUMIDITY##%
Rain last hour##RAIN_HOUR_IN##\"
Rain today##RAIN_TODAY_IN##\"
Rain last 24 hours##RAIN_24_IN##\"
\r\n" + + "

" + + "\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + + +char * get_all() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the stations heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_mobileall() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All Mobile Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the mobile stations heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_obj() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All Objects

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the objects heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + + +char * get_noinfo() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + "

No information is available for ##CALLSIGN##

\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_wxall() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

All WX Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the WX stations heard in the past ##EXPIRE_TIME## minutes,
both on RF and on the internet.

" + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "" + "##STATION_TABLE##
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_info() +{ + char Msg[] = + "" + "" + "" + "" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

Information

\r\n" + + "\r\n" + "
\r\n" + "

BPQ-WebServer is a simple plug-in web server for BPQ32. It will dynamically serve a selection of pages created from the data in BPQ32. The pages currently available are -

\r\n" + + "
    " + "
  • index.html - return to the home page
  • \r\n" + "
  • all.html - a list of all the stations heard
  • \r\n" + "
  • allrf.html - a list of all stations heard on RF
  • \r\n" + "
  • mobilesall.html - a list of all mobiles stations heard
  • \r\n" + "
  • mobilesrf.html - a list of all the mobile stations heard on RF
  • \r\n" + "
  • wxall.html - a list of all WX stations heard
  • \r\n" + "
  • wxrf.html - a list of all the WX stations heard on RF
  • \r\n" + "
  • obj.html - a list of all objects heard
  • \r\n" + "
  • objrf.html - a list of all objects heard on RF
  • \r\n" + "
" + + "

The dynamic pages use templates, so the user can make them as pretty or as simple as he wishes, including using graphics.

" + + "

Besides serving dynamic pages, BPQ-WebServer can also serve other pages that you may wish to make available. For example, this page is made available simply by putting it in BPQ-WebServer's HTML folder, and adding a link to it to the other pages.

" + "
\r\n"; + + return _strdup(Msg);; +} + +char * get_allrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

RF Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the stations heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + "" + "" + "" + "" + "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_mobilesrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

Mobile RF Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the mobile stations heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + // "" + // "" + // "" + // "" + // "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_objrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

RF Objects

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the objects heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + // "" + // "" + // "" + // "" + // "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_wxrf() +{ + char Msg[] = + "" + "" + "" + "" + "" + "\r\n" + " \r\n" + "\r\n" + "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" + "\r\n" + + "\r\n" + + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Home\r\n" + "All Stations\r\n" + "RF Stations\r\n" + "All WX Stations\r\n" + "RF WX Stations\r\n" + "All Mobile Stations\r\n" + "RF Mobile Stations\r\n" + "All Objects\r\n" + "RF Objects\r\n" + "Information\r\n" + "Node Pages\r\n" + "\r\n" + "\r\n" + + "\r\n" + "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" + + "

RF WX Stations

\r\n" + + "

(This page will automatically refresh every five minutes)

\r\n" + + "

The following is a list of all the WX stations heard on RF in the past ##EXPIRE_TIME## minutes.

" + + "

'*' after a callsign means that it was heard via a digi

" + + // "" + // "" + // "" + // "" + // "
The list only includes callsigns heard on RF, direct or via digipeaters.
It does not include callsigns heard on the internet,
or heard as third-party RF traffic via IGATEs.
" + + "

There are ##TABLE_COUNT## callsigns in the list,
click a callsign to get an information page for that station.

" + + "\r\n" + "\r\n" + "\r\n" + "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" + + "\r\n" + "\r\n"; + + return _strdup(Msg);; +} + +char * get_favicon(int * Len) +{ + unsigned char * ptr; + + unsigned char Msg[] = + { + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0xC0, 0xC0, + 0xC0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0x00, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xB0, 0x0B, 0xB0, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, + 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, + 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, + 0xBB, 0x00, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0x00, 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0x00, 0x00, 0x0B, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x0B, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0x00, 0x00, 0x0B, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, + 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + +#define favicon_LENGTH 0x000002FE + + ptr = malloc(favicon_LENGTH); + memcpy(ptr, Msg, favicon_LENGTH); + + return ptr; +} + +static unsigned char Clouds[] = +{ + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xE1, 0x08, 0x03, + 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x07, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x62, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x6A, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x72, 0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x86, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, + 0x68, 0x6F, 0x70, 0x20, 0x37, 0x2E, 0x30, 0x00, 0x32, 0x30, 0x31, 0x32, + 0x3A, 0x30, 0x35, 0x3A, 0x33, 0x30, 0x20, 0x31, 0x32, 0x3A, 0x33, 0x38, + 0x3A, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03, 0xA0, 0x01, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0xA0, 0x02, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF4, 0xA0, 0x03, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x06, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x16, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x1E, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x26, 0x02, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x06, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xED, 0x00, 0x0C, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x5F, 0x43, 0x4D, 0x00, 0x02, 0xFF, 0xEE, + 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, + 0x08, 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, + 0x0C, 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, + 0x0D, 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, + 0x0E, 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, + 0x80, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, + 0xDD, 0x00, 0x04, 0x00, 0x08, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, + 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, + 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, + 0x23, 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, + 0x07, 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, + 0xB2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, + 0x17, 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, + 0xF3, 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, + 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, + 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, + 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, + 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, + 0x52, 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, + 0x15, 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, + 0x26, 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, + 0x36, 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, + 0x46, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, + 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, + 0xD6, 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, + 0x11, 0x00, 0x3F, 0x00, 0xF4, 0x22, 0x09, 0x44, 0xAC, 0x18, 0xD1, 0x40, + 0x14, 0x7A, 0x84, 0x85, 0x29, 0x61, 0x0C, 0x60, 0xF8, 0x28, 0xB8, 0x4A, + 0xB0, 0x5A, 0x10, 0x8B, 0x7D, 0xDE, 0x09, 0x02, 0x92, 0x1A, 0xEE, 0x08, + 0x64, 0x42, 0xB2, 0xF0, 0x10, 0x6C, 0x6C, 0x09, 0x1A, 0xA2, 0x0A, 0xD2, + 0x1A, 0xEE, 0x74, 0x21, 0xEE, 0x51, 0xB9, 0xE4, 0x18, 0x00, 0x94, 0x22, + 0xF7, 0x07, 0x42, 0x78, 0x0B, 0x09, 0x4F, 0x29, 0xA5, 0x44, 0x6E, 0x70, + 0xD0, 0x29, 0x32, 0xB7, 0x1E, 0x78, 0x49, 0x4A, 0xE5, 0x49, 0xA0, 0xA9, + 0x06, 0x34, 0x09, 0xF0, 0x4C, 0xDE, 0x50, 0x4D, 0x32, 0x68, 0x46, 0x63, + 0x47, 0x7D, 0x14, 0x1A, 0xE8, 0x10, 0x8A, 0xD3, 0x25, 0x02, 0x90, 0xCB, + 0x60, 0x09, 0xE5, 0x46, 0x4F, 0x8A, 0x52, 0x7B, 0xA0, 0x9B, 0x7F, 0xFF, + 0xD0, 0xF4, 0x26, 0xB4, 0xF7, 0x56, 0x2B, 0x8F, 0x04, 0x1D, 0xE0, 0xF0, + 0x21, 0x15, 0xA6, 0x02, 0x98, 0xB0, 0x84, 0xC3, 0x85, 0x02, 0x07, 0x25, + 0x20, 0xFD, 0x14, 0x6C, 0x70, 0x23, 0x44, 0xDA, 0x4A, 0x2B, 0x1D, 0xAA, + 0x03, 0xAC, 0x04, 0xC1, 0x45, 0xB1, 0x84, 0x83, 0xE0, 0xAA, 0xB9, 0xAE, + 0x13, 0xF9, 0x53, 0x82, 0xD2, 0x56, 0x24, 0xB9, 0xDA, 0x08, 0x08, 0xAD, + 0xC6, 0x6C, 0x4B, 0xBE, 0xF5, 0x0A, 0xD8, 0x49, 0x93, 0xC0, 0xE0, 0x23, + 0x4F, 0xB7, 0x50, 0x89, 0x40, 0x44, 0xF0, 0xD6, 0x98, 0x89, 0xF3, 0x50, + 0xB2, 0xC0, 0xC0, 0x48, 0x10, 0xA5, 0x6B, 0xD8, 0xC3, 0x3A, 0x94, 0x17, + 0x58, 0xCB, 0x3E, 0x7D, 0x91, 0x08, 0x25, 0x1F, 0xAE, 0x1C, 0x76, 0xEB, + 0x25, 0x11, 0xB3, 0xA4, 0x4A, 0x42, 0x80, 0x06, 0xE5, 0x30, 0xD8, 0x8D, + 0x11, 0x40, 0xBE, 0xA9, 0x2B, 0x73, 0x46, 0x84, 0x4C, 0xA2, 0xFB, 0x47, + 0x65, 0x0A, 0xF9, 0x82, 0x34, 0x45, 0x21, 0xA1, 0x34, 0xAE, 0x0C, 0x80, + 0xDD, 0xC0, 0x48, 0x80, 0x07, 0x1A, 0xA8, 0xB2, 0xC6, 0x8E, 0xFA, 0x29, + 0x7A, 0x80, 0xA0, 0x97, 0xFF, 0xD1, 0xF4, 0x1A, 0xCC, 0x8D, 0x51, 0x37, + 0x82, 0x15, 0x2A, 0xAE, 0x9E, 0x55, 0x86, 0x49, 0x20, 0x8E, 0x14, 0xE4, + 0x35, 0xC1, 0x6C, 0x86, 0xCB, 0x7C, 0xD0, 0x5C, 0xE8, 0x3A, 0xA2, 0x87, + 0x10, 0x39, 0x55, 0xAD, 0x81, 0x24, 0x94, 0x02, 0x4B, 0x37, 0x5B, 0xB4, + 0x6B, 0xAC, 0xA1, 0xB5, 0xBB, 0xFE, 0x03, 0x55, 0x16, 0x89, 0x1A, 0xF7, + 0xE1, 0x11, 0xB0, 0xC6, 0xA2, 0x8B, 0x58, 0x80, 0x38, 0xF6, 0xA8, 0x97, + 0x1D, 0xB3, 0xBA, 0x0F, 0x82, 0x4E, 0xD7, 0xC8, 0x1E, 0xC9, 0xBD, 0x22, + 0x62, 0x52, 0x55, 0xA2, 0xB2, 0xB9, 0x1A, 0x19, 0x43, 0x2C, 0xF2, 0xD5, + 0x5D, 0x35, 0xD7, 0xB7, 0x68, 0x10, 0x50, 0x36, 0x41, 0x23, 0xF1, 0x4E, + 0x05, 0x04, 0x2E, 0xC7, 0x43, 0x7C, 0xD2, 0x0E, 0x6C, 0x68, 0xA0, 0x46, + 0xC9, 0xF1, 0x48, 0x81, 0x00, 0xFD, 0xE9, 0x52, 0x2D, 0x6F, 0x59, 0xD3, + 0x00, 0xC2, 0x9F, 0xAA, 0xF8, 0xFA, 0x48, 0x24, 0x7B, 0x92, 0x6D, 0x80, + 0x18, 0x76, 0x90, 0x95, 0x22, 0xD9, 0xFA, 0x86, 0x79, 0xD4, 0x27, 0x6B, + 0xDC, 0x4F, 0x2A, 0xBB, 0xAD, 0x6C, 0x94, 0x99, 0x76, 0xB2, 0x8D, 0x2A, + 0xDF, 0xFF, 0xD2, 0xED, 0x68, 0x68, 0x8F, 0x35, 0x66, 0xB7, 0x96, 0x13, + 0xAE, 0x8A, 0xB3, 0x76, 0x31, 0x93, 0x30, 0x67, 0x84, 0xDE, 0xA8, 0x2E, + 0xD4, 0xAB, 0x24, 0x5B, 0x50, 0x1A, 0x6F, 0x7A, 0xA1, 0xDD, 0xE5, 0x02, + 0xE7, 0x01, 0xA3, 0x8F, 0xC9, 0x53, 0xB3, 0x20, 0xB5, 0xE4, 0xB7, 0x8F, + 0x04, 0x3F, 0x5D, 0xD6, 0x76, 0x44, 0x41, 0x46, 0x6D, 0xC6, 0xD9, 0xAE, + 0x9C, 0x22, 0x87, 0xB7, 0xBF, 0x2A, 0xA3, 0x09, 0xDB, 0x10, 0x3F, 0x8A, + 0x98, 0x7B, 0x82, 0x44, 0x28, 0x49, 0xB6, 0x4B, 0x42, 0x94, 0x83, 0x0A, + 0xA8, 0xB0, 0x11, 0x12, 0x65, 0x19, 0x91, 0x09, 0xB4, 0xB8, 0x16, 0x76, + 0x4F, 0xE6, 0xEA, 0x7B, 0x21, 0x7B, 0x86, 0xAE, 0xD3, 0xE2, 0xA7, 0xEA, + 0x11, 0x12, 0x9A, 0xDB, 0x18, 0x5B, 0xA9, 0x94, 0x94, 0x85, 0xF1, 0x3E, + 0xEE, 0xE8, 0x86, 0xB9, 0x60, 0x11, 0x03, 0xC5, 0x0C, 0x34, 0x1D, 0x47, + 0x6E, 0xC5, 0x11, 0xB6, 0xB8, 0x0D, 0x46, 0x83, 0x84, 0x4A, 0x02, 0x27, + 0x8F, 0x4C, 0x6B, 0xCA, 0xA9, 0x6D, 0x85, 0xDF, 0x04, 0x7B, 0x8E, 0xF3, + 0xA6, 0x84, 0x94, 0xDE, 0x80, 0x03, 0xDC, 0x7E, 0x49, 0xC1, 0x69, 0xD5, + 0xA2, 0x5E, 0x67, 0x5F, 0xBD, 0x16, 0xA0, 0x4E, 0xBF, 0x72, 0xB4, 0xCC, + 0x6A, 0xCB, 0x74, 0x4E, 0x68, 0xD9, 0x03, 0x90, 0x8F, 0x10, 0x5B, 0xC2, + 0x5F, 0xFF, 0xD3, 0xEA, 0xCD, 0xA7, 0xE8, 0x84, 0xDB, 0xFC, 0x79, 0x4F, + 0xE9, 0x38, 0xA7, 0xF4, 0x65, 0x5C, 0xD1, 0xA1, 0xAA, 0x33, 0xA9, 0x48, + 0x13, 0xD9, 0x17, 0xD1, 0x69, 0xD4, 0x9D, 0x14, 0x0E, 0x86, 0x3C, 0x38, + 0x45, 0x54, 0xB7, 0xA8, 0x41, 0xD3, 0x94, 0x41, 0x69, 0xEE, 0x50, 0x5C, + 0xD2, 0x99, 0xAC, 0x74, 0xF9, 0x25, 0xA2, 0xAC, 0xB6, 0x1B, 0x71, 0x07, + 0x8F, 0x9A, 0x28, 0xB1, 0xC6, 0x35, 0x55, 0x64, 0x8E, 0x79, 0x44, 0x63, + 0x87, 0x32, 0x81, 0x09, 0x05, 0x3B, 0x9C, 0xE3, 0xAC, 0xA6, 0x13, 0x3A, + 0x73, 0xE2, 0xA2, 0x1C, 0x0F, 0x74, 0x4A, 0xE0, 0x90, 0x0F, 0x74, 0x17, + 0x32, 0x60, 0x23, 0xCD, 0x4C, 0x26, 0x88, 0x30, 0x91, 0x29, 0xA9, 0x46, + 0xE6, 0x80, 0xE9, 0x8E, 0x39, 0x51, 0x71, 0xDD, 0xA7, 0x08, 0xA5, 0x30, + 0x69, 0x3C, 0x04, 0x6D, 0x0C, 0x01, 0xD9, 0xC7, 0x3E, 0x2A, 0x0F, 0x79, + 0x8E, 0x54, 0x9E, 0xD2, 0x0A, 0x0B, 0xDA, 0x4A, 0x21, 0x05, 0xFF, 0xD4, + 0xEC, 0xC3, 0x44, 0x6A, 0x96, 0xE0, 0x3C, 0xD5, 0x6F, 0xB4, 0x87, 0x1D, + 0x24, 0xA2, 0x56, 0x4B, 0xCC, 0x42, 0xB7, 0x4D, 0x3B, 0x1D, 0x12, 0x68, + 0x75, 0xF0, 0x50, 0x73, 0x9B, 0xD8, 0x29, 0xBD, 0x9B, 0x47, 0x28, 0x71, + 0xDD, 0x20, 0xA2, 0xC6, 0x0F, 0x60, 0x9F, 0x67, 0x92, 0x93, 0x66, 0x51, + 0x01, 0x8E, 0x44, 0xA5, 0x6A, 0xA4, 0x05, 0x93, 0xCA, 0x67, 0x6D, 0x6A, + 0x23, 0xCE, 0xBA, 0x28, 0x47, 0xF2, 0x44, 0xA2, 0x82, 0x15, 0x59, 0x9F, + 0x29, 0xF1, 0x56, 0x2B, 0x60, 0xE4, 0x99, 0xF2, 0x55, 0xB7, 0x86, 0xC4, + 0x85, 0x2F, 0xB4, 0xBF, 0x81, 0xF8, 0xA0, 0x6D, 0x40, 0x80, 0xDB, 0x0E, + 0x05, 0x22, 0x55, 0x56, 0xD9, 0xAC, 0xCA, 0x28, 0xB0, 0x21, 0x4B, 0xAD, + 0x28, 0x1B, 0xB8, 0x45, 0xAD, 0xA5, 0xBC, 0xF7, 0x55, 0x3E, 0xD0, 0x1A, + 0xA4, 0xCC, 0xC6, 0x9D, 0x0F, 0x28, 0x10, 0x54, 0x08, 0x4D, 0x68, 0x07, + 0x8D, 0x4A, 0x09, 0x69, 0xF0, 0x4E, 0xEB, 0xC7, 0x68, 0x3E, 0x68, 0x67, + 0x29, 0xAD, 0x69, 0x9F, 0x92, 0x20, 0x15, 0x12, 0x1F, 0xFF, 0xD5, 0xEB, + 0x46, 0x33, 0x87, 0x01, 0x3F, 0xA7, 0x6B, 0x75, 0x06, 0x07, 0xE2, 0xAD, + 0x8D, 0xC3, 0x8E, 0xE9, 0xB6, 0x13, 0xCA, 0xB5, 0xC4, 0xD4, 0xE1, 0x0D, + 0x33, 0xEA, 0x18, 0x92, 0xA5, 0xC0, 0x8E, 0xEA, 0xDF, 0xA2, 0xD3, 0xD9, + 0x2F, 0x48, 0x01, 0xC2, 0x5C, 0x4A, 0xE1, 0x2D, 0x51, 0x1F, 0x34, 0xE6, + 0xC0, 0x02, 0x31, 0xAC, 0x14, 0x3B, 0x18, 0x47, 0x01, 0x2B, 0x0A, 0xA2, + 0x87, 0x79, 0x9D, 0x54, 0x1D, 0x6F, 0x75, 0x37, 0x53, 0x61, 0xD6, 0x14, + 0x0D, 0x64, 0x09, 0x21, 0x3B, 0x45, 0xA6, 0xD1, 0xBD, 0xFB, 0xB5, 0x01, + 0x38, 0x32, 0x3F, 0x82, 0x44, 0x38, 0xE8, 0x02, 0x23, 0x2B, 0x9F, 0x2F, + 0x8A, 0x56, 0x8A, 0x60, 0x1D, 0x1A, 0xA4, 0x6D, 0x8F, 0x8A, 0x28, 0xA9, + 0xDD, 0xB5, 0x1F, 0x82, 0x4E, 0xA8, 0x1D, 0x74, 0x94, 0xAC, 0x2A, 0x8A, + 0x22, 0xF9, 0x52, 0x68, 0xF2, 0x92, 0xA4, 0x6B, 0x20, 0x79, 0xF9, 0x26, + 0x1B, 0x87, 0x23, 0x44, 0xAD, 0x34, 0xC1, 0xAC, 0x71, 0x76, 0xB2, 0x8A, + 0x71, 0x89, 0xD7, 0x52, 0x54, 0x98, 0x78, 0x85, 0x65, 0x80, 0xC2, 0x06, + 0x49, 0x11, 0x0F, 0xFF, 0xD6, 0xEF, 0x9A, 0xDF, 0x15, 0x20, 0xDF, 0x24, + 0xFC, 0x15, 0x36, 0x89, 0x53, 0x5B, 0x05, 0x30, 0xDA, 0xA2, 0x5A, 0x8E, + 0x5A, 0xA0, 0x5A, 0x95, 0xA6, 0x90, 0x96, 0xC2, 0x81, 0x00, 0xA2, 0xB8, + 0x28, 0x22, 0xB5, 0x19, 0x60, 0x88, 0x84, 0x27, 0xD6, 0x22, 0x00, 0xE1, + 0x58, 0x25, 0x0C, 0xA2, 0x10, 0x40, 0x43, 0xB1, 0xA7, 0xE9, 0x69, 0x0A, + 0x62, 0xB1, 0x13, 0x32, 0x12, 0x21, 0x38, 0x24, 0x70, 0x8D, 0xA2, 0x91, + 0x19, 0x07, 0x44, 0xA1, 0xDF, 0xEC, 0x44, 0x30, 0x90, 0x84, 0xAD, 0x54, + 0xC1, 0xAD, 0x77, 0x8A, 0x23, 0x6B, 0x61, 0x3E, 0xED, 0x54, 0x80, 0x08, + 0x83, 0x6F, 0x64, 0x09, 0x48, 0x0C, 0x05, 0x4D, 0xEC, 0x11, 0x03, 0x23, + 0x84, 0xED, 0x08, 0x80, 0x21, 0x69, 0x01, 0xFF, 0xD9, 0xFF, 0xED, 0x0C, + 0x98, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, 0x68, 0x6F, 0x70, 0x20, 0x33, + 0x2E, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x25, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x03, + 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x38, + 0x42, 0x49, 0x4D, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1E, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x38, 0x42, 0x49, + 0x4D, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x42, 0x49, + 0x4D, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x03, + 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x2F, 0x66, 0x66, 0x00, + 0x01, 0x00, 0x6C, 0x66, 0x66, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x2F, 0x66, 0x66, 0x00, 0x01, 0x00, 0xA1, 0x99, 0x9A, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x03, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, + 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x38, + 0x42, 0x49, 0x4D, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x1E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, + 0x1A, 0x00, 0x00, 0x00, 0x00, 0x03, 0x41, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, + 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x06, 0x00, 0x63, 0x00, 0x6C, 0x00, + 0x6F, 0x00, 0x75, 0x00, 0x64, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x01, 0xF4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x75, + 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6F, + 0x75, 0x6E, 0x64, 0x73, 0x4F, 0x62, 0x6A, 0x63, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6F, 0x70, 0x20, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x65, + 0x66, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x42, 0x74, 0x6F, 0x6D, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, + 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x06, 0x73, 0x6C, + 0x69, 0x63, 0x65, 0x73, 0x56, 0x6C, 0x4C, 0x73, 0x00, 0x00, 0x00, 0x01, + 0x4F, 0x62, 0x6A, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x07, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x49, 0x44, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x67, 0x72, + 0x6F, 0x75, 0x70, 0x49, 0x44, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x65, + 0x6E, 0x75, 0x6D, 0x00, 0x00, 0x00, 0x0C, 0x45, 0x53, 0x6C, 0x69, 0x63, + 0x65, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x0D, 0x61, + 0x75, 0x74, 0x6F, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6E, 0x75, 0x6D, + 0x00, 0x00, 0x00, 0x0A, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6D, 0x67, 0x20, 0x00, 0x00, + 0x00, 0x06, 0x62, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x4F, 0x62, 0x6A, 0x63, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, + 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6F, + 0x70, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4C, 0x65, 0x66, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6F, 0x6D, 0x6C, 0x6F, + 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, + 0x68, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, + 0x00, 0x03, 0x75, 0x72, 0x6C, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x75, 0x6C, 0x6C, 0x54, + 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4D, 0x73, 0x67, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x61, 0x6C, 0x74, 0x54, 0x61, + 0x67, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x63, 0x65, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x49, + 0x73, 0x48, 0x54, 0x4D, 0x4C, 0x62, 0x6F, 0x6F, 0x6C, 0x01, 0x00, 0x00, + 0x00, 0x08, 0x63, 0x65, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x68, 0x6F, 0x72, 0x7A, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x65, 0x6E, 0x75, + 0x6D, 0x00, 0x00, 0x00, 0x0F, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x48, + 0x6F, 0x72, 0x7A, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x00, 0x00, 0x00, 0x07, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x00, 0x09, 0x76, + 0x65, 0x72, 0x74, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x65, 0x6E, 0x75, 0x6D, + 0x00, 0x00, 0x00, 0x0F, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x56, 0x65, + 0x72, 0x74, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x00, 0x00, 0x00, 0x07, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x62, 0x67, + 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6E, 0x75, + 0x6D, 0x00, 0x00, 0x00, 0x11, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x42, + 0x47, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x4E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x09, 0x74, 0x6F, + 0x70, 0x4F, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x6C, 0x65, 0x66, 0x74, 0x4F, + 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x4F, + 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x72, 0x69, 0x67, 0x68, 0x74, 0x4F, 0x75, + 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x04, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x06, 0xF1, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0x06, 0xD5, 0x00, 0x18, 0x00, 0x01, 0xFF, + 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, + 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xED, 0x00, 0x0C, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x5F, 0x43, 0x4D, 0x00, 0x02, 0xFF, 0xEE, 0x00, + 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, 0x08, + 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, 0x0C, + 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, 0x0D, + 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, 0x0E, + 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0x80, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDD, + 0x00, 0x04, 0x00, 0x08, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, 0x05, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, + 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, + 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, 0x23, + 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, 0x07, + 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, 0xB2, + 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, 0x17, + 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, + 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, + 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, + 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, + 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, + 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, 0x52, + 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, + 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, 0x26, + 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, 0x36, + 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, 0x46, + 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, 0xC5, + 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, + 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, + 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00, 0xF4, 0x22, 0x09, 0x44, 0xAC, 0x18, 0xD1, 0x40, 0x14, + 0x7A, 0x84, 0x85, 0x29, 0x61, 0x0C, 0x60, 0xF8, 0x28, 0xB8, 0x4A, 0xB0, + 0x5A, 0x10, 0x8B, 0x7D, 0xDE, 0x09, 0x02, 0x92, 0x1A, 0xEE, 0x08, 0x64, + 0x42, 0xB2, 0xF0, 0x10, 0x6C, 0x6C, 0x09, 0x1A, 0xA2, 0x0A, 0xD2, 0x1A, + 0xEE, 0x74, 0x21, 0xEE, 0x51, 0xB9, 0xE4, 0x18, 0x00, 0x94, 0x22, 0xF7, + 0x07, 0x42, 0x78, 0x0B, 0x09, 0x4F, 0x29, 0xA5, 0x44, 0x6E, 0x70, 0xD0, + 0x29, 0x32, 0xB7, 0x1E, 0x78, 0x49, 0x4A, 0xE5, 0x49, 0xA0, 0xA9, 0x06, + 0x34, 0x09, 0xF0, 0x4C, 0xDE, 0x50, 0x4D, 0x32, 0x68, 0x46, 0x63, 0x47, + 0x7D, 0x14, 0x1A, 0xE8, 0x10, 0x8A, 0xD3, 0x25, 0x02, 0x90, 0xCB, 0x60, + 0x09, 0xE5, 0x46, 0x4F, 0x8A, 0x52, 0x7B, 0xA0, 0x9B, 0x7F, 0xFF, 0xD0, + 0xF4, 0x26, 0xB4, 0xF7, 0x56, 0x2B, 0x8F, 0x04, 0x1D, 0xE0, 0xF0, 0x21, + 0x15, 0xA6, 0x02, 0x98, 0xB0, 0x84, 0xC3, 0x85, 0x02, 0x07, 0x25, 0x20, + 0xFD, 0x14, 0x6C, 0x70, 0x23, 0x44, 0xDA, 0x4A, 0x2B, 0x1D, 0xAA, 0x03, + 0xAC, 0x04, 0xC1, 0x45, 0xB1, 0x84, 0x83, 0xE0, 0xAA, 0xB9, 0xAE, 0x13, + 0xF9, 0x53, 0x82, 0xD2, 0x56, 0x24, 0xB9, 0xDA, 0x08, 0x08, 0xAD, 0xC6, + 0x6C, 0x4B, 0xBE, 0xF5, 0x0A, 0xD8, 0x49, 0x93, 0xC0, 0xE0, 0x23, 0x4F, + 0xB7, 0x50, 0x89, 0x40, 0x44, 0xF0, 0xD6, 0x98, 0x89, 0xF3, 0x50, 0xB2, + 0xC0, 0xC0, 0x48, 0x10, 0xA5, 0x6B, 0xD8, 0xC3, 0x3A, 0x94, 0x17, 0x58, + 0xCB, 0x3E, 0x7D, 0x91, 0x08, 0x25, 0x1F, 0xAE, 0x1C, 0x76, 0xEB, 0x25, + 0x11, 0xB3, 0xA4, 0x4A, 0x42, 0x80, 0x06, 0xE5, 0x30, 0xD8, 0x8D, 0x11, + 0x40, 0xBE, 0xA9, 0x2B, 0x73, 0x46, 0x84, 0x4C, 0xA2, 0xFB, 0x47, 0x65, + 0x0A, 0xF9, 0x82, 0x34, 0x45, 0x21, 0xA1, 0x34, 0xAE, 0x0C, 0x80, 0xDD, + 0xC0, 0x48, 0x80, 0x07, 0x1A, 0xA8, 0xB2, 0xC6, 0x8E, 0xFA, 0x29, 0x7A, + 0x80, 0xA0, 0x97, 0xFF, 0xD1, 0xF4, 0x1A, 0xCC, 0x8D, 0x51, 0x37, 0x82, + 0x15, 0x2A, 0xAE, 0x9E, 0x55, 0x86, 0x49, 0x20, 0x8E, 0x14, 0xE4, 0x35, + 0xC1, 0x6C, 0x86, 0xCB, 0x7C, 0xD0, 0x5C, 0xE8, 0x3A, 0xA2, 0x87, 0x10, + 0x39, 0x55, 0xAD, 0x81, 0x24, 0x94, 0x02, 0x4B, 0x37, 0x5B, 0xB4, 0x6B, + 0xAC, 0xA1, 0xB5, 0xBB, 0xFE, 0x03, 0x55, 0x16, 0x89, 0x1A, 0xF7, 0xE1, + 0x11, 0xB0, 0xC6, 0xA2, 0x8B, 0x58, 0x80, 0x38, 0xF6, 0xA8, 0x97, 0x1D, + 0xB3, 0xBA, 0x0F, 0x82, 0x4E, 0xD7, 0xC8, 0x1E, 0xC9, 0xBD, 0x22, 0x62, + 0x52, 0x55, 0xA2, 0xB2, 0xB9, 0x1A, 0x19, 0x43, 0x2C, 0xF2, 0xD5, 0x5D, + 0x35, 0xD7, 0xB7, 0x68, 0x10, 0x50, 0x36, 0x41, 0x23, 0xF1, 0x4E, 0x05, + 0x04, 0x2E, 0xC7, 0x43, 0x7C, 0xD2, 0x0E, 0x6C, 0x68, 0xA0, 0x46, 0xC9, + 0xF1, 0x48, 0x81, 0x00, 0xFD, 0xE9, 0x52, 0x2D, 0x6F, 0x59, 0xD3, 0x00, + 0xC2, 0x9F, 0xAA, 0xF8, 0xFA, 0x48, 0x24, 0x7B, 0x92, 0x6D, 0x80, 0x18, + 0x76, 0x90, 0x95, 0x22, 0xD9, 0xFA, 0x86, 0x79, 0xD4, 0x27, 0x6B, 0xDC, + 0x4F, 0x2A, 0xBB, 0xAD, 0x6C, 0x94, 0x99, 0x76, 0xB2, 0x8D, 0x2A, 0xDF, + 0xFF, 0xD2, 0xED, 0x68, 0x68, 0x8F, 0x35, 0x66, 0xB7, 0x96, 0x13, 0xAE, + 0x8A, 0xB3, 0x76, 0x31, 0x93, 0x30, 0x67, 0x84, 0xDE, 0xA8, 0x2E, 0xD4, + 0xAB, 0x24, 0x5B, 0x50, 0x1A, 0x6F, 0x7A, 0xA1, 0xDD, 0xE5, 0x02, 0xE7, + 0x01, 0xA3, 0x8F, 0xC9, 0x53, 0xB3, 0x20, 0xB5, 0xE4, 0xB7, 0x8F, 0x04, + 0x3F, 0x5D, 0xD6, 0x76, 0x44, 0x41, 0x46, 0x6D, 0xC6, 0xD9, 0xAE, 0x9C, + 0x22, 0x87, 0xB7, 0xBF, 0x2A, 0xA3, 0x09, 0xDB, 0x10, 0x3F, 0x8A, 0x98, + 0x7B, 0x82, 0x44, 0x28, 0x49, 0xB6, 0x4B, 0x42, 0x94, 0x83, 0x0A, 0xA8, + 0xB0, 0x11, 0x12, 0x65, 0x19, 0x91, 0x09, 0xB4, 0xB8, 0x16, 0x76, 0x4F, + 0xE6, 0xEA, 0x7B, 0x21, 0x7B, 0x86, 0xAE, 0xD3, 0xE2, 0xA7, 0xEA, 0x11, + 0x12, 0x9A, 0xDB, 0x18, 0x5B, 0xA9, 0x94, 0x94, 0x85, 0xF1, 0x3E, 0xEE, + 0xE8, 0x86, 0xB9, 0x60, 0x11, 0x03, 0xC5, 0x0C, 0x34, 0x1D, 0x47, 0x6E, + 0xC5, 0x11, 0xB6, 0xB8, 0x0D, 0x46, 0x83, 0x84, 0x4A, 0x02, 0x27, 0x8F, + 0x4C, 0x6B, 0xCA, 0xA9, 0x6D, 0x85, 0xDF, 0x04, 0x7B, 0x8E, 0xF3, 0xA6, + 0x84, 0x94, 0xDE, 0x80, 0x03, 0xDC, 0x7E, 0x49, 0xC1, 0x69, 0xD5, 0xA2, + 0x5E, 0x67, 0x5F, 0xBD, 0x16, 0xA0, 0x4E, 0xBF, 0x72, 0xB4, 0xCC, 0x6A, + 0xCB, 0x74, 0x4E, 0x68, 0xD9, 0x03, 0x90, 0x8F, 0x10, 0x5B, 0xC2, 0x5F, + 0xFF, 0xD3, 0xEA, 0xCD, 0xA7, 0xE8, 0x84, 0xDB, 0xFC, 0x79, 0x4F, 0xE9, + 0x38, 0xA7, 0xF4, 0x65, 0x5C, 0xD1, 0xA1, 0xAA, 0x33, 0xA9, 0x48, 0x13, + 0xD9, 0x17, 0xD1, 0x69, 0xD4, 0x9D, 0x14, 0x0E, 0x86, 0x3C, 0x38, 0x45, + 0x54, 0xB7, 0xA8, 0x41, 0xD3, 0x94, 0x41, 0x69, 0xEE, 0x50, 0x5C, 0xD2, + 0x99, 0xAC, 0x74, 0xF9, 0x25, 0xA2, 0xAC, 0xB6, 0x1B, 0x71, 0x07, 0x8F, + 0x9A, 0x28, 0xB1, 0xC6, 0x35, 0x55, 0x64, 0x8E, 0x79, 0x44, 0x63, 0x87, + 0x32, 0x81, 0x09, 0x05, 0x3B, 0x9C, 0xE3, 0xAC, 0xA6, 0x13, 0x3A, 0x73, + 0xE2, 0xA2, 0x1C, 0x0F, 0x74, 0x4A, 0xE0, 0x90, 0x0F, 0x74, 0x17, 0x32, + 0x60, 0x23, 0xCD, 0x4C, 0x26, 0x88, 0x30, 0x91, 0x29, 0xA9, 0x46, 0xE6, + 0x80, 0xE9, 0x8E, 0x39, 0x51, 0x71, 0xDD, 0xA7, 0x08, 0xA5, 0x30, 0x69, + 0x3C, 0x04, 0x6D, 0x0C, 0x01, 0xD9, 0xC7, 0x3E, 0x2A, 0x0F, 0x79, 0x8E, + 0x54, 0x9E, 0xD2, 0x0A, 0x0B, 0xDA, 0x4A, 0x21, 0x05, 0xFF, 0xD4, 0xEC, + 0xC3, 0x44, 0x6A, 0x96, 0xE0, 0x3C, 0xD5, 0x6F, 0xB4, 0x87, 0x1D, 0x24, + 0xA2, 0x56, 0x4B, 0xCC, 0x42, 0xB7, 0x4D, 0x3B, 0x1D, 0x12, 0x68, 0x75, + 0xF0, 0x50, 0x73, 0x9B, 0xD8, 0x29, 0xBD, 0x9B, 0x47, 0x28, 0x71, 0xDD, + 0x20, 0xA2, 0xC6, 0x0F, 0x60, 0x9F, 0x67, 0x92, 0x93, 0x66, 0x51, 0x01, + 0x8E, 0x44, 0xA5, 0x6A, 0xA4, 0x05, 0x93, 0xCA, 0x67, 0x6D, 0x6A, 0x23, + 0xCE, 0xBA, 0x28, 0x47, 0xF2, 0x44, 0xA2, 0x82, 0x15, 0x59, 0x9F, 0x29, + 0xF1, 0x56, 0x2B, 0x60, 0xE4, 0x99, 0xF2, 0x55, 0xB7, 0x86, 0xC4, 0x85, + 0x2F, 0xB4, 0xBF, 0x81, 0xF8, 0xA0, 0x6D, 0x40, 0x80, 0xDB, 0x0E, 0x05, + 0x22, 0x55, 0x56, 0xD9, 0xAC, 0xCA, 0x28, 0xB0, 0x21, 0x4B, 0xAD, 0x28, + 0x1B, 0xB8, 0x45, 0xAD, 0xA5, 0xBC, 0xF7, 0x55, 0x3E, 0xD0, 0x1A, 0xA4, + 0xCC, 0xC6, 0x9D, 0x0F, 0x28, 0x10, 0x54, 0x08, 0x4D, 0x68, 0x07, 0x8D, + 0x4A, 0x09, 0x69, 0xF0, 0x4E, 0xEB, 0xC7, 0x68, 0x3E, 0x68, 0x67, 0x29, + 0xAD, 0x69, 0x9F, 0x92, 0x20, 0x15, 0x12, 0x1F, 0xFF, 0xD5, 0xEB, 0x46, + 0x33, 0x87, 0x01, 0x3F, 0xA7, 0x6B, 0x75, 0x06, 0x07, 0xE2, 0xAD, 0x8D, + 0xC3, 0x8E, 0xE9, 0xB6, 0x13, 0xCA, 0xB5, 0xC4, 0xD4, 0xE1, 0x0D, 0x33, + 0xEA, 0x18, 0x92, 0xA5, 0xC0, 0x8E, 0xEA, 0xDF, 0xA2, 0xD3, 0xD9, 0x2F, + 0x48, 0x01, 0xC2, 0x5C, 0x4A, 0xE1, 0x2D, 0x51, 0x1F, 0x34, 0xE6, 0xC0, + 0x02, 0x31, 0xAC, 0x14, 0x3B, 0x18, 0x47, 0x01, 0x2B, 0x0A, 0xA2, 0x87, + 0x79, 0x9D, 0x54, 0x1D, 0x6F, 0x75, 0x37, 0x53, 0x61, 0xD6, 0x14, 0x0D, + 0x64, 0x09, 0x21, 0x3B, 0x45, 0xA6, 0xD1, 0xBD, 0xFB, 0xB5, 0x01, 0x38, + 0x32, 0x3F, 0x82, 0x44, 0x38, 0xE8, 0x02, 0x23, 0x2B, 0x9F, 0x2F, 0x8A, + 0x56, 0x8A, 0x60, 0x1D, 0x1A, 0xA4, 0x6D, 0x8F, 0x8A, 0x28, 0xA9, 0xDD, + 0xB5, 0x1F, 0x82, 0x4E, 0xA8, 0x1D, 0x74, 0x94, 0xAC, 0x2A, 0x8A, 0x22, + 0xF9, 0x52, 0x68, 0xF2, 0x92, 0xA4, 0x6B, 0x20, 0x79, 0xF9, 0x26, 0x1B, + 0x87, 0x23, 0x44, 0xAD, 0x34, 0xC1, 0xAC, 0x71, 0x76, 0xB2, 0x8A, 0x71, + 0x89, 0xD7, 0x52, 0x54, 0x98, 0x78, 0x85, 0x65, 0x80, 0xC2, 0x06, 0x49, + 0x11, 0x0F, 0xFF, 0xD6, 0xEF, 0x9A, 0xDF, 0x15, 0x20, 0xDF, 0x24, 0xFC, + 0x15, 0x36, 0x89, 0x53, 0x5B, 0x05, 0x30, 0xDA, 0xA2, 0x5A, 0x8E, 0x5A, + 0xA0, 0x5A, 0x95, 0xA6, 0x90, 0x96, 0xC2, 0x81, 0x00, 0xA2, 0xB8, 0x28, + 0x22, 0xB5, 0x19, 0x60, 0x88, 0x84, 0x27, 0xD6, 0x22, 0x00, 0xE1, 0x58, + 0x25, 0x0C, 0xA2, 0x10, 0x40, 0x43, 0xB1, 0xA7, 0xE9, 0x69, 0x0A, 0x62, + 0xB1, 0x13, 0x32, 0x12, 0x21, 0x38, 0x24, 0x70, 0x8D, 0xA2, 0x91, 0x19, + 0x07, 0x44, 0xA1, 0xDF, 0xEC, 0x44, 0x30, 0x90, 0x84, 0xAD, 0x54, 0xC1, + 0xAD, 0x77, 0x8A, 0x23, 0x6B, 0x61, 0x3E, 0xED, 0x54, 0x80, 0x08, 0x83, + 0x6F, 0x64, 0x09, 0x48, 0x0C, 0x05, 0x4D, 0xEC, 0x11, 0x03, 0x23, 0x84, + 0xED, 0x08, 0x80, 0x21, 0x69, 0x01, 0xFF, 0xD9, 0x00, 0x38, 0x42, 0x49, + 0x4D, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6F, + 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, + 0x00, 0x74, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x13, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x62, + 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x74, + 0x00, 0x6F, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x70, 0x00, 0x20, + 0x00, 0x37, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, + 0x42, 0x49, 0x4D, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xFF, 0xE1, 0x12, 0x48, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, + 0x62, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, 0x2F, 0x31, + 0x2E, 0x30, 0x2F, 0x00, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, + 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x3D, 0x27, 0xEF, 0xBB, 0xBF, + 0x27, 0x20, 0x69, 0x64, 0x3D, 0x27, 0x57, 0x35, 0x4D, 0x30, 0x4D, 0x70, + 0x43, 0x65, 0x68, 0x69, 0x48, 0x7A, 0x72, 0x65, 0x53, 0x7A, 0x4E, 0x54, + 0x63, 0x7A, 0x6B, 0x63, 0x39, 0x64, 0x27, 0x3F, 0x3E, 0x0A, 0x3C, 0x3F, + 0x61, 0x64, 0x6F, 0x62, 0x65, 0x2D, 0x78, 0x61, 0x70, 0x2D, 0x66, 0x69, + 0x6C, 0x74, 0x65, 0x72, 0x73, 0x20, 0x65, 0x73, 0x63, 0x3D, 0x22, 0x43, + 0x52, 0x22, 0x3F, 0x3E, 0x0A, 0x3C, 0x78, 0x3A, 0x78, 0x61, 0x70, 0x6D, + 0x65, 0x74, 0x61, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x3D, + 0x27, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x3A, 0x6E, 0x73, 0x3A, 0x6D, 0x65, + 0x74, 0x61, 0x2F, 0x27, 0x20, 0x78, 0x3A, 0x78, 0x61, 0x70, 0x74, 0x6B, + 0x3D, 0x27, 0x58, 0x4D, 0x50, 0x20, 0x74, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, + 0x74, 0x20, 0x32, 0x2E, 0x38, 0x2E, 0x32, 0x2D, 0x33, 0x33, 0x2C, 0x20, + 0x66, 0x72, 0x61, 0x6D, 0x65, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x31, 0x2E, + 0x35, 0x27, 0x3E, 0x0A, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, + 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x72, 0x64, 0x66, 0x3D, 0x27, + 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, + 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x31, 0x39, 0x39, 0x39, 0x2F, 0x30, + 0x32, 0x2F, 0x32, 0x32, 0x2D, 0x72, 0x64, 0x66, 0x2D, 0x73, 0x79, 0x6E, + 0x74, 0x61, 0x78, 0x2D, 0x6E, 0x73, 0x23, 0x27, 0x20, 0x78, 0x6D, 0x6C, + 0x6E, 0x73, 0x3A, 0x69, 0x58, 0x3D, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, + 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x2E, 0x63, + 0x6F, 0x6D, 0x2F, 0x69, 0x58, 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x27, 0x3E, + 0x0A, 0x0A, 0x20, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x61, 0x62, 0x6F, 0x75, + 0x74, 0x3D, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3A, 0x65, 0x35, 0x35, 0x35, + 0x61, 0x39, 0x65, 0x35, 0x2D, 0x61, 0x61, 0x38, 0x65, 0x2D, 0x31, 0x31, + 0x65, 0x31, 0x2D, 0x62, 0x30, 0x62, 0x33, 0x2D, 0x65, 0x62, 0x31, 0x39, + 0x32, 0x32, 0x62, 0x38, 0x35, 0x34, 0x30, 0x35, 0x27, 0x0A, 0x20, 0x20, + 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x61, 0x70, 0x4D, 0x4D, 0x3D, + 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, + 0x64, 0x6F, 0x62, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, + 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x6D, 0x6D, 0x2F, 0x27, 0x3E, 0x0A, 0x20, + 0x20, 0x3C, 0x78, 0x61, 0x70, 0x4D, 0x4D, 0x3A, 0x44, 0x6F, 0x63, 0x75, + 0x6D, 0x65, 0x6E, 0x74, 0x49, 0x44, 0x3E, 0x61, 0x64, 0x6F, 0x62, 0x65, + 0x3A, 0x64, 0x6F, 0x63, 0x69, 0x64, 0x3A, 0x70, 0x68, 0x6F, 0x74, 0x6F, + 0x73, 0x68, 0x6F, 0x70, 0x3A, 0x65, 0x35, 0x35, 0x35, 0x61, 0x39, 0x65, + 0x31, 0x2D, 0x61, 0x61, 0x38, 0x65, 0x2D, 0x31, 0x31, 0x65, 0x31, 0x2D, + 0x62, 0x30, 0x62, 0x33, 0x2D, 0x65, 0x62, 0x31, 0x39, 0x32, 0x32, 0x62, + 0x38, 0x35, 0x34, 0x30, 0x35, 0x3C, 0x2F, 0x78, 0x61, 0x70, 0x4D, 0x4D, + 0x3A, 0x44, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x49, 0x44, 0x3E, + 0x0A, 0x20, 0x3C, 0x2F, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0A, 0x0A, 0x3C, 0x2F, + 0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, 0x3E, 0x0A, 0x3C, 0x2F, 0x78, + 0x3A, 0x78, 0x61, 0x70, 0x6D, 0x65, 0x74, 0x61, 0x3E, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0A, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, 0x74, 0x20, + 0x65, 0x6E, 0x64, 0x3D, 0x27, 0x77, 0x27, 0x3F, 0x3E, 0xFF, 0xEE, 0x00, + 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, 0x08, + 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, 0x0C, + 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, 0x0D, + 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, 0x0E, + 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0xF4, 0x01, 0xF4, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDD, + 0x00, 0x04, 0x00, 0x20, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, 0x05, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, + 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, + 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, 0x23, + 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, 0x07, + 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, 0xB2, + 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, 0x17, + 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, + 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, + 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, + 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, + 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, + 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, 0x52, + 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, + 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, 0x26, + 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, 0x36, + 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, 0x46, + 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, 0xC5, + 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, + 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, + 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00, 0xF4, 0x24, 0xC9, 0xD3, 0x15, 0x33, 0x02, 0xC5, 0x20, + 0x98, 0xA7, 0x09, 0x29, 0x90, 0x53, 0x0A, 0x01, 0x4C, 0x24, 0x96, 0x61, + 0x24, 0xC9, 0xD0, 0x4A, 0x92, 0x49, 0x24, 0x94, 0xA4, 0xD0, 0x9D, 0x24, + 0x94, 0xB2, 0x89, 0x52, 0x2A, 0x25, 0x24, 0x2C, 0x53, 0x27, 0x29, 0x8A, + 0x2A, 0x59, 0x44, 0xA9, 0x14, 0xC5, 0x24, 0x31, 0x2A, 0x25, 0x49, 0x32, + 0x28, 0x62, 0x92, 0x74, 0xC9, 0x29, 0x64, 0x92, 0x4C, 0x52, 0x52, 0xC5, + 0x32, 0x72, 0x98, 0xA2, 0x85, 0x93, 0x14, 0x89, 0x4C, 0x4A, 0x4A, 0x5D, + 0x34, 0xA6, 0x49, 0x24, 0x2F, 0x29, 0x4A, 0x69, 0x49, 0x15, 0x29, 0x24, + 0x92, 0x49, 0x4B, 0x24, 0x92, 0x49, 0x29, 0x49, 0x24, 0x92, 0x4A, 0x52, + 0x49, 0x24, 0x92, 0x94, 0x99, 0x24, 0x92, 0x52, 0x92, 0x49, 0x3A, 0x4A, + 0x5D, 0x38, 0x4C, 0x13, 0xA4, 0xA5, 0x27, 0x4C, 0x9C, 0x20, 0x95, 0xD3, + 0xA6, 0x09, 0xD2, 0x52, 0xE9, 0x24, 0x14, 0xA1, 0x25, 0x28, 0x05, 0x28, + 0x48, 0x05, 0x20, 0xD4, 0x12, 0xB4, 0x25, 0x0A, 0x61, 0xA5, 0x3E, 0xD4, + 0x13, 0x4C, 0x00, 0x52, 0x4A, 0x13, 0x80, 0x92, 0x94, 0x02, 0x78, 0x49, + 0x24, 0x92, 0xA4, 0x92, 0x49, 0x25, 0x2C, 0x92, 0x7E, 0xE9, 0x24, 0xA7, + 0xFF, 0xD0, 0xF4, 0x24, 0x88, 0x4A, 0x12, 0x85, 0x33, 0x0B, 0x18, 0x4E, + 0x02, 0x78, 0x4E, 0x92, 0x95, 0x0A, 0x4D, 0x09, 0x94, 0xDA, 0x82, 0x97, + 0x84, 0xA1, 0x48, 0x27, 0x84, 0x92, 0xC1, 0x25, 0x22, 0x13, 0x24, 0xA5, + 0x93, 0x29, 0x42, 0x45, 0x25, 0x31, 0x4C, 0xA4, 0x99, 0x24, 0x31, 0x84, + 0xC4, 0x29, 0x24, 0x42, 0x4A, 0x60, 0x42, 0x89, 0x0A, 0x70, 0x98, 0x84, + 0x50, 0xC0, 0x85, 0x12, 0x11, 0x08, 0x51, 0x29, 0x29, 0x81, 0x4C, 0xA4, + 0x53, 0x22, 0x86, 0x29, 0x8A, 0x91, 0x51, 0x29, 0x29, 0x89, 0x51, 0x25, + 0x39, 0x51, 0x25, 0x14, 0x28, 0x94, 0xD2, 0x98, 0x94, 0xD2, 0x8A, 0x17, + 0x94, 0xA5, 0x46, 0x53, 0xA4, 0x85, 0xD2, 0x4D, 0x29, 0x24, 0xA5, 0xD2, + 0x4C, 0x94, 0xA4, 0x95, 0xD2, 0x4D, 0x29, 0x4A, 0x4A, 0x5D, 0x32, 0x49, + 0x12, 0x92, 0x95, 0x29, 0x26, 0x4A, 0x52, 0x52, 0xE9, 0x24, 0x9D, 0x25, + 0x29, 0x28, 0x4E, 0x13, 0xA0, 0xA5, 0x04, 0x92, 0x52, 0x09, 0x25, 0x68, + 0x4E, 0x94, 0x27, 0xDA, 0x92, 0x94, 0x13, 0xA5, 0xB4, 0xA7, 0x0D, 0x29, + 0x29, 0x41, 0x48, 0x24, 0x1A, 0x9F, 0x6A, 0x09, 0x5C, 0x15, 0x36, 0xA8, + 0x06, 0x95, 0x2D, 0x40, 0x41, 0x21, 0x20, 0x21, 0x3E, 0x8A, 0x0D, 0x53, + 0x49, 0x2B, 0x10, 0xA2, 0xA4, 0x98, 0xA4, 0xA5, 0x4A, 0x64, 0x92, 0x49, + 0x0A, 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, 0xFF, 0x00, 0xFF, + 0xD1, 0xF4, 0x24, 0xEA, 0x29, 0xC2, 0x99, 0x81, 0x92, 0x50, 0x94, 0x84, + 0x92, 0x4A, 0xE0, 0x22, 0x00, 0x86, 0x11, 0x1A, 0x50, 0x2A, 0x0C, 0x80, + 0x4F, 0x09, 0xC2, 0x94, 0x04, 0x12, 0xC2, 0x12, 0x85, 0x32, 0xD4, 0xD0, + 0x95, 0xA5, 0x86, 0xD4, 0x88, 0x52, 0x84, 0x92, 0x42, 0x32, 0x21, 0x45, + 0x11, 0xCA, 0x05, 0x15, 0x2C, 0x9F, 0x94, 0x80, 0x4F, 0x10, 0x92, 0x98, + 0x90, 0xA2, 0x42, 0x99, 0xE5, 0x31, 0x09, 0x21, 0x19, 0x0A, 0x24, 0x22, + 0x15, 0x12, 0x25, 0x15, 0x23, 0x51, 0x44, 0x2D, 0x51, 0x20, 0x84, 0x51, + 0x4C, 0x0A, 0x89, 0x52, 0x2A, 0x05, 0x24, 0x31, 0x28, 0x6E, 0x2A, 0x6E, + 0x42, 0x71, 0x44, 0x2D, 0x2A, 0x94, 0xC4, 0xA6, 0x95, 0x12, 0x8A, 0x19, + 0x4A, 0x79, 0x43, 0x94, 0xB7, 0x22, 0xA4, 0x92, 0x90, 0x2A, 0x1B, 0x93, + 0x82, 0x92, 0x99, 0xA4, 0x9A, 0x52, 0x94, 0x14, 0xBA, 0x69, 0x4C, 0x4A, + 0x69, 0x49, 0x4C, 0xE5, 0x28, 0x25, 0x44, 0x4A, 0x23, 0x02, 0x49, 0x63, + 0x05, 0x38, 0x0A, 0x65, 0xA1, 0x20, 0x12, 0xB5, 0x53, 0x18, 0x29, 0xC0, + 0x28, 0x81, 0xA9, 0xC8, 0x08, 0x5A, 0x69, 0x84, 0x42, 0x8A, 0x21, 0x1A, + 0x28, 0x10, 0x92, 0x94, 0x14, 0x82, 0x82, 0x90, 0x49, 0x4C, 0x82, 0x70, + 0x52, 0x09, 0xD2, 0x53, 0x30, 0x9E, 0x14, 0x01, 0x85, 0x2D, 0xC8, 0x25, + 0x74, 0xE1, 0x44, 0x15, 0x24, 0x92, 0xBC, 0xA4, 0x13, 0x27, 0x08, 0x29, + 0x96, 0x81, 0x2D, 0xC5, 0x32, 0x64, 0x94, 0xCB, 0x72, 0x45, 0xC1, 0x45, + 0x24, 0x95, 0x6C, 0xA5, 0x31, 0x29, 0xA5, 0x32, 0x4A, 0x5E, 0x53, 0xCA, + 0x64, 0x92, 0x52, 0xF2, 0x92, 0x49, 0x24, 0xA7, 0xFF, 0xD2, 0xF4, 0x10, + 0x9F, 0x44, 0xC9, 0xD4, 0xCC, 0x0B, 0xA7, 0x05, 0x45, 0x3A, 0x4A, 0x66, + 0x35, 0x44, 0x6B, 0x50, 0x9A, 0x8A, 0xD2, 0x50, 0x29, 0x09, 0x00, 0x52, + 0x50, 0x05, 0x48, 0x14, 0x0A, 0x57, 0x48, 0x94, 0xD2, 0x92, 0x09, 0x51, + 0x51, 0x2A, 0x69, 0x8A, 0x4A, 0x46, 0xE5, 0x15, 0x22, 0xA2, 0x9C, 0x85, + 0x04, 0xE5, 0x32, 0x49, 0x29, 0x68, 0x4C, 0x53, 0xA8, 0x94, 0x94, 0xC4, + 0xA4, 0x92, 0x74, 0x50, 0xC6, 0x54, 0x5C, 0x25, 0x48, 0x84, 0xC4, 0xA4, + 0xA4, 0x2E, 0x69, 0x43, 0x76, 0x88, 0xEE, 0xE1, 0x09, 0xC1, 0x10, 0xB4, + 0xA1, 0x79, 0x41, 0x71, 0x56, 0x0B, 0x25, 0x09, 0xD5, 0xA7, 0x05, 0xA5, + 0x0C, 0x95, 0x30, 0xA5, 0xE9, 0x78, 0x27, 0xD8, 0x95, 0xA2, 0x91, 0x38, + 0x1E, 0xC8, 0x7A, 0xCA, 0x3B, 0x99, 0xE0, 0xA1, 0xB0, 0xA2, 0x82, 0x18, + 0x82, 0xA4, 0x13, 0xB6, 0xB5, 0x31, 0x5A, 0x49, 0xA6, 0x20, 0x15, 0x2D, + 0xA5, 0x48, 0x30, 0x85, 0x30, 0xD0, 0x82, 0x69, 0x16, 0xC4, 0xE1, 0xA8, + 0xBB, 0x53, 0x6D, 0x4A, 0xD5, 0x4C, 0x43, 0x41, 0x52, 0x0D, 0x85, 0x26, + 0x85, 0x20, 0x02, 0x16, 0x96, 0x30, 0x9C, 0x00, 0xA4, 0x40, 0x4C, 0x52, + 0x52, 0xE2, 0x13, 0x14, 0xA5, 0x31, 0x28, 0x29, 0x44, 0xA8, 0x92, 0x98, + 0xB9, 0x34, 0x94, 0x54, 0xB8, 0x52, 0xF8, 0x28, 0xEA, 0x53, 0x8D, 0x12, + 0x53, 0x24, 0xF2, 0x98, 0x14, 0x81, 0x49, 0x4B, 0x82, 0xA4, 0xA2, 0x9C, + 0x20, 0x96, 0x40, 0xA9, 0x4A, 0x8A, 0x49, 0x29, 0x98, 0x52, 0x10, 0xA0, + 0x14, 0x81, 0x41, 0x2C, 0xE0, 0x28, 0x94, 0xFA, 0x26, 0x49, 0x4B, 0x24, + 0x9D, 0x24, 0x94, 0xB2, 0x65, 0x28, 0x4C, 0x8A, 0x14, 0x92, 0x78, 0x4D, + 0x09, 0x29, 0x49, 0x25, 0xA2, 0x49, 0x29, 0xFF, 0xD3, 0xF4, 0x09, 0x48, + 0x14, 0xC9, 0xC0, 0x53, 0xB5, 0xD7, 0x95, 0x21, 0xAA, 0x88, 0x53, 0x01, + 0x04, 0xB2, 0x01, 0x4C, 0x4A, 0x88, 0x85, 0x36, 0xA0, 0x96, 0x40, 0x14, + 0x82, 0x79, 0x4E, 0x82, 0x54, 0x13, 0xA6, 0x09, 0xD0, 0x4A, 0x93, 0x14, + 0xE9, 0x92, 0x53, 0x17, 0x28, 0xA9, 0x9D, 0x53, 0x42, 0x28, 0x63, 0x09, + 0x29, 0x42, 0x62, 0x8A, 0x98, 0x15, 0x12, 0xA4, 0x54, 0x0A, 0x48, 0x50, + 0x48, 0xA6, 0x9D, 0x14, 0x0B, 0x91, 0x53, 0x22, 0x54, 0x09, 0x48, 0x94, + 0xD2, 0x92, 0x16, 0x82, 0x98, 0xB4, 0x29, 0x4A, 0x8B, 0x9C, 0x11, 0x53, + 0x12, 0x04, 0x21, 0x18, 0x95, 0x37, 0xB8, 0x42, 0x14, 0xF7, 0x44, 0x2D, + 0x2C, 0xE0, 0x28, 0x9D, 0x52, 0x12, 0x54, 0xDA, 0xD4, 0x94, 0xC0, 0x34, + 0xA7, 0x35, 0xA2, 0x86, 0x29, 0x86, 0x4A, 0x16, 0x9A, 0x6B, 0x0A, 0xE1, + 0x4D, 0xAD, 0x47, 0x35, 0xC2, 0x6D, 0xA9, 0x5A, 0xA9, 0x1F, 0xA6, 0x0A, + 0x6D, 0x84, 0x22, 0xBB, 0x45, 0x02, 0xE0, 0x92, 0x98, 0xC2, 0x7D, 0xA9, + 0xE5, 0x38, 0x84, 0x94, 0xC2, 0x0A, 0x92, 0x4E, 0x50, 0x2E, 0x80, 0x92, + 0x19, 0x39, 0xC0, 0x04, 0x32, 0xE5, 0x17, 0x3D, 0x36, 0xE4, 0x69, 0x56, + 0xCE, 0x53, 0x12, 0xA3, 0xB9, 0x44, 0x90, 0x8A, 0x2D, 0x72, 0x52, 0x92, + 0x9A, 0x25, 0x38, 0x09, 0x29, 0x70, 0xE2, 0x13, 0x87, 0x25, 0xB4, 0x26, + 0x49, 0x4C, 0xC1, 0x52, 0x0A, 0x0D, 0x53, 0x08, 0x25, 0x9B, 0x42, 0x9E, + 0xD5, 0x06, 0x14, 0x50, 0xE0, 0x82, 0x43, 0x12, 0xC4, 0xB6, 0xA2, 0x03, + 0x29, 0x42, 0x09, 0xA6, 0x00, 0x27, 0x88, 0x52, 0xD1, 0x32, 0x4A, 0x5C, + 0x29, 0x6D, 0x51, 0x05, 0x4C, 0x1D, 0x12, 0x53, 0x12, 0x13, 0x15, 0x22, + 0x52, 0x84, 0x94, 0xC1, 0x3E, 0xD4, 0xF0, 0xA4, 0x21, 0x25, 0x31, 0x84, + 0xC4, 0x22, 0x40, 0x50, 0x29, 0x29, 0x8F, 0x74, 0x92, 0xEE, 0x92, 0x28, + 0x7F, 0xFF, 0xD4, 0xF4, 0x18, 0x94, 0xFB, 0x54, 0x83, 0x52, 0x2A, 0x66, + 0x0A, 0x58, 0x05, 0x24, 0xC1, 0x48, 0x24, 0xA5, 0xC2, 0x94, 0xA8, 0xA9, + 0x35, 0x04, 0xAE, 0x25, 0x48, 0x25, 0x01, 0x24, 0x92, 0xCA, 0x53, 0x85, + 0x1D, 0x14, 0x82, 0x05, 0x4B, 0xA8, 0x95, 0x24, 0xD0, 0x82, 0x58, 0xA6, + 0x52, 0x21, 0x32, 0x28, 0x59, 0x45, 0xC5, 0x39, 0x2A, 0x04, 0xA2, 0xA6, + 0x2E, 0x2A, 0x04, 0xA9, 0x15, 0x02, 0x8A, 0x16, 0x95, 0x12, 0x53, 0x98, + 0x51, 0x25, 0x24, 0x30, 0x25, 0x2D, 0xC9, 0x9C, 0x54, 0x51, 0x43, 0x30, + 0xF2, 0xA2, 0xE7, 0xA6, 0xEC, 0x98, 0xA2, 0x86, 0x26, 0x4A, 0x40, 0x15, + 0x20, 0x97, 0x09, 0x29, 0x70, 0x11, 0x58, 0xD5, 0x06, 0x19, 0x46, 0x10, + 0x81, 0x48, 0x5C, 0x31, 0x48, 0x02, 0x12, 0x0E, 0x4E, 0x5C, 0x10, 0x5C, + 0xB1, 0x1E, 0x29, 0x88, 0x09, 0x17, 0x28, 0x12, 0x52, 0x43, 0x17, 0x84, + 0x2E, 0xE8, 0x84, 0xA8, 0x10, 0x88, 0x41, 0x5B, 0x72, 0x7D, 0xC5, 0x46, + 0x14, 0xA0, 0x22, 0x86, 0x25, 0xCA, 0x2E, 0x70, 0x53, 0x20, 0x21, 0x3C, + 0x88, 0x49, 0x05, 0x13, 0xDC, 0x86, 0x5E, 0xE5, 0x23, 0x2A, 0x24, 0x27, + 0x2D, 0x56, 0xF4, 0xFB, 0xE4, 0xA8, 0x10, 0x53, 0xB4, 0x19, 0x49, 0x49, + 0xDA, 0x4C, 0x29, 0x02, 0xA0, 0xD4, 0x46, 0xE8, 0x82, 0xE0, 0xAD, 0xC7, + 0x84, 0xE0, 0x27, 0x89, 0xE1, 0x38, 0x49, 0x4A, 0x01, 0x38, 0x49, 0x21, + 0x28, 0x25, 0x90, 0x4E, 0x0A, 0x68, 0x29, 0xC0, 0x49, 0x49, 0x1A, 0x4A, + 0x91, 0x25, 0x45, 0xAA, 0x52, 0x82, 0x56, 0xDC, 0x9E, 0x54, 0x53, 0x80, + 0x92, 0x97, 0x95, 0x20, 0x54, 0x42, 0x98, 0x84, 0x12, 0xB8, 0x52, 0x01, + 0x33, 0x54, 0xF4, 0x84, 0x92, 0xC2, 0x0A, 0x68, 0x2A, 0x64, 0xA6, 0x84, + 0x94, 0xC4, 0xCA, 0x89, 0x95, 0x32, 0x13, 0x6D, 0x49, 0x0C, 0x35, 0x49, + 0x4B, 0x6A, 0x49, 0x2A, 0x9F, 0xFF, 0xD5, 0xF4, 0x60, 0x12, 0xDA, 0x12, + 0xDC, 0x9B, 0x72, 0x99, 0x85, 0x47, 0x44, 0xA5, 0x31, 0x29, 0x82, 0x48, + 0x66, 0x35, 0x44, 0x6A, 0x1B, 0x54, 0xC2, 0x09, 0x67, 0x29, 0x4A, 0x8A, + 0x52, 0x92, 0x59, 0x05, 0x20, 0x54, 0x42, 0x52, 0x82, 0x99, 0xCA, 0x69, + 0x51, 0x25, 0x29, 0x4A, 0x94, 0xC9, 0x45, 0xC9, 0xE5, 0x33, 0x92, 0x53, + 0x02, 0x54, 0x0A, 0x91, 0x4C, 0x8A, 0x18, 0x94, 0x37, 0x29, 0x95, 0x12, + 0x35, 0x45, 0x08, 0xDD, 0xC2, 0x19, 0x25, 0x19, 0xCD, 0x42, 0x70, 0x45, + 0x05, 0x84, 0xA5, 0xC2, 0x63, 0x29, 0x6B, 0x08, 0xA1, 0x44, 0x84, 0xDC, + 0xEA, 0x9A, 0x13, 0xA4, 0xA5, 0xB5, 0x09, 0xF5, 0x29, 0x19, 0x52, 0x63, + 0x67, 0x54, 0x94, 0xCA, 0xB6, 0x9E, 0x51, 0x44, 0x26, 0x1A, 0x04, 0xDB, + 0xA5, 0x04, 0xA4, 0x1C, 0x68, 0x9E, 0x09, 0x0A, 0x2D, 0x30, 0x96, 0xE2, + 0x82, 0x54, 0x41, 0x50, 0x32, 0xA5, 0xBB, 0x54, 0xFB, 0x41, 0x49, 0x48, + 0x88, 0x3D, 0x93, 0x06, 0xF8, 0xA2, 0x3B, 0x40, 0xA0, 0x4A, 0x28, 0x5F, + 0x68, 0x4C, 0x40, 0x0A, 0x32, 0x91, 0x72, 0x4A, 0x62, 0xE5, 0x02, 0x25, + 0x49, 0xCA, 0x28, 0xAD, 0x28, 0xDC, 0x02, 0x8C, 0x22, 0x91, 0x29, 0xBD, + 0x39, 0x45, 0x14, 0x8F, 0x64, 0xA4, 0x1A, 0x8C, 0x18, 0x91, 0x6A, 0x56, + 0xAA, 0x60, 0x1A, 0xA6, 0x02, 0x70, 0x13, 0x80, 0x92, 0x54, 0x04, 0x25, + 0x09, 0xCA, 0x64, 0x12, 0xC9, 0xA1, 0x11, 0xA0, 0x28, 0x37, 0x44, 0xF2, + 0x52, 0x53, 0x38, 0x0A, 0x41, 0xA8, 0x61, 0x11, 0xA5, 0x04, 0xAF, 0xB5, + 0x2D, 0xAA, 0x4D, 0x0A, 0x44, 0x20, 0x96, 0x00, 0x25, 0x0A, 0x6A, 0x25, + 0x25, 0x31, 0x21, 0x38, 0x4E, 0x92, 0x4A, 0x64, 0xD5, 0x30, 0x86, 0x0A, + 0x98, 0x29, 0x29, 0x74, 0x93, 0x12, 0x9B, 0x72, 0x49, 0x64, 0x99, 0x2D, + 0xC9, 0x89, 0x49, 0x4A, 0x84, 0x92, 0x94, 0x92, 0x43, 0xFF, 0xD6, 0xF4, + 0x1D, 0xC9, 0xE5, 0x44, 0xC2, 0x75, 0x3B, 0x5D, 0x79, 0x52, 0x01, 0x40, + 0x29, 0x04, 0x12, 0x90, 0x29, 0x05, 0x16, 0xA7, 0xD5, 0x04, 0xAF, 0x29, + 0x4A, 0x8A, 0x52, 0x92, 0x99, 0x02, 0x9E, 0x54, 0x41, 0x49, 0x25, 0x32, + 0x4A, 0x54, 0x65, 0x22, 0x52, 0x53, 0x3D, 0xCA, 0x24, 0xA8, 0x92, 0x9B, + 0x72, 0x54, 0xAB, 0x5E, 0x53, 0x26, 0x94, 0xE8, 0xA9, 0x44, 0x26, 0xDA, + 0x9E, 0x52, 0x25, 0x25, 0x31, 0x23, 0x44, 0x37, 0x34, 0x22, 0xF6, 0x42, + 0x78, 0x84, 0x90, 0x50, 0xB9, 0xA9, 0x80, 0x21, 0x12, 0x67, 0xB2, 0x73, + 0xF4, 0x74, 0x46, 0xD1, 0x48, 0x5D, 0xA7, 0x29, 0xB9, 0x0A, 0x4E, 0x32, + 0x91, 0x6E, 0x82, 0x11, 0x42, 0xC0, 0x4F, 0x6E, 0x11, 0x18, 0x21, 0x30, + 0x02, 0x25, 0x4C, 0x44, 0x20, 0x95, 0x41, 0x51, 0x1F, 0x49, 0x14, 0x44, + 0x6A, 0x91, 0x0D, 0x41, 0x34, 0xB0, 0x88, 0xD1, 0x47, 0x50, 0x53, 0x81, + 0x1C, 0x27, 0x29, 0x29, 0x62, 0x53, 0x6F, 0x01, 0x28, 0x51, 0x73, 0x80, + 0x45, 0x0A, 0x7B, 0xA5, 0x57, 0x71, 0x32, 0xA6, 0x4A, 0x83, 0xA0, 0xA2, + 0x10, 0x58, 0x7A, 0x84, 0x26, 0x0F, 0x32, 0x99, 0xDC, 0xA6, 0x82, 0x78, + 0x45, 0x6D, 0xA4, 0xF5, 0x12, 0xDE, 0x10, 0xA0, 0x85, 0x20, 0x95, 0x2A, + 0xD2, 0xB4, 0xCA, 0x20, 0x08, 0x0C, 0x3A, 0xA3, 0x02, 0x91, 0x48, 0x65, + 0x09, 0x88, 0x53, 0x6C, 0x14, 0xE5, 0x92, 0x82, 0x50, 0x93, 0xAA, 0x70, + 0x14, 0x8D, 0x71, 0xAA, 0x5A, 0x24, 0xA5, 0x94, 0x60, 0xA2, 0x42, 0x68, + 0x49, 0x4B, 0x34, 0x15, 0x28, 0x48, 0x0D, 0x54, 0xB4, 0x49, 0x4B, 0x34, + 0x22, 0x00, 0xA2, 0x21, 0x48, 0x10, 0x82, 0x43, 0x36, 0xA9, 0x97, 0x04, + 0x30, 0x42, 0x79, 0x94, 0x12, 0xBC, 0xA8, 0x92, 0x9E, 0x53, 0x14, 0x94, + 0xC7, 0x74, 0x24, 0x5E, 0x53, 0x39, 0x42, 0x75, 0x45, 0x09, 0x1A, 0xE5, + 0x20, 0xF4, 0x12, 0xE4, 0xB7, 0x25, 0x4A, 0xB4, 0xDB, 0xA5, 0x29, 0x43, + 0x0E, 0x4F, 0xBD, 0x2A, 0x55, 0xA4, 0x94, 0xA5, 0x0F, 0x78, 0x4B, 0x70, + 0x49, 0x56, 0x93, 0x71, 0x49, 0x43, 0x70, 0x49, 0x25, 0x3F, 0xFF, 0xD7, + 0xEF, 0x9A, 0x88, 0x10, 0x5A, 0x51, 0x01, 0x53, 0x96, 0xB8, 0x66, 0x21, + 0x38, 0x4C, 0x9C, 0x20, 0x96, 0x63, 0x84, 0xE0, 0xA1, 0xEE, 0x52, 0x05, + 0x24, 0xA8, 0xA4, 0x98, 0x94, 0x92, 0x43, 0x20, 0xA4, 0x02, 0x66, 0xA9, + 0x84, 0x12, 0xC6, 0x14, 0x5C, 0x88, 0x50, 0xDC, 0x92, 0x98, 0x95, 0x14, + 0x89, 0x4D, 0x28, 0xA1, 0x94, 0xA5, 0x2A, 0x32, 0x94, 0xA4, 0xA6, 0x52, + 0x94, 0xA8, 0xA4, 0x4A, 0x4A, 0x67, 0x28, 0x4F, 0xE7, 0x54, 0x8B, 0xA1, + 0x45, 0xCE, 0x25, 0x25, 0x5A, 0xA5, 0x24, 0xC1, 0x2E, 0x51, 0x42, 0xC1, + 0xB3, 0xAA, 0x90, 0xAE, 0x75, 0x4E, 0x01, 0x52, 0x69, 0x09, 0x29, 0x89, + 0x61, 0xE1, 0x37, 0x05, 0x49, 0xC5, 0x30, 0x49, 0x2B, 0x13, 0x01, 0x29, + 0x48, 0xB4, 0x95, 0x12, 0x08, 0x49, 0x0C, 0xF7, 0x42, 0x89, 0x77, 0x82, + 0x60, 0x09, 0x4E, 0x6B, 0x29, 0x29, 0x8E, 0xF3, 0x08, 0x66, 0x49, 0xD5, + 0x4C, 0x88, 0xD1, 0x3E, 0xD0, 0x42, 0x28, 0x44, 0x65, 0x41, 0xC0, 0xA3, + 0x16, 0xE8, 0xA0, 0x44, 0xE8, 0x92, 0x0B, 0x5E, 0x25, 0x4C, 0x08, 0x4E, + 0x5A, 0x25, 0x2E, 0xE8, 0xA1, 0x50, 0x98, 0x80, 0xA7, 0x1A, 0x26, 0x21, + 0x25, 0x31, 0x68, 0x84, 0x46, 0x92, 0x86, 0x7C, 0x94, 0xDB, 0x21, 0x24, + 0x84, 0xC0, 0xE8, 0xA6, 0x0A, 0x1B, 0x22, 0x75, 0x53, 0x25, 0x04, 0xA8, + 0x92, 0x74, 0x51, 0x80, 0x91, 0x29, 0x83, 0x92, 0x53, 0x20, 0x14, 0xA0, + 0x42, 0x8C, 0xA5, 0x29, 0x29, 0x62, 0x96, 0xE0, 0x13, 0x39, 0xC8, 0x72, + 0x51, 0x45, 0xA5, 0x2E, 0x4D, 0xBD, 0x0C, 0x98, 0x4D, 0x32, 0x95, 0x2A, + 0xD2, 0x7A, 0xA5, 0x48, 0x5A, 0x84, 0x9C, 0x25, 0x4A, 0xB4, 0x9E, 0xA9, + 0x4B, 0xD5, 0x50, 0x48, 0xA5, 0x4A, 0xB6, 0x46, 0xC4, 0xDB, 0x94, 0x0A, + 0x64, 0xA9, 0x56, 0xCC, 0x94, 0xD2, 0x53, 0x4A, 0x52, 0x92, 0x99, 0x6F, + 0x29, 0xB7, 0x15, 0x19, 0x4D, 0x32, 0x92, 0x2D, 0x9E, 0xE2, 0xA4, 0x1C, + 0x54, 0x02, 0x90, 0x49, 0x36, 0xCE, 0x74, 0x49, 0x46, 0x74, 0x49, 0x04, + 0xBF, 0xFF, 0xD0, 0xEE, 0xDA, 0x88, 0x10, 0xC1, 0x53, 0x05, 0x58, 0x6B, + 0x33, 0x05, 0x3C, 0xA8, 0x82, 0x94, 0xA0, 0x96, 0x52, 0x9C, 0x15, 0x09, + 0x4A, 0x52, 0x55, 0xB3, 0x94, 0xE0, 0xA1, 0xCA, 0x93, 0x42, 0x4A, 0x4C, + 0xD5, 0x30, 0x14, 0x18, 0x54, 0xC1, 0x4D, 0x2B, 0x94, 0x42, 0x83, 0x82, + 0x20, 0x51, 0x74, 0x24, 0xA4, 0x04, 0x28, 0x95, 0x27, 0x94, 0x2D, 0xC9, + 0xCB, 0x4B, 0x29, 0x48, 0x15, 0x09, 0x4B, 0x74, 0x24, 0xA4, 0xB2, 0xA0, + 0xE2, 0xA3, 0xBE, 0x52, 0x71, 0x94, 0xA9, 0x56, 0xB1, 0x71, 0x51, 0x94, + 0xD0, 0x65, 0x39, 0x28, 0xA1, 0x98, 0x3A, 0x29, 0x35, 0xB2, 0x14, 0x1A, + 0x8A, 0xDD, 0x10, 0x29, 0x0C, 0x83, 0x40, 0x0A, 0x24, 0x29, 0x82, 0x3B, + 0xA8, 0xBB, 0x94, 0x12, 0xB4, 0x28, 0xE8, 0x91, 0x72, 0x89, 0xE5, 0x14, + 0x32, 0x04, 0x05, 0x07, 0x9D, 0x52, 0x71, 0x51, 0x99, 0x4A, 0x91, 0x6C, + 0xDA, 0xEF, 0x15, 0x22, 0xFD, 0x10, 0x8A, 0x89, 0x72, 0x34, 0xAB, 0x66, + 0xE2, 0x0A, 0x40, 0x00, 0x84, 0x5C, 0x65, 0x3B, 0x49, 0x4A, 0x95, 0x69, + 0x49, 0x04, 0x28, 0x6D, 0x4E, 0x13, 0xED, 0x29, 0x29, 0x0B, 0x80, 0xEC, + 0x9B, 0x6A, 0x9B, 0x80, 0x09, 0xA5, 0x14, 0x2C, 0x98, 0xF9, 0x29, 0x25, + 0x09, 0x21, 0x18, 0x06, 0x54, 0xC0, 0x4E, 0x02, 0x53, 0x09, 0x29, 0x93, + 0x74, 0x53, 0x90, 0x50, 0x83, 0x94, 0x81, 0x42, 0x93, 0x6B, 0xB9, 0x46, + 0x40, 0x49, 0xDA, 0xA8, 0xC2, 0x2A, 0x48, 0x1C, 0x0A, 0x45, 0x0D, 0x3C, + 0x94, 0x95, 0x6A, 0x72, 0x8C, 0xA7, 0x95, 0x12, 0x92, 0x16, 0x71, 0x4C, + 0x0A, 0x63, 0xCA, 0x78, 0x45, 0x0C, 0x81, 0x52, 0x0A, 0x01, 0x48, 0x24, + 0x96, 0x52, 0x91, 0x4C, 0x9C, 0x20, 0xA5, 0xB5, 0x51, 0x28, 0x88, 0x6F, + 0xE5, 0x25, 0x2C, 0x0A, 0x97, 0x65, 0x0E, 0x13, 0xCA, 0x2A, 0x5D, 0x20, + 0x9A, 0x52, 0x94, 0x94, 0xC8, 0x15, 0x20, 0x50, 0xE4, 0xCA, 0x9B, 0x4A, + 0x4A, 0x66, 0x92, 0x7E, 0xC9, 0x20, 0x97, 0xFF, 0xD1, 0xEE, 0x03, 0x94, + 0x83, 0x90, 0x03, 0x94, 0xC3, 0x95, 0x9A, 0x6A, 0x02, 0x9C, 0x15, 0x20, + 0x84, 0xD2, 0xA6, 0x1C, 0x82, 0xE6, 0x49, 0xA5, 0x2E, 0x52, 0x84, 0x94, + 0xC9, 0xA5, 0x11, 0xA5, 0x09, 0xA1, 0x15, 0x81, 0x02, 0x90, 0xC8, 0x15, + 0x30, 0x54, 0x21, 0x3E, 0xA1, 0x05, 0xCC, 0xCB, 0xA0, 0x28, 0x39, 0xC9, + 0x6A, 0x98, 0x8D, 0x12, 0x52, 0x27, 0x21, 0x98, 0x45, 0x70, 0x95, 0x02, + 0xCD, 0x51, 0x5A, 0x58, 0xC4, 0x85, 0x12, 0xA6, 0x44, 0x21, 0xB9, 0x10, + 0x85, 0x82, 0x9B, 0x44, 0xA1, 0xB6, 0x65, 0x10, 0x24, 0xA5, 0xCB, 0x61, + 0x46, 0x24, 0xA9, 0x1E, 0x13, 0x00, 0x92, 0x97, 0x68, 0xF1, 0x46, 0x6C, + 0x14, 0x36, 0x89, 0x53, 0x80, 0x10, 0x5C, 0x19, 0x18, 0x0A, 0x24, 0x4F, + 0x09, 0x89, 0x4C, 0x5E, 0x21, 0x05, 0x5A, 0xCE, 0x30, 0xA0, 0x5C, 0x53, + 0xF2, 0xA2, 0x9C, 0xB5, 0x6F, 0x8A, 0x8F, 0x74, 0x43, 0x0A, 0x1C, 0xA4, + 0xA5, 0xFB, 0x28, 0x16, 0xA2, 0x80, 0x13, 0x16, 0xCA, 0x4A, 0x46, 0x1A, + 0x49, 0x45, 0x15, 0x24, 0xD6, 0xC2, 0x33, 0x48, 0x29, 0x12, 0xA0, 0x18, + 0x8A, 0xCA, 0x45, 0x88, 0xA0, 0x84, 0xC5, 0x0B, 0x5D, 0x4D, 0x67, 0xB5, + 0x08, 0x85, 0x65, 0xE2, 0x4A, 0x13, 0x9B, 0x08, 0x82, 0xB4, 0x84, 0x60, + 0x27, 0x00, 0xA4, 0xA4, 0x11, 0x43, 0x12, 0x21, 0x44, 0xA2, 0x6D, 0x94, + 0xC5, 0xA9, 0x29, 0x80, 0x10, 0xA4, 0x13, 0x42, 0x49, 0x21, 0x79, 0x09, + 0x93, 0x6A, 0xA4, 0xD1, 0xE2, 0x92, 0x56, 0xDA, 0x98, 0x84, 0x4D, 0x14, + 0x1D, 0x29, 0x28, 0xAD, 0x09, 0x88, 0x09, 0xF5, 0x4C, 0xED, 0x02, 0x48, + 0x62, 0x40, 0x4A, 0x14, 0x75, 0x52, 0x01, 0x14, 0x2B, 0xB2, 0x70, 0x53, + 0x14, 0xD2, 0x92, 0x52, 0x4A, 0x90, 0x84, 0x20, 0x51, 0x1A, 0x82, 0x82, + 0xEE, 0x43, 0x71, 0x52, 0x79, 0x43, 0x73, 0xD2, 0x0A, 0x2B, 0xCC, 0xA6, + 0x85, 0x02, 0xEF, 0x05, 0x20, 0xF0, 0x8A, 0x14, 0x4C, 0x26, 0xDC, 0x99, + 0xC5, 0x46, 0x51, 0xA5, 0x5A, 0x50, 0x42, 0x93, 0x4A, 0x0B, 0x4A, 0x90, + 0x30, 0x85, 0x2A, 0xD3, 0xEE, 0xD1, 0x24, 0x3D, 0xC1, 0x24, 0xA9, 0x36, + 0xFF, 0x00, 0xFF, 0xD2, 0xEB, 0xC1, 0x45, 0x08, 0x2D, 0x44, 0x0A, 0xD9, + 0x69, 0x84, 0x8D, 0x72, 0x98, 0x28, 0x60, 0x29, 0x84, 0xD4, 0x84, 0x8D, + 0x72, 0x20, 0x84, 0x10, 0x11, 0x18, 0x25, 0x02, 0xB8, 0x24, 0x01, 0x4C, + 0x04, 0x9A, 0xD4, 0xE4, 0x26, 0xAE, 0x0B, 0xA7, 0x27, 0x44, 0xC1, 0x22, + 0x42, 0x49, 0x50, 0x72, 0x62, 0x54, 0x49, 0x51, 0x94, 0x91, 0x6B, 0xA6, + 0x2E, 0x09, 0x89, 0x1E, 0x2A, 0x2E, 0xF1, 0x45, 0x16, 0xC5, 0xCE, 0x01, + 0x08, 0x9D, 0x53, 0xB8, 0xCA, 0x84, 0x90, 0x9C, 0x02, 0xD2, 0x52, 0x35, + 0x4F, 0x44, 0x30, 0x54, 0xA6, 0x50, 0x4B, 0x25, 0x28, 0x0A, 0x21, 0x3C, + 0xA4, 0xA6, 0x61, 0x31, 0x25, 0x46, 0x61, 0x29, 0x41, 0x36, 0xB9, 0x2A, + 0x30, 0x94, 0xA4, 0x25, 0x14, 0x28, 0x02, 0x91, 0x01, 0x3A, 0x62, 0x0A, + 0x4A, 0x59, 0x38, 0x6A, 0x60, 0x35, 0x44, 0x68, 0x09, 0x29, 0x6D, 0xA9, + 0x6D, 0x53, 0x51, 0x28, 0x25, 0x41, 0xA9, 0xD2, 0x09, 0xD2, 0x52, 0xC4, + 0x94, 0x83, 0x8A, 0x45, 0x45, 0x25, 0x32, 0x9D, 0x54, 0x1F, 0xC2, 0x74, + 0x8A, 0x48, 0x43, 0x09, 0xE1, 0x48, 0xC4, 0xA5, 0xCA, 0x2A, 0x58, 0x6A, + 0x91, 0x09, 0xE2, 0x13, 0x3C, 0xA4, 0xA6, 0x05, 0x46, 0x42, 0x91, 0x43, + 0x74, 0xA2, 0xB4, 0xAE, 0x4A, 0x76, 0xA8, 0x29, 0xB5, 0x25, 0x32, 0x94, + 0xC5, 0x3C, 0x26, 0x76, 0x89, 0x25, 0x64, 0xC6, 0x0A, 0x69, 0x4C, 0x8A, + 0x15, 0x09, 0xC0, 0x4A, 0x12, 0x49, 0x4B, 0x11, 0xD9, 0x41, 0xC2, 0x11, + 0x44, 0x26, 0x70, 0x05, 0x25, 0x22, 0x69, 0xD7, 0x55, 0x2D, 0xEA, 0x2E, + 0xE5, 0x40, 0xB9, 0x1A, 0x45, 0xB3, 0x2F, 0x25, 0x44, 0x95, 0x02, 0xE5, + 0x12, 0xE2, 0x95, 0x22, 0xD9, 0x94, 0x83, 0x90, 0xCB, 0xBB, 0x26, 0xDC, + 0x8D, 0x22, 0xD9, 0xB9, 0xDA, 0xE8, 0x90, 0x72, 0x82, 0x53, 0x08, 0xA2, + 0xD2, 0x87, 0x27, 0x94, 0x30, 0x54, 0x82, 0x09, 0xB4, 0x92, 0x92, 0x82, + 0x49, 0x2A, 0xDF, 0xFF, 0xD3, 0xEB, 0x9A, 0x11, 0x58, 0x14, 0x1B, 0x08, + 0xAD, 0x56, 0x8B, 0x4C, 0x32, 0x01, 0x4B, 0xB2, 0x68, 0x53, 0x6B, 0x74, + 0xD5, 0x05, 0xC1, 0x6D, 0x54, 0xD8, 0x60, 0xA8, 0xB8, 0x24, 0x01, 0x41, + 0x29, 0xDB, 0x62, 0x9E, 0xF4, 0x01, 0x3D, 0xD4, 0xC6, 0xA8, 0x52, 0x6D, + 0x26, 0xE0, 0x94, 0x94, 0x30, 0x42, 0x96, 0xE0, 0x38, 0x49, 0x36, 0xB2, + 0x62, 0x0A, 0x5B, 0xF5, 0x48, 0xBA, 0x42, 0x48, 0x60, 0x65, 0x44, 0xEE, + 0x53, 0x95, 0x12, 0x51, 0x42, 0x32, 0x13, 0x10, 0x93, 0x8A, 0x62, 0x74, + 0x45, 0x0A, 0x94, 0xA6, 0x14, 0x4B, 0x92, 0xDC, 0x92, 0x2D, 0x28, 0x72, + 0x79, 0x43, 0x69, 0x52, 0x05, 0x2A, 0x4D, 0xA4, 0x02, 0x53, 0xC2, 0x83, + 0x5C, 0xA7, 0xB9, 0x04, 0xAF, 0x01, 0x3C, 0x28, 0x87, 0x29, 0xC8, 0x84, + 0x14, 0xB7, 0x29, 0x14, 0xF2, 0xA2, 0x4A, 0x49, 0x5E, 0x13, 0xB5, 0x46, + 0x53, 0x82, 0x92, 0x19, 0x15, 0x12, 0xA5, 0xCA, 0x68, 0x49, 0x2A, 0x49, + 0x3A, 0x74, 0x94, 0xC0, 0xA6, 0x52, 0x72, 0x81, 0x45, 0x0B, 0x17, 0x28, + 0x97, 0x26, 0x71, 0x51, 0x25, 0x2A, 0x45, 0xB2, 0x05, 0x38, 0x50, 0x68, + 0x25, 0x48, 0x22, 0xA6, 0x52, 0x14, 0x0A, 0x91, 0x0A, 0x25, 0x25, 0x31, + 0x25, 0x47, 0x95, 0x3D, 0x23, 0x54, 0xD0, 0x3B, 0x24, 0x86, 0x30, 0xA6, + 0xC6, 0xA4, 0x14, 0x80, 0x29, 0x24, 0x32, 0xDA, 0x21, 0x41, 0xCD, 0x95, + 0x36, 0x92, 0x98, 0xF2, 0x82, 0x5A, 0xE5, 0xA4, 0x27, 0x1A, 0x1D, 0x51, + 0x8B, 0x74, 0x43, 0x2D, 0xD5, 0x1B, 0x5B, 0x4A, 0xD3, 0x94, 0xDA, 0x29, + 0x46, 0xD0, 0xA1, 0x29, 0x29, 0x72, 0x44, 0x28, 0x13, 0x09, 0x38, 0xA1, + 0x39, 0xC6, 0x51, 0x01, 0x04, 0xA9, 0xE4, 0x21, 0x94, 0x89, 0x51, 0x94, + 0xE5, 0xA4, 0xA8, 0xA8, 0x97, 0x29, 0x1E, 0x10, 0x89, 0x44, 0x21, 0x79, + 0x94, 0xE8, 0x61, 0xCA, 0x5B, 0x91, 0xA4, 0x32, 0x05, 0x29, 0x4C, 0x0A, + 0x48, 0x29, 0x9B, 0x51, 0x02, 0x1B, 0x25, 0x15, 0x02, 0x90, 0xAE, 0xC9, + 0x25, 0x26, 0x12, 0x49, 0x2F, 0xFF, 0xD4, 0xEC, 0x5B, 0x0A, 0x6C, 0xE5, + 0x04, 0x13, 0xCA, 0x2B, 0x78, 0x56, 0x8B, 0x4D, 0x30, 0x2A, 0x60, 0xA1, + 0x84, 0xED, 0x3E, 0x28, 0x2E, 0x64, 0xE3, 0x29, 0x4A, 0x62, 0x3C, 0x14, + 0x4B, 0x88, 0x49, 0x49, 0x03, 0x93, 0xEE, 0x84, 0x1D, 0xDA, 0xA4, 0x49, + 0x4A, 0x95, 0x69, 0x0B, 0xF5, 0x52, 0x6B, 0x87, 0x28, 0x0E, 0x72, 0x61, + 0x62, 0x54, 0xAB, 0x6C, 0xC8, 0x2A, 0x4D, 0x21, 0x00, 0x3D, 0x3E, 0xF8, + 0x08, 0x52, 0x6D, 0x33, 0xA3, 0x94, 0x17, 0xB8, 0x04, 0xCE, 0xB4, 0xA1, + 0x3D, 0xC4, 0xA2, 0x02, 0x09, 0x51, 0x7A, 0x62, 0xF4, 0x3D, 0xCA, 0x24, + 0xCA, 0x75, 0x2C, 0xB4, 0xBB, 0xA5, 0x38, 0x50, 0x68, 0x2A, 0x44, 0x14, + 0x92, 0xC8, 0x15, 0x30, 0x50, 0x81, 0x52, 0x05, 0x05, 0x24, 0x94, 0xE0, + 0xA8, 0x02, 0x9C, 0x14, 0x13, 0x6C, 0xC1, 0x44, 0x69, 0xD1, 0x09, 0x3C, + 0xA5, 0x49, 0xB4, 0xA4, 0x85, 0x12, 0xA2, 0x0A, 0x72, 0x50, 0x4A, 0xA5, + 0x38, 0x2A, 0x12, 0x90, 0x72, 0x34, 0x8B, 0x4C, 0x1C, 0x9B, 0x72, 0x80, + 0x29, 0xD0, 0x4D, 0xB3, 0x0E, 0x52, 0x94, 0x36, 0x85, 0x20, 0x50, 0x52, + 0xE5, 0x40, 0xA2, 0x26, 0x84, 0x94, 0x88, 0xD7, 0x29, 0x7A, 0x68, 0xD2, + 0x13, 0xE8, 0x8D, 0xAA, 0x90, 0x06, 0x42, 0x5A, 0x22, 0x90, 0x86, 0xF6, + 0xF8, 0x24, 0xA6, 0x24, 0xA8, 0x98, 0x2A, 0x2E, 0x4C, 0x1C, 0x42, 0x2B, + 0x6D, 0x93, 0xB8, 0x43, 0x2E, 0x85, 0x27, 0x19, 0x51, 0x21, 0x15, 0x15, + 0x35, 0xC4, 0x95, 0x66, 0xBE, 0x15, 0x76, 0x84, 0x7A, 0xA4, 0x20, 0x52, + 0x19, 0x96, 0xA8, 0x96, 0xED, 0xE5, 0x1B, 0x70, 0x08, 0x64, 0x97, 0x26, + 0xAE, 0x63, 0x1B, 0x93, 0x6D, 0x85, 0x30, 0x08, 0x3A, 0xA1, 0xD8, 0xE2, + 0x25, 0x14, 0x23, 0xB3, 0x94, 0x27, 0x10, 0x02, 0x91, 0x71, 0x28, 0x6F, + 0x4E, 0x0B, 0x09, 0x46, 0xF7, 0x6A, 0xA0, 0x49, 0xE5, 0x4C, 0x85, 0x10, + 0x13, 0x96, 0xB1, 0x25, 0x44, 0x8E, 0xE8, 0xA5, 0xA0, 0x26, 0x20, 0x94, + 0x95, 0x48, 0xA4, 0xA8, 0x22, 0x3B, 0x42, 0x96, 0xDD, 0xC3, 0x44, 0x50, + 0x84, 0xB6, 0x38, 0x4B, 0x58, 0x46, 0xD8, 0x78, 0x4F, 0xB5, 0xBB, 0x7C, + 0xD2, 0xB5, 0x52, 0x1D, 0x54, 0xD8, 0x27, 0x44, 0xFE, 0x9A, 0x9B, 0x18, + 0x67, 0x54, 0x89, 0x55, 0x2E, 0x1A, 0x42, 0x94, 0x29, 0xE8, 0x98, 0x93, + 0x28, 0x5A, 0xEA, 0x5F, 0x6E, 0x89, 0x26, 0x97, 0x24, 0x82, 0x9F, 0xFF, + 0xD5, 0xEC, 0x83, 0x77, 0x71, 0xA2, 0x9B, 0x1B, 0xA2, 0x61, 0xCC, 0x84, + 0x66, 0x01, 0xB5, 0x5A, 0x2D, 0x40, 0x11, 0xEA, 0x0A, 0x70, 0x7C, 0x14, + 0xDC, 0x04, 0x4F, 0x74, 0x2D, 0xC3, 0x80, 0x82, 0x97, 0xDE, 0x5B, 0xCA, + 0x8B, 0x9C, 0x49, 0xF2, 0x4F, 0xA1, 0xE5, 0x41, 0xCE, 0x00, 0xA2, 0x86, + 0x6D, 0xD7, 0x95, 0x28, 0x50, 0x04, 0x15, 0x30, 0x44, 0x24, 0x96, 0x2F, + 0x28, 0x45, 0xD0, 0xA4, 0xF7, 0x4A, 0x0B, 0x89, 0x44, 0x2D, 0x25, 0x2B, + 0x6C, 0x4E, 0x6D, 0x0A, 0xB6, 0xE2, 0x13, 0x7A, 0x84, 0x23, 0x48, 0xE2, + 0x6C, 0x17, 0x26, 0xDE, 0xAB, 0x9B, 0x49, 0xE5, 0x36, 0xE4, 0xA9, 0x5C, + 0x49, 0xCB, 0x82, 0x8C, 0xEA, 0xA1, 0xB9, 0x38, 0x49, 0x16, 0x9D, 0x8E, + 0x08, 0x9B, 0x82, 0xAE, 0x14, 0xDB, 0x28, 0x10, 0xB8, 0x14, 0x9A, 0x14, + 0x81, 0x50, 0xD5, 0x3C, 0xA4, 0xA6, 0x60, 0xA7, 0x94, 0x39, 0x4E, 0x0A, + 0x49, 0x49, 0xB9, 0x20, 0x50, 0xE5, 0x48, 0x14, 0x14, 0x94, 0x25, 0xAA, + 0x80, 0x2A, 0x5B, 0x90, 0x4A, 0xC4, 0x14, 0xED, 0x6F, 0x9A, 0x53, 0x29, + 0xC2, 0x4A, 0x64, 0x02, 0x9C, 0x28, 0x29, 0x83, 0x28, 0x25, 0x74, 0xDB, + 0xA1, 0x2D, 0x53, 0x77, 0x49, 0x2C, 0xC3, 0x92, 0x2E, 0x51, 0xDD, 0xA2, + 0x89, 0x72, 0x54, 0xAB, 0x48, 0x35, 0x2A, 0x53, 0x08, 0x2D, 0x72, 0x20, + 0x74, 0xA4, 0xA5, 0xCE, 0xA1, 0x40, 0x95, 0x30, 0x54, 0x1F, 0x11, 0x28, + 0x05, 0x23, 0x70, 0xEE, 0x86, 0x42, 0x99, 0x2A, 0x33, 0x29, 0xCB, 0x4B, + 0x18, 0x29, 0x44, 0x15, 0x38, 0x4C, 0xE6, 0xA2, 0xA5, 0x08, 0x44, 0x6B, + 0xC0, 0x90, 0x85, 0x09, 0x4C, 0x21, 0x4A, 0x05, 0x36, 0xE0, 0x88, 0x1C, + 0x15, 0x5D, 0xC5, 0x4D, 0xA5, 0x22, 0x12, 0x0A, 0x77, 0x38, 0x76, 0x41, + 0xB2, 0x0F, 0x74, 0xC5, 0xE5, 0x0C, 0xBB, 0x54, 0x80, 0x51, 0x2B, 0x3B, + 0xCB, 0xEF, 0x41, 0x79, 0x32, 0x8A, 0x78, 0x41, 0x70, 0x32, 0x9C, 0x16, + 0x16, 0x32, 0x65, 0x48, 0xC9, 0x09, 0x35, 0xA8, 0x81, 0xBE, 0x48, 0xA8, + 0x23, 0x0D, 0x31, 0xAA, 0x5B, 0x62, 0x04, 0x23, 0x86, 0xA7, 0x2D, 0x1F, + 0x34, 0x2D, 0x34, 0xD6, 0x75, 0x40, 0xF6, 0x4E, 0xDA, 0xA0, 0xEA, 0x8A, + 0xEE, 0x53, 0xB4, 0x18, 0x94, 0xAD, 0x14, 0xC0, 0xD6, 0x99, 0xD5, 0x77, + 0x89, 0x47, 0x00, 0x42, 0x72, 0xD3, 0xC1, 0x28, 0x5A, 0x69, 0xAB, 0xB3, + 0x58, 0x4E, 0x18, 0x40, 0x56, 0x3D, 0x30, 0x9B, 0x66, 0x88, 0xDA, 0xA9, + 0x0E, 0x91, 0xE6, 0xA5, 0x09, 0xCB, 0x61, 0x3B, 0x48, 0x8D, 0x52, 0x53, + 0x18, 0xD2, 0x23, 0x54, 0x94, 0xF4, 0x49, 0x05, 0x3F, 0xFF, 0xD6, 0xED, + 0x41, 0x0A, 0x5B, 0xFC, 0x10, 0x37, 0x6B, 0xE6, 0x9C, 0x38, 0xC2, 0xB5, + 0x4D, 0x3B, 0x66, 0xF7, 0x94, 0x30, 0xED, 0x53, 0xB9, 0xC2, 0x10, 0xC1, + 0xD5, 0x10, 0x10, 0x4B, 0x32, 0x4A, 0x89, 0x24, 0xA7, 0x26, 0x54, 0x51, + 0x43, 0x36, 0xBA, 0x13, 0x97, 0x21, 0x02, 0x9E, 0x52, 0xA5, 0x5A, 0xEE, + 0x72, 0x19, 0x76, 0xA9, 0xC9, 0x43, 0x29, 0x20, 0x95, 0x12, 0xA2, 0x4A, + 0x92, 0x68, 0x45, 0x0C, 0x21, 0x48, 0x04, 0xF0, 0xA4, 0x20, 0x23, 0x6A, + 0x5C, 0x30, 0xA9, 0x06, 0xA4, 0x1C, 0x53, 0xEE, 0x09, 0xA9, 0x64, 0xD1, + 0xE2, 0xA6, 0x21, 0x08, 0xBD, 0x20, 0xE8, 0x49, 0x36, 0x9A, 0x25, 0x34, + 0x28, 0x0B, 0x0F, 0x75, 0x20, 0xF0, 0x82, 0x6D, 0x96, 0xD2, 0x42, 0x68, + 0x52, 0x0F, 0x85, 0x12, 0xE0, 0x52, 0x52, 0x93, 0xA8, 0xCA, 0x79, 0x49, + 0x4C, 0x81, 0x4F, 0x2A, 0x20, 0xA7, 0x00, 0xA0, 0x96, 0x60, 0x92, 0xA4, + 0x09, 0x0A, 0x21, 0x4B, 0x44, 0x92, 0xC8, 0x12, 0x54, 0x9A, 0xE8, 0x43, + 0x05, 0x22, 0xE0, 0x82, 0xAD, 0x3C, 0xC8, 0x51, 0x3A, 0xA1, 0x8B, 0x14, + 0xB7, 0x21, 0x49, 0xB5, 0xCE, 0x8A, 0x24, 0x84, 0xC6, 0xC0, 0xA0, 0xE7, + 0xA3, 0x48, 0x25, 0x90, 0x76, 0xAA, 0x61, 0xD0, 0x83, 0x23, 0x94, 0xC5, + 0xE8, 0xD2, 0xAD, 0x39, 0x79, 0x50, 0x2E, 0x28, 0x7B, 0xCA, 0x40, 0x99, + 0xD5, 0x2A, 0x55, 0xB2, 0x2E, 0x4D, 0xF0, 0x48, 0x90, 0x9A, 0x42, 0x48, + 0x66, 0x09, 0x52, 0xEC, 0x86, 0x0A, 0x23, 0x50, 0x48, 0x58, 0xC4, 0x28, + 0xC2, 0x24, 0x04, 0xC9, 0x29, 0x88, 0x6E, 0xAA, 0x70, 0x98, 0x1D, 0x54, + 0xB4, 0x49, 0x41, 0x19, 0x25, 0x0E, 0x61, 0x15, 0xC1, 0x08, 0x84, 0x42, + 0x0A, 0xC5, 0xCA, 0x1A, 0x12, 0x9D, 0xC0, 0xA7, 0x6B, 0x51, 0x43, 0x26, + 0x80, 0x88, 0x1A, 0x14, 0x40, 0x52, 0xEC, 0x82, 0x42, 0xC7, 0x45, 0x1D, + 0xC9, 0xDC, 0x86, 0x52, 0x52, 0xE5, 0xC9, 0xDA, 0x44, 0xA8, 0x04, 0xFC, + 0x22, 0x84, 0xC2, 0x09, 0xF2, 0x53, 0x20, 0x46, 0x88, 0x2D, 0x28, 0x93, + 0xA1, 0x09, 0xAB, 0x97, 0x01, 0x3A, 0x8E, 0xE0, 0x02, 0x6D, 0xE9, 0x2A, + 0xD4, 0xE6, 0xA8, 0x6D, 0xD7, 0xC2, 0x14, 0x8D, 0x8A, 0x25, 0xC1, 0x14, + 0x2A, 0x04, 0xCA, 0x4A, 0x3B, 0xF5, 0x84, 0x92, 0x55, 0xBF, 0xFF, 0xD7, + 0xEB, 0xA6, 0x12, 0xDE, 0xA0, 0x5C, 0xA2, 0x4A, 0xB9, 0x4D, 0x1B, 0x66, + 0x4A, 0x8C, 0xA8, 0xEE, 0x4D, 0x24, 0xA3, 0x48, 0xB4, 0x9B, 0x92, 0x25, + 0x40, 0x12, 0x9E, 0x52, 0x53, 0x29, 0x4D, 0x29, 0xA5, 0x29, 0x49, 0x4A, + 0x29, 0x92, 0x29, 0x24, 0xA5, 0x24, 0x92, 0x49, 0x29, 0x49, 0x92, 0x29, + 0x24, 0xA5, 0xD3, 0xA8, 0xA7, 0x49, 0x4B, 0xA4, 0x9A, 0x52, 0x94, 0x94, + 0xBC, 0xA9, 0x07, 0x21, 0x92, 0x90, 0x25, 0x25, 0x5A, 0x69, 0x4E, 0x0A, + 0x10, 0x25, 0x4E, 0x50, 0x4D, 0xB3, 0x05, 0x29, 0x50, 0x94, 0x89, 0x49, + 0x56, 0xCF, 0x74, 0x29, 0x36, 0xC4, 0x09, 0x4E, 0x0A, 0x54, 0xAB, 0x6C, + 0x6F, 0x94, 0xB7, 0x94, 0x10, 0xE5, 0x20, 0x50, 0xA4, 0xDA, 0x5D, 0xE5, + 0x34, 0xA8, 0xCA, 0x79, 0x49, 0x4B, 0x82, 0x65, 0x4A, 0x4A, 0x80, 0x52, + 0x41, 0x2B, 0x19, 0x4C, 0xA4, 0x98, 0xA4, 0xA6, 0x29, 0xC2, 0x49, 0xA1, + 0x14, 0x32, 0x81, 0xE2, 0x91, 0xF0, 0x51, 0x4A, 0x12, 0x52, 0xE9, 0x00, + 0x52, 0x01, 0x4C, 0x04, 0x92, 0xA0, 0xA6, 0x14, 0x42, 0x70, 0x82, 0x99, + 0xA6, 0x4C, 0x9D, 0x04, 0xA8, 0x27, 0x4C, 0xA4, 0x12, 0x53, 0x12, 0x0A, + 0x81, 0x08, 0xA6, 0x21, 0x40, 0xA4, 0xA4, 0x4E, 0x09, 0x82, 0x99, 0x0A, + 0x31, 0xAA, 0x28, 0x66, 0x08, 0x48, 0x90, 0x54, 0x7B, 0x68, 0xA2, 0x4E, + 0xA9, 0x29, 0x99, 0x28, 0x66, 0x0F, 0x09, 0xF5, 0x29, 0x35, 0xBA, 0xA2, + 0xA6, 0x20, 0x42, 0x90, 0x6E, 0xA9, 0xF6, 0xA7, 0x02, 0x35, 0x49, 0x4B, + 0xB4, 0x01, 0xAA, 0x45, 0xDA, 0x28, 0x92, 0x12, 0x88, 0x12, 0x82, 0x94, + 0x5C, 0x90, 0x04, 0xEA, 0x99, 0x3E, 0xE8, 0x45, 0x4B, 0x3B, 0x45, 0x02, + 0x54, 0xDC, 0x54, 0x1D, 0xC2, 0x48, 0x2C, 0x65, 0x25, 0x19, 0x49, 0x14, + 0x5B, 0xFF, 0xD0, 0xEA, 0x25, 0x31, 0x94, 0xE9, 0x95, 0xD7, 0x3D, 0x88, + 0x52, 0x10, 0x98, 0x27, 0x49, 0x4B, 0xA6, 0x25, 0x24, 0x88, 0x49, 0x4A, + 0x94, 0xE0, 0xA8, 0xC2, 0x74, 0x94, 0xB9, 0x4C, 0x9D, 0x34, 0xA4, 0x95, + 0x24, 0x94, 0xA4, 0x92, 0x16, 0x4E, 0xA4, 0x04, 0xA4, 0x52, 0x4A, 0xC0, + 0x24, 0x42, 0x70, 0x91, 0x49, 0x4C, 0x13, 0x4A, 0x72, 0x99, 0x14, 0x29, + 0x48, 0x28, 0x25, 0x30, 0x92, 0x92, 0x82, 0x12, 0x94, 0x30, 0x52, 0xDC, + 0x85, 0x2A, 0xD2, 0x4A, 0x52, 0xA0, 0x0A, 0x98, 0x29, 0x25, 0x6D, 0x53, + 0xC2, 0x49, 0x24, 0xA6, 0x41, 0x48, 0x28, 0x05, 0x29, 0x41, 0x21, 0x98, + 0x2A, 0x41, 0x0E, 0x54, 0x81, 0x41, 0x2C, 0xD3, 0xCA, 0x8C, 0xA7, 0x49, + 0x4B, 0xA4, 0x53, 0x4A, 0x52, 0x82, 0x54, 0x92, 0x49, 0x22, 0xA5, 0x27, + 0x01, 0x24, 0xE1, 0x05, 0x2E, 0x02, 0x78, 0x48, 0x27, 0x09, 0x29, 0x50, + 0x9E, 0x12, 0x4E, 0x82, 0x54, 0x92, 0x74, 0x92, 0x52, 0xC9, 0xD3, 0x24, + 0x92, 0x97, 0x51, 0x21, 0x3A, 0x62, 0x92, 0x96, 0x2D, 0x4C, 0x42, 0x94, + 0x26, 0x45, 0x08, 0xCC, 0x84, 0xA1, 0x4C, 0xA8, 0xC2, 0x4A, 0x58, 0x18, + 0x4E, 0x94, 0x27, 0x84, 0x94, 0xAE, 0x12, 0x24, 0x26, 0x29, 0x8A, 0x4A, + 0x62, 0x60, 0x14, 0xE5, 0xC9, 0x26, 0x28, 0xA1, 0x69, 0x48, 0x19, 0x49, + 0x34, 0xA4, 0xA5, 0x38, 0xE8, 0xA1, 0x29, 0x12, 0xA0, 0x4E, 0xA8, 0x80, + 0x82, 0x57, 0x9D, 0x52, 0x51, 0x92, 0x92, 0x28, 0x7F, 0xFF, 0xD1, 0xEA, + 0x08, 0x48, 0x05, 0x38, 0x4D, 0x0A, 0xEB, 0x41, 0x84, 0x27, 0x85, 0x28, + 0x4E, 0x18, 0x52, 0x55, 0x31, 0x00, 0x24, 0x42, 0x28, 0x68, 0xF9, 0xA6, + 0x2D, 0x42, 0xD5, 0x48, 0xB6, 0x9F, 0x04, 0xE1, 0x88, 0xA1, 0xA9, 0xF6, + 0xA5, 0x69, 0xA4, 0x5B, 0x02, 0x62, 0xC2, 0x8C, 0x1B, 0xAA, 0x7D, 0xA0, + 0xA5, 0x6A, 0xA4, 0x02, 0xA9, 0x52, 0xF4, 0xA3, 0x94, 0x70, 0x00, 0x51, + 0x72, 0x56, 0xAA, 0x47, 0x11, 0xC2, 0x81, 0xD5, 0x49, 0xCA, 0x29, 0x20, + 0xA8, 0x24, 0x52, 0x48, 0xA2, 0xA6, 0x05, 0x32, 0x9C, 0x14, 0xC4, 0x24, + 0x86, 0x2A, 0x30, 0xA7, 0x09, 0x8A, 0x2A, 0x63, 0xC2, 0x61, 0xCA, 0x91, + 0x09, 0xC3, 0x52, 0x52, 0xE1, 0x38, 0x48, 0x35, 0x39, 0x08, 0x29, 0x70, + 0x52, 0x4D, 0x05, 0x2D, 0x52, 0x4A, 0xEA, 0x41, 0x41, 0x38, 0x29, 0x29, + 0x9A, 0x90, 0x51, 0x69, 0x52, 0x08, 0x25, 0x90, 0x49, 0x32, 0x79, 0x41, + 0x2B, 0xA7, 0x4C, 0x0A, 0x79, 0x49, 0x4A, 0x84, 0xA1, 0x34, 0xA7, 0x05, + 0x24, 0xAE, 0x14, 0x80, 0x4D, 0x29, 0xC2, 0x0A, 0x66, 0x02, 0x50, 0xA4, + 0xD4, 0xEE, 0x08, 0x25, 0x82, 0x70, 0x53, 0x24, 0x92, 0x99, 0x24, 0x99, + 0x24, 0x94, 0xA4, 0xC9, 0x14, 0xC9, 0x29, 0x74, 0x93, 0x05, 0x24, 0x94, + 0xB1, 0x4C, 0x9D, 0x32, 0x2A, 0x59, 0x28, 0x49, 0x24, 0x90, 0xA4, 0xC9, + 0xD2, 0x49, 0x4B, 0x15, 0x12, 0x14, 0x93, 0x10, 0x92, 0x98, 0x26, 0x2A, + 0x64, 0x26, 0x84, 0x54, 0xC5, 0x44, 0xA9, 0xC2, 0x89, 0x09, 0x21, 0x19, + 0x51, 0x21, 0x13, 0x6A, 0x68, 0x45, 0x14, 0xC2, 0x12, 0x52, 0x84, 0x91, + 0x45, 0x3F, 0xFF, 0xD2, 0xEB, 0xA1, 0x48, 0x34, 0x42, 0x50, 0x9C, 0x2B, + 0x76, 0xD2, 0x63, 0x0D, 0x09, 0xE5, 0x22, 0xD4, 0x82, 0x4A, 0x50, 0x09, + 0x42, 0x98, 0x88, 0x48, 0x42, 0x56, 0xAA, 0x62, 0x02, 0x44, 0x29, 0x81, + 0x29, 0xF6, 0xA1, 0x69, 0xA4, 0x62, 0x52, 0x06, 0x14, 0xA0, 0x28, 0x91, + 0x1C, 0x24, 0xA5, 0x12, 0xA2, 0x52, 0x24, 0xCA, 0x70, 0x09, 0x45, 0x0C, + 0x1C, 0x02, 0x81, 0x08, 0xA5, 0xA9, 0x06, 0x23, 0x6A, 0xA4, 0x30, 0x53, + 0xC2, 0x2C, 0x05, 0x03, 0x09, 0x5A, 0x29, 0x8C, 0x26, 0x21, 0x49, 0x24, + 0x94, 0xC6, 0x13, 0x6D, 0x53, 0x49, 0x2B, 0x45, 0x30, 0xDA, 0x9C, 0x05, + 0x28, 0x4A, 0x12, 0x55, 0x2D, 0x09, 0xE1, 0x20, 0x92, 0x49, 0x58, 0x84, + 0xCA, 0x44, 0x28, 0x90, 0x92, 0x16, 0x4E, 0xA2, 0x9C, 0x04, 0x54, 0xC8, + 0x15, 0x2D, 0xCA, 0x21, 0x24, 0x12, 0xCB, 0x72, 0x6D, 0xC9, 0x8A, 0x52, + 0x92, 0x19, 0x87, 0x29, 0x02, 0x86, 0x0A, 0x90, 0x29, 0x25, 0x94, 0xA9, + 0x05, 0x10, 0x92, 0x09, 0x66, 0x9C, 0x39, 0x40, 0x14, 0xE0, 0xC9, 0x49, + 0x49, 0xD8, 0xE5, 0x32, 0x84, 0xD4, 0x40, 0x0A, 0x69, 0x5C, 0x16, 0x29, + 0x24, 0x44, 0x24, 0x92, 0x94, 0x92, 0x50, 0x92, 0x49, 0x59, 0x34, 0x29, + 0x42, 0x64, 0x90, 0xA8, 0x49, 0x24, 0x92, 0x52, 0x93, 0x27, 0x49, 0x25, + 0x2D, 0x09, 0x27, 0x49, 0x25, 0x28, 0x09, 0x52, 0xD8, 0x9D, 0x81, 0x11, + 0x0B, 0x48, 0x08, 0xBD, 0x35, 0x12, 0xC4, 0x78, 0x43, 0x72, 0x56, 0xAA, + 0x42, 0x5A, 0x98, 0x84, 0x42, 0x14, 0x0A, 0x28, 0xA6, 0x24, 0x28, 0x90, + 0xA6, 0x98, 0x84, 0x50, 0xC0, 0x85, 0x12, 0x11, 0x21, 0x34, 0x22, 0x86, + 0x10, 0x92, 0x9C, 0x24, 0x92, 0xA9, 0xFF, 0xD3, 0xEC, 0x53, 0xB5, 0x25, + 0x20, 0xD5, 0x69, 0xA6, 0xA8, 0x05, 0x2D, 0x89, 0xE2, 0x12, 0x98, 0x49, + 0x2B, 0x6D, 0x84, 0xB4, 0x4A, 0x74, 0x4C, 0x4C, 0x24, 0xA6, 0x40, 0xA4, + 0x4A, 0x81, 0x72, 0x6D, 0xC9, 0x29, 0x92, 0x64, 0xD2, 0x9C, 0x10, 0x92, + 0x15, 0x09, 0x27, 0x91, 0xD9, 0x31, 0x29, 0x29, 0x7D, 0xAA, 0x26, 0x42, + 0x45, 0xE4, 0x28, 0x97, 0x92, 0x92, 0x94, 0x75, 0x51, 0x20, 0x26, 0x25, + 0x34, 0x94, 0x50, 0xBA, 0x62, 0x94, 0x14, 0xA1, 0x14, 0x2C, 0xA4, 0x13, + 0x42, 0x70, 0x12, 0x52, 0xE0, 0x25, 0x09, 0x27, 0x41, 0x2B, 0x42, 0x65, + 0x24, 0xA1, 0x25, 0x30, 0x84, 0x8B, 0x54, 0xE1, 0x32, 0x2A, 0xA6, 0x01, + 0x8A, 0x41, 0x8A, 0x41, 0x29, 0x4A, 0xD5, 0x4C, 0x4B, 0x53, 0x42, 0x91, + 0x4D, 0xB9, 0x24, 0x31, 0x21, 0x32, 0x44, 0xA6, 0x94, 0x90, 0xC8, 0x29, + 0x88, 0x50, 0x12, 0x78, 0x52, 0x00, 0xA4, 0x90, 0xCA, 0x53, 0xCA, 0x4D, + 0x68, 0x8D, 0x54, 0x83, 0x42, 0x09, 0xA5, 0x80, 0x44, 0x63, 0x0A, 0x76, + 0x88, 0x44, 0x05, 0x02, 0x52, 0x02, 0x80, 0x4F, 0x2A, 0x25, 0xCA, 0x32, + 0x82, 0x52, 0x13, 0x29, 0x94, 0x41, 0x4F, 0x29, 0x25, 0x74, 0x92, 0x4D, + 0x29, 0x29, 0x72, 0x99, 0x32, 0x49, 0x21, 0x49, 0x4A, 0x69, 0x4A, 0x52, + 0x52, 0xE9, 0x15, 0x20, 0x13, 0xC0, 0x49, 0x54, 0xC1, 0x48, 0x04, 0xF0, + 0x9F, 0x69, 0xEC, 0x92, 0x69, 0x41, 0x4D, 0xA9, 0x9A, 0x13, 0x80, 0x82, + 0x57, 0x3C, 0x28, 0x10, 0x89, 0x09, 0x89, 0x08, 0x29, 0x11, 0x6A, 0x19, + 0x84, 0x67, 0x4A, 0x11, 0x09, 0xC1, 0x05, 0x82, 0x62, 0xA5, 0xB4, 0xA8, + 0xC2, 0x28, 0x59, 0x24, 0xF0, 0x94, 0x14, 0x90, 0xB2, 0x49, 0xE1, 0x24, + 0x94, 0xFF, 0x00, 0xFF, 0xD4, 0xEC, 0xD3, 0xEE, 0x51, 0x71, 0x0A, 0x3B, + 0x95, 0xA6, 0xA2, 0x49, 0x51, 0x73, 0x94, 0x65, 0x34, 0xA5, 0x48, 0xB6, + 0x5B, 0x93, 0xB8, 0xCA, 0x1C, 0xA9, 0x07, 0x24, 0xA5, 0x42, 0x49, 0x12, + 0x9B, 0x59, 0x45, 0x4B, 0xEA, 0x92, 0x53, 0x1A, 0x26, 0x28, 0x29, 0x94, + 0xA6, 0x2A, 0x29, 0x4A, 0x4A, 0x51, 0x4C, 0x91, 0x29, 0xA5, 0x14, 0x29, + 0x20, 0x0A, 0x49, 0xE4, 0x22, 0xA5, 0x04, 0xE9, 0x93, 0xA0, 0xA5, 0x42, + 0x96, 0xD4, 0x82, 0x70, 0x82, 0x56, 0xDA, 0x96, 0xD5, 0x24, 0xF0, 0x52, + 0x55, 0x30, 0x84, 0xB6, 0xA2, 0x06, 0x94, 0xCE, 0x05, 0x2B, 0x55, 0x30, + 0xDA, 0x13, 0x42, 0x72, 0x9A, 0x52, 0x52, 0xD0, 0x98, 0x94, 0xE9, 0x88, + 0x45, 0x0C, 0x4B, 0x82, 0x81, 0x72, 0x99, 0x62, 0x60, 0xCD, 0x75, 0x45, + 0x06, 0xD8, 0x4C, 0xF0, 0x90, 0x61, 0x94, 0x5D, 0x80, 0x26, 0xD1, 0x2B, + 0x45, 0x2D, 0x04, 0x27, 0xDC, 0x53, 0x81, 0x29, 0xF6, 0xC2, 0x49, 0x53, + 0x49, 0xEE, 0x8A, 0xD5, 0x06, 0x80, 0xA7, 0xB8, 0x04, 0x0A, 0x43, 0x31, + 0x2A, 0x4A, 0x2D, 0x7A, 0x94, 0xCA, 0x6A, 0xE5, 0x1F, 0x35, 0x02, 0x61, + 0x3B, 0x8F, 0x9A, 0x13, 0x88, 0x05, 0x10, 0x82, 0xCF, 0x72, 0x90, 0x72, + 0x00, 0x2A, 0x61, 0xC9, 0x52, 0xAD, 0x34, 0xA5, 0x28, 0x60, 0xA7, 0x94, + 0x12, 0xCA, 0x52, 0x94, 0xC9, 0xD2, 0x52, 0xD0, 0x9C, 0x24, 0x92, 0x4A, + 0x5C, 0x15, 0x31, 0x08, 0x63, 0x94, 0x49, 0xD1, 0x24, 0x86, 0x41, 0x4D, + 0xA0, 0x20, 0xEE, 0x0A, 0x4D, 0x72, 0x09, 0x09, 0x0F, 0x29, 0x4A, 0x8C, + 0xCA, 0x79, 0x41, 0x4B, 0x92, 0x86, 0x65, 0x48, 0x94, 0xDA, 0x22, 0xA6, + 0x24, 0xA6, 0x2A, 0x44, 0x05, 0x14, 0x90, 0xB2, 0x8E, 0xC9, 0x4E, 0x4C, + 0x26, 0x0F, 0x45, 0x4A, 0xDA, 0x02, 0x73, 0x09, 0xC1, 0x4C, 0x52, 0x53, + 0x0E, 0xFC, 0x24, 0x9E, 0x44, 0xA4, 0x92, 0x1F, 0xFF, 0xD5, 0xEB, 0x8B, + 0xB5, 0x4C, 0x5C, 0x10, 0x4D, 0x89, 0x7A, 0x81, 0x5C, 0xA6, 0x8D, 0xA6, + 0xDC, 0x13, 0x6E, 0x41, 0xDE, 0x13, 0x87, 0x25, 0x4A, 0xB4, 0xB3, 0x29, + 0x03, 0xAA, 0x80, 0x72, 0x94, 0x94, 0x93, 0x69, 0x01, 0x95, 0x30, 0xA0, + 0xD9, 0x44, 0x03, 0x54, 0xD2, 0x95, 0xB6, 0x84, 0xE5, 0xA2, 0x14, 0xC3, + 0x53, 0x38, 0x14, 0x93, 0x48, 0x88, 0x51, 0x32, 0xA6, 0xE2, 0xA1, 0x25, + 0x14, 0x2D, 0x09, 0xA1, 0x49, 0x24, 0x90, 0xC6, 0x12, 0x52, 0x84, 0xD0, + 0x92, 0x94, 0x92, 0x49, 0xD2, 0x52, 0xE1, 0x38, 0x4C, 0x14, 0xE1, 0x04, + 0xAC, 0xA4, 0x1C, 0xA3, 0x09, 0x88, 0x29, 0x29, 0x30, 0x72, 0x89, 0xD5, + 0x40, 0x27, 0xDC, 0x61, 0x2A, 0x4D, 0xAC, 0xE0, 0x50, 0xD4, 0x89, 0x51, + 0x21, 0x14, 0x15, 0x12, 0x98, 0x3B, 0x54, 0x93, 0x42, 0x48, 0x65, 0x25, + 0x29, 0x0A, 0x24, 0x25, 0x09, 0x29, 0x96, 0xE0, 0xA2, 0x4C, 0xA6, 0x84, + 0xA1, 0x14, 0x32, 0x07, 0x44, 0x89, 0x50, 0xE1, 0x22, 0xE2, 0x92, 0xAD, + 0x9B, 0x5C, 0x9C, 0xBA, 0x50, 0xC3, 0x92, 0x2F, 0x09, 0x2A, 0xD2, 0x6E, + 0x29, 0x7A, 0x87, 0xC5, 0x0B, 0x7A, 0x60, 0x65, 0x2A, 0x55, 0xA4, 0x2F, + 0x25, 0x29, 0x51, 0x0A, 0x40, 0x68, 0x92, 0x99, 0x02, 0xA6, 0x10, 0x82, + 0x90, 0x28, 0x25, 0x28, 0x29, 0x4A, 0x80, 0x29, 0xC3, 0x90, 0x4B, 0x39, + 0x4F, 0x2A, 0x12, 0x94, 0xA4, 0xA6, 0x72, 0x9E, 0x50, 0xE5, 0x36, 0xE4, + 0x95, 0x69, 0x66, 0x12, 0x0F, 0x42, 0xDC, 0x53, 0x17, 0x25, 0x4A, 0xB4, + 0xA5, 0xFA, 0xA7, 0x0F, 0xF1, 0x55, 0xF7, 0xC1, 0x4B, 0xD5, 0x08, 0xD2, + 0xAD, 0xB4, 0x1E, 0xA5, 0xBD, 0x55, 0x6D, 0xA1, 0x4C, 0x58, 0x10, 0xA4, + 0xF1, 0x36, 0x37, 0x26, 0xDC, 0x84, 0x2C, 0x52, 0x2E, 0x04, 0x21, 0x49, + 0xB5, 0xDC, 0xF8, 0x4D, 0xBE, 0x50, 0xDC, 0xE2, 0x9A, 0x7C, 0x51, 0xA4, + 0x5B, 0x32, 0x65, 0x32, 0x81, 0x78, 0x0A, 0x2E, 0xB2, 0x78, 0x4A, 0x91, + 0x69, 0xC3, 0x82, 0x77, 0x3F, 0x45, 0x5C, 0x3D, 0x27, 0x5B, 0x09, 0x52, + 0xAD, 0x9E, 0xED, 0x52, 0x55, 0xFD, 0x5D, 0x52, 0x46, 0x91, 0xC4, 0xFF, + 0x00, 0xFF, 0xD6, 0xE8, 0x35, 0x52, 0x82, 0xA4, 0x1A, 0xA5, 0xB6, 0x15, + 0xDB, 0x73, 0xE9, 0x88, 0x0A, 0x40, 0x29, 0x00, 0x13, 0xC0, 0x4A, 0xD3, + 0x4B, 0x00, 0xA6, 0xD0, 0x98, 0x27, 0x0E, 0x01, 0x04, 0xA7, 0xAC, 0x0E, + 0xE8, 0x82, 0x15, 0x60, 0xF8, 0x4B, 0xD5, 0x84, 0xDA, 0x5D, 0x6D, 0xA2, + 0xE0, 0xA2, 0xE7, 0x20, 0x8B, 0x27, 0x95, 0x17, 0x58, 0x95, 0x26, 0xD9, + 0xB9, 0xC1, 0x46, 0x50, 0xF7, 0xEA, 0x9C, 0x39, 0x1A, 0x5B, 0x6C, 0xD2, + 0x4D, 0x29, 0xC2, 0x49, 0x5E, 0x13, 0x27, 0x4C, 0x92, 0x96, 0x4E, 0x92, + 0x50, 0x92, 0x17, 0x05, 0x48, 0x15, 0x10, 0x9C, 0x20, 0x96, 0x69, 0x68, + 0xA3, 0x29, 0xD2, 0x4A, 0x88, 0x4C, 0x94, 0xA6, 0x2E, 0x49, 0x0A, 0x85, + 0x12, 0x96, 0xE4, 0xB7, 0x22, 0xA5, 0x42, 0x50, 0x94, 0xA4, 0x92, 0x95, + 0x0A, 0x3A, 0x27, 0x24, 0x26, 0x90, 0x92, 0x14, 0x91, 0x4B, 0x44, 0x8C, + 0x24, 0xA6, 0x25, 0x0D, 0xC7, 0xC1, 0x4D, 0xCA, 0x05, 0x15, 0xA5, 0x69, + 0x4A, 0x0A, 0x62, 0x9C, 0x14, 0x54, 0xBC, 0x14, 0x80, 0x84, 0xF3, 0x09, + 0x72, 0x92, 0x95, 0x29, 0xF7, 0x15, 0x12, 0x13, 0x80, 0x92, 0x99, 0x02, + 0xA4, 0x10, 0xD4, 0x9A, 0x82, 0x43, 0x30, 0x53, 0x82, 0xA2, 0x9A, 0x52, + 0x4A, 0x49, 0x4C, 0x1C, 0xA3, 0x29, 0xA6, 0x10, 0x55, 0xA5, 0x2A, 0x24, + 0xA8, 0xEE, 0x4B, 0x70, 0x49, 0x4B, 0xEE, 0x51, 0x2E, 0x29, 0x12, 0x14, + 0x09, 0x45, 0x05, 0x72, 0xE5, 0x12, 0x53, 0xCA, 0x6E, 0xE9, 0x21, 0x40, + 0x90, 0x54, 0xDA, 0xE2, 0xA3, 0xB7, 0x45, 0x26, 0x88, 0x49, 0x4C, 0xC3, + 0x93, 0xEF, 0x3E, 0x28, 0x4E, 0x74, 0x28, 0x1B, 0x09, 0xE1, 0x2A, 0x4D, + 0xB6, 0x0B, 0xC7, 0x8A, 0x6D, 0xE3, 0xB2, 0xAF, 0x2E, 0x3C, 0xA9, 0x00, + 0x52, 0xA4, 0x5A, 0x57, 0x3A, 0x54, 0x0A, 0x61, 0x29, 0x14, 0x94, 0xAD, + 0xCA, 0x0E, 0x24, 0xA7, 0x2A, 0x3A, 0xA2, 0x86, 0x3E, 0xE9, 0x49, 0x4D, + 0x24, 0x6D, 0x54, 0xFF, 0x00, 0xFF, 0xD7, 0xEA, 0x76, 0x27, 0xD8, 0x8A, + 0x18, 0xA5, 0xB3, 0x45, 0x6E, 0xDA, 0x54, 0x80, 0x35, 0x3C, 0x22, 0x6C, + 0x4B, 0x6A, 0x56, 0xAA, 0x44, 0x42, 0x81, 0x06, 0x55, 0x80, 0xC9, 0x48, + 0xD4, 0x07, 0x64, 0xAD, 0x54, 0x80, 0x07, 0x14, 0xFB, 0x61, 0x17, 0x6C, + 0x28, 0xBA, 0x51, 0xB5, 0x53, 0x0E, 0xC9, 0xB5, 0x52, 0x2C, 0x29, 0xC3, + 0x60, 0x24, 0x8A, 0x47, 0x05, 0x4C, 0x04, 0xA3, 0x55, 0x20, 0x92, 0xA9, + 0x93, 0x53, 0xA6, 0x09, 0x1D, 0x10, 0x5C, 0xA2, 0xA3, 0x25, 0x24, 0xE9, + 0x21, 0x70, 0x9E, 0x13, 0x04, 0xF2, 0x92, 0x57, 0x49, 0x34, 0xA5, 0x28, + 0x29, 0x92, 0x52, 0xA1, 0xB9, 0x2D, 0xC9, 0x52, 0x99, 0x12, 0xA0, 0xE2, + 0x98, 0x95, 0x12, 0x51, 0xA4, 0x12, 0xBC, 0xA5, 0xB9, 0x42, 0x4A, 0x70, + 0x8A, 0x2D, 0x98, 0x29, 0xC9, 0x51, 0x4C, 0x4A, 0x49, 0x51, 0x70, 0x4D, + 0xB9, 0x46, 0x53, 0x4A, 0x48, 0xB6, 0x5B, 0xD3, 0xEE, 0x42, 0x25, 0x29, + 0xD1, 0x14, 0x5B, 0x32, 0x65, 0x44, 0x94, 0xC4, 0x94, 0xDA, 0xA4, 0xA5, + 0x12, 0x9D, 0xAE, 0x95, 0x02, 0x13, 0xB5, 0x24, 0x24, 0x29, 0x00, 0x9B, + 0x54, 0xFA, 0xC2, 0x49, 0x52, 0x79, 0x51, 0x82, 0x90, 0x09, 0x29, 0x79, + 0x52, 0x06, 0x14, 0x53, 0xA4, 0xA5, 0xF7, 0x25, 0x2A, 0x25, 0xC9, 0x20, + 0xA6, 0x41, 0xD0, 0x9C, 0xBC, 0x76, 0x43, 0x77, 0x0A, 0x32, 0x95, 0x2A, + 0xD2, 0x6E, 0x4E, 0x20, 0xA1, 0x84, 0xB7, 0x24, 0xAB, 0x48, 0x48, 0x09, + 0xB4, 0x2A, 0x32, 0x94, 0xA4, 0xA6, 0x49, 0x0F, 0x34, 0xC1, 0x49, 0x24, + 0xAE, 0x9E, 0x54, 0x64, 0xA4, 0x64, 0xA4, 0xA5, 0x39, 0xBD, 0xD0, 0xC8, + 0x85, 0x32, 0x0A, 0x6D, 0xA5, 0x24, 0x10, 0xC4, 0x29, 0xB5, 0x20, 0xD5, + 0x36, 0x35, 0x25, 0x00, 0xB6, 0xD4, 0xE1, 0x88, 0x80, 0x29, 0x86, 0xA1, + 0x6B, 0xA9, 0x07, 0xA4, 0x9F, 0xD3, 0x01, 0x1A, 0x13, 0x16, 0xCA, 0x56, + 0xAA, 0x43, 0xB4, 0x4A, 0x48, 0x9B, 0x12, 0x4A, 0xD5, 0x4F, 0xFF, 0xD0, + 0xED, 0x8B, 0x52, 0x0C, 0x48, 0xA7, 0x69, 0x56, 0x5A, 0xAB, 0x8A, 0xD2, + 0x35, 0x85, 0x30, 0xE4, 0xCE, 0x72, 0x09, 0x47, 0xB4, 0x04, 0xA1, 0x39, + 0x29, 0xB7, 0x14, 0x50, 0xC1, 0xC0, 0x28, 0x96, 0xA2, 0x19, 0x29, 0x6D, + 0x29, 0x5A, 0x29, 0x09, 0x61, 0x4B, 0x69, 0xEE, 0x8D, 0xB1, 0x36, 0xC4, + 0x6D, 0x54, 0x84, 0xB5, 0x3C, 0x04, 0x5D, 0x85, 0x37, 0xA6, 0x95, 0xAA, + 0x91, 0x41, 0x4B, 0x69, 0x46, 0x0C, 0x4F, 0xB5, 0x2B, 0x55, 0x21, 0xDA, + 0x52, 0xDA, 0x8D, 0xB5, 0x2D, 0xA9, 0x5A, 0xA9, 0x0E, 0xD4, 0xD0, 0x51, + 0x8B, 0x53, 0x40, 0x4A, 0xD5, 0x48, 0xA0, 0xA5, 0x05, 0x13, 0x6A, 0x50, + 0x95, 0xA2, 0x91, 0x42, 0x62, 0x11, 0x4B, 0x53, 0x10, 0x8D, 0xAA, 0x91, + 0x96, 0xA6, 0xDA, 0xA6, 0x54, 0x4A, 0x48, 0x62, 0x42, 0x8C, 0xA9, 0x39, + 0x40, 0x84, 0x50, 0x57, 0xDC, 0x9A, 0x54, 0x53, 0xF0, 0x92, 0x14, 0x99, + 0x4B, 0x94, 0xD0, 0x92, 0x98, 0x99, 0x4D, 0x05, 0x4A, 0x14, 0xA1, 0x1B, + 0x53, 0x00, 0xDF, 0x14, 0xA0, 0xA9, 0x42, 0x45, 0x2B, 0x53, 0x08, 0x4A, + 0x13, 0x92, 0x13, 0x84, 0x94, 0xC7, 0x50, 0xA4, 0x25, 0x38, 0x0A, 0x40, + 0x04, 0x95, 0x4B, 0x76, 0x4C, 0xA4, 0x79, 0x4D, 0xDD, 0x05, 0x2B, 0x6A, + 0x7D, 0xBA, 0x25, 0xB8, 0xA7, 0x0E, 0x49, 0x28, 0xF6, 0x92, 0x54, 0xB6, + 0xE8, 0x88, 0x1A, 0x0A, 0x62, 0xD0, 0x95, 0xAA, 0x90, 0xBA, 0x41, 0x4D, + 0x05, 0x14, 0xB5, 0x47, 0x6A, 0x36, 0x8A, 0x61, 0x05, 0x48, 0x35, 0x48, + 0x35, 0x3C, 0x21, 0x6A, 0xA6, 0x10, 0x52, 0xDA, 0x89, 0xB5, 0x23, 0x09, + 0x5A, 0x69, 0x88, 0x10, 0x9C, 0x04, 0x8A, 0x70, 0xE4, 0x94, 0xA8, 0x52, + 0x01, 0x30, 0x29, 0xD0, 0x4A, 0x93, 0x42, 0x74, 0xE1, 0x25, 0x28, 0x35, + 0x38, 0x09, 0xC0, 0x4E, 0x02, 0x16, 0x95, 0xDA, 0x11, 0x00, 0x4C, 0x14, + 0x82, 0x09, 0x5B, 0x6A, 0x70, 0xD9, 0x4E, 0x13, 0xA4, 0x9A, 0x5B, 0xD3, + 0x09, 0x27, 0x49, 0x25, 0x3F, 0xFF, 0xD1, 0xED, 0xE1, 0x34, 0x29, 0xC2, + 0x50, 0xAC, 0x35, 0x98, 0xA6, 0x32, 0xA5, 0x09, 0x6D, 0x94, 0x94, 0xC5, + 0x3C, 0x27, 0x0D, 0x4F, 0x09, 0x29, 0x68, 0x4F, 0x09, 0xE1, 0x3C, 0x24, + 0xA6, 0x30, 0x94, 0x29, 0x42, 0x78, 0x41, 0x2C, 0x21, 0x28, 0x53, 0x84, + 0xA1, 0x25, 0x30, 0x84, 0xA1, 0x4E, 0x13, 0x42, 0x4A, 0x63, 0x09, 0x88, + 0x53, 0x85, 0x12, 0x8A, 0x18, 0x90, 0x9A, 0x14, 0x92, 0x84, 0x94, 0xC2, + 0x12, 0x85, 0x38, 0x4C, 0x42, 0x36, 0x8A, 0x60, 0x42, 0x81, 0x45, 0x20, + 0xA8, 0x16, 0xA4, 0x82, 0x89, 0xC5, 0x40, 0xCA, 0x29, 0x6A, 0x8E, 0xD4, + 0xE4, 0x23, 0x32, 0xA0, 0x8E, 0x6B, 0x94, 0xBD, 0x24, 0xAD, 0x14, 0x82, + 0x13, 0xC2, 0x21, 0xA8, 0xA5, 0xB0, 0x84, 0x6D, 0x54, 0xC1, 0x25, 0x2D, + 0xA5, 0x2D, 0xA5, 0x05, 0x53, 0x08, 0x4C, 0x53, 0xB9, 0x44, 0x82, 0x8A, + 0x16, 0x94, 0xC4, 0xA7, 0xD5, 0x30, 0xD4, 0xA4, 0x85, 0xBB, 0xA9, 0xB4, + 0x26, 0xD8, 0xA4, 0x02, 0x49, 0x01, 0x96, 0x89, 0xC0, 0x94, 0xC1, 0x15, + 0xA0, 0x42, 0x09, 0x46, 0x5A, 0x9A, 0x11, 0x88, 0x09, 0xA0, 0x25, 0x6A, + 0xA4, 0x50, 0x9C, 0x22, 0x16, 0xA4, 0x18, 0x95, 0xAA, 0x98, 0x04, 0xFA, + 0x22, 0x6C, 0x01, 0x2D, 0xA0, 0xA5, 0x69, 0xA4, 0x70, 0x91, 0x03, 0xB2, + 0x9E, 0xC0, 0x13, 0x7B, 0x52, 0xB5, 0x52, 0x38, 0x29, 0x00, 0x42, 0x9C, + 0x6A, 0x9C, 0x35, 0x2B, 0x55, 0x30, 0x84, 0xC4, 0x22, 0x90, 0x02, 0x83, + 0x92, 0x53, 0x08, 0x4D, 0x21, 0x3C, 0x4A, 0x6D, 0x85, 0x14, 0x2E, 0x0A, + 0x70, 0xE4, 0xC1, 0xA9, 0xE1, 0x05, 0x32, 0x04, 0x27, 0x0A, 0x09, 0xC2, + 0x49, 0x66, 0x9C, 0x28, 0xA9, 0x09, 0x41, 0x4C, 0x81, 0x52, 0x0A, 0x20, + 0x29, 0x80, 0x82, 0x59, 0x04, 0xE9, 0x00, 0x9D, 0x05, 0xCB, 0x24, 0xA4, + 0x92, 0x4A, 0x7F, 0xFF, 0xD2, 0xEE, 0xA1, 0x3C, 0x24, 0x9D, 0x4E, 0xD7, + 0x63, 0x09, 0xE1, 0x4A, 0x12, 0xDA, 0x92, 0x96, 0x84, 0xA1, 0x4A, 0x02, + 0x50, 0x92, 0x96, 0x84, 0xA1, 0x4A, 0x12, 0x49, 0x4B, 0x42, 0x50, 0xA4, + 0x99, 0x25, 0x2D, 0x09, 0x42, 0x74, 0x92, 0x4A, 0xD0, 0x9A, 0x13, 0xA6, + 0x29, 0x21, 0x64, 0xC4, 0x27, 0x4C, 0x92, 0x96, 0x84, 0xA1, 0x3C, 0x24, + 0x92, 0x96, 0x84, 0xA1, 0x3A, 0x49, 0x29, 0x89, 0x0A, 0x24, 0x29, 0xA4, + 0x8A, 0x11, 0x16, 0xA8, 0x46, 0xA8, 0xC4, 0x26, 0x2D, 0x4A, 0xD1, 0x4C, + 0x02, 0x4A, 0x61, 0xA9, 0x10, 0x8D, 0xAA, 0x98, 0x18, 0x43, 0x25, 0x14, + 0xB1, 0x0C, 0xB7, 0x54, 0x90, 0x56, 0x4C, 0xE5, 0x3D, 0xAA, 0x2E, 0x6A, + 0x2A, 0xA4, 0x25, 0xA2, 0x53, 0x86, 0x4A, 0x91, 0x69, 0x4E, 0xD0, 0x51, + 0xB4, 0x52, 0x33, 0x5A, 0x6D, 0xA0, 0x23, 0x98, 0x50, 0x2D, 0x4A, 0xD5, + 0x4C, 0x04, 0x15, 0x20, 0xC0, 0x9F, 0x6A, 0x90, 0x1A, 0x21, 0x6A, 0xA6, + 0x21, 0x9D, 0xD4, 0xA0, 0x24, 0x02, 0x78, 0x29, 0x25, 0x6D, 0x52, 0x82, + 0xA7, 0xAA, 0x89, 0x3E, 0x29, 0x29, 0x40, 0x88, 0xD5, 0x48, 0x42, 0x82, + 0x40, 0x84, 0x94, 0xCD, 0xDC, 0x28, 0x14, 0xF3, 0x29, 0x8A, 0x4A, 0x5C, + 0x04, 0xC5, 0xA5, 0x2D, 0xC9, 0xC3, 0xA5, 0x25, 0x2C, 0x34, 0x52, 0x05, + 0x44, 0xA6, 0x0E, 0x01, 0x25, 0x33, 0x21, 0x47, 0x68, 0x4E, 0x5C, 0x90, + 0x72, 0x4A, 0x5B, 0x60, 0x4C, 0x5A, 0xA6, 0x0A, 0x72, 0x92, 0x90, 0xED, + 0x48, 0xB6, 0x78, 0x46, 0x00, 0x14, 0xB6, 0x78, 0x25, 0x6A, 0xA4, 0x3B, + 0x4A, 0x70, 0xD4, 0x6F, 0x4C, 0xA7, 0x15, 0xC2, 0x56, 0xAA, 0x42, 0x02, + 0x90, 0x08, 0x9B, 0x12, 0xD8, 0x85, 0xAA, 0x96, 0x01, 0x4C, 0x26, 0x01, + 0x4F, 0x6A, 0x45, 0x21, 0x70, 0x9E, 0x12, 0x01, 0x3A, 0x09, 0x5A, 0x12, + 0x4E, 0x92, 0x4A, 0x7F, 0xFF, 0xD3, 0xEF, 0x13, 0x84, 0xF0, 0x94, 0x29, + 0xDA, 0xEB, 0xA4, 0x92, 0x78, 0x41, 0x2C, 0x75, 0x4E, 0x14, 0xA1, 0x28, + 0x49, 0x4B, 0x42, 0x50, 0xA5, 0x09, 0x42, 0x4A, 0x62, 0x92, 0x94, 0x25, + 0x09, 0x29, 0x8C, 0x25, 0x0A, 0x50, 0x9A, 0x12, 0x53, 0x14, 0xD0, 0xA7, + 0x09, 0xA1, 0x25, 0x31, 0x84, 0xA1, 0x3C, 0x24, 0x8A, 0x98, 0x90, 0x99, + 0x48, 0xA6, 0x21, 0x24, 0x31, 0x49, 0x3A, 0x64, 0x94, 0xA8, 0x4A, 0x12, + 0x49, 0x25, 0x2D, 0x09, 0xA1, 0x3A, 0x49, 0x29, 0x68, 0x29, 0x27, 0x49, + 0x25, 0x31, 0x2A, 0x24, 0x05, 0x22, 0x98, 0xA2, 0x86, 0x30, 0x14, 0x0A, + 0x99, 0x09, 0xA1, 0x24, 0x23, 0xD5, 0x38, 0x4E, 0x5A, 0x98, 0x84, 0x50, + 0xA4, 0xE1, 0xA1, 0x47, 0x55, 0x20, 0xE4, 0x94, 0xAD, 0x81, 0x3E, 0xC4, + 0xDB, 0x94, 0x83, 0xA7, 0x94, 0x92, 0xAD, 0xA0, 0x25, 0xB4, 0x29, 0x88, + 0x29, 0x10, 0x85, 0xAA, 0x98, 0x91, 0xA2, 0x19, 0x6E, 0xA8, 0x84, 0x21, + 0x94, 0x42, 0x8B, 0x12, 0x9A, 0x13, 0x94, 0xD0, 0x8A, 0x15, 0xAA, 0x7D, + 0x53, 0x80, 0x9D, 0x05, 0x31, 0xDA, 0x98, 0x68, 0xA6, 0x94, 0x24, 0xA6, + 0x04, 0xCA, 0x50, 0xA7, 0x09, 0x42, 0x4A, 0xA6, 0x29, 0xE0, 0x29, 0x6D, + 0x4A, 0x12, 0x55, 0x28, 0x37, 0xC1, 0x39, 0x6A, 0x76, 0x88, 0x52, 0x29, + 0x26, 0x98, 0xB5, 0xA8, 0xCD, 0x68, 0x50, 0x10, 0x14, 0xA5, 0x02, 0x95, + 0xC8, 0x01, 0x31, 0x12, 0x90, 0xD5, 0x3A, 0x0A, 0x5A, 0x13, 0x16, 0xA9, + 0x29, 0x00, 0x8A, 0x98, 0x06, 0xA9, 0x84, 0xB6, 0xA7, 0x01, 0x04, 0xAC, + 0x92, 0x94, 0x25, 0x09, 0x29, 0x8A, 0x4A, 0x50, 0x92, 0x4A, 0x7F, 0xFF, + 0xD4, 0xF4, 0x08, 0x4A, 0x13, 0xA4, 0xA6, 0x60, 0x52, 0x74, 0xC9, 0xD2, + 0x52, 0x93, 0xA4, 0x92, 0x49, 0x52, 0x49, 0x27, 0x49, 0x4B, 0x25, 0x09, + 0xD2, 0x49, 0x4B, 0x24, 0x9E, 0x12, 0x84, 0x94, 0xC5, 0x32, 0x92, 0x64, + 0x94, 0xC5, 0x24, 0xF0, 0x98, 0xA4, 0x85, 0x8A, 0x64, 0xE5, 0x32, 0x4A, + 0x58, 0xA6, 0x4E, 0x98, 0xA2, 0x85, 0x92, 0x4E, 0x92, 0x4A, 0x5A, 0x12, + 0x4E, 0x99, 0x25, 0x29, 0x28, 0x49, 0x24, 0x94, 0xB1, 0x0A, 0x24, 0x29, + 0x14, 0xC9, 0x29, 0x89, 0x09, 0x93, 0x94, 0xC5, 0x14, 0x2C, 0x42, 0x62, + 0x13, 0xA4, 0x92, 0x98, 0x42, 0x50, 0x14, 0x8A, 0x8A, 0x28, 0x5A, 0x13, + 0x84, 0x93, 0x84, 0x94, 0xCC, 0x29, 0x21, 0x82, 0x53, 0xCA, 0x09, 0x5C, + 0x85, 0x02, 0x14, 0xE5, 0x44, 0xA4, 0xA6, 0x10, 0x94, 0x29, 0x27, 0x80, + 0x8A, 0x18, 0x42, 0x74, 0xF0, 0x94, 0x24, 0xA5, 0xA1, 0x3A, 0x74, 0x90, + 0x52, 0xD0, 0x9C, 0x04, 0xE9, 0x42, 0x49, 0x54, 0x25, 0x09, 0xE1, 0x3C, + 0x24, 0xA6, 0x30, 0x9C, 0x05, 0x28, 0x09, 0xE1, 0x25, 0x2D, 0x09, 0xE1, + 0x3C, 0x27, 0x84, 0x12, 0xC6, 0x13, 0xC4, 0x27, 0x80, 0x92, 0x4A, 0x50, + 0x52, 0x10, 0x99, 0x3A, 0x4A, 0x5D, 0x3A, 0x41, 0x3A, 0x09, 0x54, 0x25, + 0x09, 0x27, 0x49, 0x4B, 0x42, 0x49, 0xE1, 0x24, 0x94, 0xFF, 0x00, 0xFF, + 0xD5, 0xF4, 0x14, 0x93, 0x95, 0x12, 0xA6, 0x60, 0x5D, 0x3A, 0x8C, 0xA7, + 0x49, 0x4C, 0x93, 0xA8, 0x85, 0x20, 0x92, 0x54, 0x9E, 0x13, 0xC2, 0x74, + 0x14, 0xB4, 0x25, 0x09, 0xE1, 0x24, 0x94, 0xC5, 0x22, 0x9D, 0x24, 0x94, + 0xC5, 0x34, 0x29, 0x42, 0x68, 0x45, 0x4C, 0x61, 0x31, 0x52, 0x29, 0x8A, + 0x4A, 0x62, 0x98, 0xA9, 0x14, 0xC5, 0x24, 0x30, 0x4C, 0xA4, 0x54, 0x4A, + 0x28, 0x52, 0x49, 0x26, 0x49, 0x4B, 0xA6, 0x49, 0x24, 0x94, 0xA4, 0x92, + 0x4D, 0x29, 0x29, 0x4A, 0x25, 0x39, 0x4C, 0x92, 0x16, 0x29, 0x92, 0x49, + 0x15, 0x2C, 0x92, 0x49, 0x24, 0xA5, 0x93, 0x42, 0x92, 0x64, 0x94, 0xB2, + 0x49, 0x14, 0x92, 0x42, 0xE9, 0x26, 0x4E, 0x92, 0x94, 0x92, 0x49, 0x24, + 0x95, 0x92, 0x49, 0x24, 0x90, 0xA4, 0xE9, 0x92, 0x45, 0x4B, 0xA7, 0x4C, + 0x92, 0x0A, 0x5D, 0x3A, 0x64, 0xE9, 0x25, 0x70, 0x9D, 0x32, 0x74, 0x94, + 0xB8, 0x53, 0xD1, 0x0C, 0x29, 0x02, 0x82, 0x59, 0x82, 0x12, 0x30, 0xA0, + 0x13, 0xA4, 0xA5, 0xD2, 0x49, 0x24, 0x94, 0xA0, 0x9D, 0x20, 0xA4, 0x92, + 0x94, 0x13, 0x84, 0xA1, 0x3C, 0x20, 0x95, 0x24, 0x94, 0x27, 0x49, 0x4B, + 0x24, 0x9D, 0x24, 0x92, 0xFF, 0x00, 0xFF, 0xD6, 0xF4, 0x24, 0xC5, 0x7C, + 0xD2, 0x92, 0x99, 0x81, 0xFA, 0x55, 0x20, 0xBE, 0x6A, 0x49, 0x25, 0x3F, + 0x4B, 0x85, 0x31, 0x0B, 0xE6, 0x54, 0x92, 0x4B, 0xF4, 0xE0, 0x4E, 0xBE, + 0x62, 0x49, 0x04, 0xBF, 0x4E, 0xA4, 0xBE, 0x62, 0x49, 0x25, 0x3F, 0x4E, + 0x24, 0xBE, 0x63, 0x49, 0x24, 0x3F, 0x4D, 0xA6, 0x2B, 0xE6, 0x54, 0x92, + 0x53, 0xF4, 0xC9, 0x4C, 0x57, 0xCC, 0xE9, 0x22, 0xA7, 0xE9, 0x62, 0x98, + 0xAF, 0x9A, 0x92, 0x49, 0x0F, 0xD2, 0x65, 0x31, 0x5F, 0x36, 0xA4, 0x8A, + 0x1F, 0xA4, 0x12, 0x5F, 0x37, 0xA4, 0x92, 0x9F, 0xA4, 0x0A, 0x45, 0x7C, + 0xDE, 0x92, 0x4A, 0x7E, 0x8F, 0x4C, 0xBE, 0x71, 0x49, 0x24, 0x3F, 0x46, + 0x94, 0xC5, 0x7C, 0xE6, 0x92, 0x2A, 0x7E, 0x8B, 0x49, 0x7C, 0xE8, 0x92, + 0x4A, 0x7E, 0x8A, 0x4E, 0xBE, 0x74, 0x49, 0x25, 0x3F, 0x45, 0x26, 0x5F, + 0x3B, 0x24, 0x92, 0x1F, 0xA2, 0x53, 0x2F, 0x9D, 0xD2, 0x49, 0x4F, 0xD1, + 0x09, 0xD7, 0xCE, 0xC9, 0x22, 0xA7, 0xE8, 0x94, 0x97, 0xCE, 0xC9, 0x20, + 0x97, 0xE8, 0x84, 0x97, 0xCE, 0xE9, 0x24, 0x87, 0xE8, 0x84, 0x97, 0xCE, + 0xE9, 0x22, 0xA7, 0xE8, 0x94, 0xEB, 0xE7, 0x54, 0x90, 0x53, 0xF4, 0x52, + 0x75, 0xF3, 0xA2, 0x49, 0x25, 0xFA, 0x31, 0x3A, 0xF9, 0xC9, 0x24, 0x94, + 0xFD, 0x1C, 0x90, 0x5F, 0x38, 0xA4, 0x92, 0x9F, 0xA3, 0xC2, 0x92, 0xF9, + 0xB9, 0x24, 0x12, 0xFD, 0x22, 0x9D, 0x7C, 0xDA, 0x92, 0x4A, 0x7E, 0x93, + 0x0A, 0x41, 0x7C, 0xD4, 0x92, 0x09, 0x7E, 0x96, 0x4E, 0xBE, 0x68, 0x49, + 0x25, 0x3F, 0x4B, 0xA4, 0xBE, 0x68, 0x49, 0x24, 0xBF, 0x4B, 0xA4, 0xBE, + 0x68, 0x49, 0x25, 0x3F, 0xFF, 0xD9, +}; + +#define IMAGE_LENGTH 0x000049F2 + +char * get_clouds(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(IMAGE_LENGTH); + memcpy(ptr, Clouds, IMAGE_LENGTH); + *Len = IMAGE_LENGTH; + + return ptr; +} + +char * get_ais_blue(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisblue_png_len); + memcpy(ptr, aisblue_png, aisblue_png_len); + *Len = IMAGE_LENGTH; + + return ptr; +} + +char * get_ais_navaid(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisnavaid_png_len); + memcpy(ptr, aisnavaid_png, aisnavaid_png_len); + *Len = aisnavaid_png_len; + + return ptr; +} + +char * get_ais_green(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisgreen_png_len); + memcpy(ptr, aisgreen_png, aisgreen_png_len); + *Len = aisgreen_png_len; + + return ptr; +} + +char * get_ais_red(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aisred_png_len); + memcpy(ptr, aisred_png, aisred_png_len); + *Len = aisred_png_len; + + return ptr; +} + +char * get_ais_white(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aiswhite_png_len); + memcpy(ptr, aiswhite_png, aiswhite_png_len); + *Len = aiswhite_png_len; + + return ptr; +} + +char * get_ais_trans(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(aistrans_png_len); + memcpy(ptr, aistrans_png, aistrans_png_len); + *Len = aistrans_png_len; + + return ptr; +} + + +char * get_greenplane(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(greenplane_png_len); + memcpy(ptr, greenplane_png, greenplane_png_len); + *Len = greenplane_png_len; + + return ptr; +} + +char * get_yellowplane(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(yellowplane_png_len); + memcpy(ptr, yellowplane_png, yellowplane_png_len); + *Len = yellowplane_png_len; + + return ptr; +} + +char * get_helicopter(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(helicopter_png_len); + memcpy(ptr, helicopter_png, helicopter_png_len); + *Len = helicopter_png_len; + + return ptr; +} + + +char * get_plane(int * Len) +{ + unsigned char * ptr; + + ptr = malloc(plane_png_len); + memcpy(ptr, plane_png, plane_png_len); + *Len = plane_png_len; + + return ptr; +} + + + + +char * get_aprs() +{ + char Msg[] = + +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"G8BPQ APRS Display\n" +"\n" + +"\n" +"\n" + + +//"\n" +//"\n" +"\n" +"\n" +"\n" +"\n" +" \n" +"\n" +"\n" +"\n" +"\n" +"\n" +"
\n" +"\n" +""; + + + return _strdup(Msg);; +} + + +char * get_rotatedMarker() +{ + char Msg[] = + +"(function() {\n" +" // save these original methods before they are overwritten\n" +" var proto_initIcon = L.Marker.prototype._initIcon;\n" +" var proto_setPos = L.Marker.prototype._setPos;\n" +"\n" +" var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');\n" +"\n" +" L.Marker.addInitHook(function () {\n" +" var iconOptions = this.options.icon && this.options.icon.options;\n" +" var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;\n" +" if (iconAnchor) {\n" +" iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');\n" +" }\n" +" this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;\n" +" this.options.rotationAngle = this.options.rotationAngle || 0;\n" +"\n" +" // Ensure marker keeps rotated during dragging\n" +" this.on('drag', function(e) { e.target._applyRotation(); });\n" +" });\n" +"\n" +" L.Marker.include({\n" +" _initIcon: function() {\n" +" proto_initIcon.call(this);\n" +" },\n" +"\n" +" _setPos: function (pos) {\n" +" proto_setPos.call(this, pos);\n" +" this._applyRotation();\n" +" },\n" +"\n" +" _applyRotation: function () {\n" +" if(this.options.rotationAngle) {\n" +" this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;\n" +"\n" +" if(oldIE) {\n" +" // for IE 9, use the 2D rotation\n" +" this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';\n" +" } else {\n" +" // for modern browsers, prefer the 3D accelerated version\n" +" this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';\n" +" }\n" +" }\n" +" },\n" +"\n" +" setRotationAngle: function(angle) {\n" +" this.options.rotationAngle = angle;\n" +" this.update();\n" +" return this;\n" +" },\n" +"\n" +" setRotationOrigin: function(origin) {\n" +" this.options.rotationOrigin = origin;\n" +" this.update();\n" +" return this;\n" +" }\n" +" });\n" +"})();"; + + + return _strdup(Msg);; +} + + + +char * GetStandardPage(char * FN, int * Len) +{ + if (_stricmp(FN, "aprs.html") == 0) + return get_aprs(); + + if (_stricmp(FN, "leaflet.rotatedMarker.js") == 0) + return get_rotatedMarker(); + + if (_stricmp(FN, "info_call.html") == 0) + return get_info_call(); + + if (_stricmp(FN, "infomobile_call.html") == 0) + return get_infomobile_call(); + + if (_stricmp(FN, "infoobj_call.html") == 0) + return get_infoobj_call(); + + if (_stricmp(FN, "infowx_call.html") == 0) + return get_infowx_call(); + + if (_stricmp(FN, "all.html") == 0) + return get_all(); + + if (_stricmp(FN, "allrf.html") == 0) + return get_allrf(); + + if (_stricmp(FN, "wxall.html") == 0) + return get_wxall(); + + if (_stricmp(FN, "wxrf.html") == 0) + return get_wxrf(); + + if (_stricmp(FN, "obj.html") == 0) + return get_obj(); + + if (_stricmp(FN, "objrf.html") == 0) + return get_objrf(); + + if (_stricmp(FN, "mobilesall.html") == 0) + return get_mobileall(); + + if (_stricmp(FN, "mobilesrf.html") == 0) + return get_mobilesrf(); + + if (_stricmp(FN, "info.html") == 0) + return get_info(); + + if (_stricmp(FN, "noinfo.html") == 0) + return get_noinfo(); + + if (_stricmp(FN, "favicion.ico") == 0) + return get_favicon(Len); + + if (_stricmp(FN, "Images/clouds.jpg") == 0) + return get_clouds(Len); + + if (_stricmp(FN, "aisnavaid.png") == 0) + return get_ais_navaid(Len); + + if (_stricmp(FN, "aisgreen.png") == 0) + return get_ais_green(Len); + + if (_stricmp(FN, "aisblue.png") == 0) + return get_ais_blue(Len); + + if (_stricmp(FN, "aisred.png") == 0) + return get_ais_red(Len); + + if (_stricmp(FN, "aistrans.png") == 0) + return get_ais_trans(Len); + + if (_stricmp(FN, "aiswhite.png") == 0) + return get_ais_white(Len); + + if (_stricmp(FN, "greenplane.png") == 0) + return get_greenplane(Len); + + if (_stricmp(FN, "yellowplane.png") == 0) + return get_yellowplane(Len); + + if (_stricmp(FN, "plane.png") == 0) + return get_plane(Len); + + if (_stricmp(FN, "helicopter.png") == 0) + return get_helicopter(Len); + + return 0; +} + diff --git a/ARDOP.c b/ARDOP.c new file mode 100644 index 0000000..431196f --- /dev/null +++ b/ARDOP.c @@ -0,0 +1,6370 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use ARDOP Virtual TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifdef WIN32 +//#include +#else + +// For serial over i2c support + +#ifdef MACBPQ +#define NOI2C +#endif + +#ifdef FREEBSD +#define NOI2C +#endif + +#ifndef NOI2C +#include "i2c-dev.h" +#endif +#endif + +#include "CHeaders.h" + + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define APMaxStreams 10 // First is used for ARDOP, even though ARDOP uses channel 31 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len); +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID ARDOPSendPktCommand(struct TNCINFO * TNC, int Stream, char * Buff); +VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length); +void ARDOPSCSCheckRX(struct TNCINFO * TNC); +VOID ARDOPSCSPoll(struct TNCINFO * TNC); +VOID ARDOPDoTNCReinit(struct TNCINFO * TNC); +int SerialGetTCPMessage(struct TNCINFO * TNC, unsigned char * Buffer, int Len); +int SerialConnecttoTCP(struct TNCINFO * TNC); +int ARDOPWriteCommBlock(struct TNCINFO * TNC); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +BOOL WriteCommBlock(struct TNCINFO * TNC); +VOID AddVirtualKISSPort(struct TNCINFO * TNC, int Port, char * buf); +int ConfigVirtualKISSPort(struct TNCINFO * TNC, char * Cmd); +void ProcessKISSBytes(struct TNCINFO * TNC, UCHAR * Data, int Len); +void ProcessKISSPacket(struct TNCINFO * TNC, UCHAR * KISSBuffer, int Len); +int ARDOPProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen); +int ConnecttoARDOP(struct TNCINFO * TNC); +int standardParams(struct TNCINFO * TNC, char * buf); + +#ifndef LINBPQ +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="ARDOPSTATUS"; +static char WindowTitle[] = "ARDOP"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + + +BOOL ARDOPStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + if (TNC->hDevice) + { + CloseCOMPort(TNC->hDevice); + TNC->hDevice = 0; + } + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + +BOOL ARDOPStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC->ARDOPCommsMode == 'T') + { + ConnecttoARDOP(TNC); + TNC->lasttime = time(NULL);; + } + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + +int GenCRC16(unsigned char * Data, unsigned short length) +{ + // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF + // intSeed is the seed value for the shift register and must be in the range 0-&HFFFF + + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length); j++) + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + return intRegister; +} + +BOOL checkcrc16(unsigned char * Data, unsigned short length) +{ + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length - 2); j++) // ' 2 bytes short of data length + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + if (Data[length - 2] == intRegister >> 8) + if (Data[length - 1] == (intRegister & 0xFF)) + return TRUE; + + return FALSE; +} + + +// Logging Interface. Used for Log over Serial Mode + +BOOL ARDOPOpenLogFiles(struct TNCINFO * TNC) +{ + UCHAR FN[MAX_PATH]; + + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + strlop(TNC->LogPath, 13); + strlop(TNC->LogPath, 32); + + sprintf(FN,"%s/ARDOPDebugLog_%02d%02d_%d.txt", TNC->LogPath, tm->tm_mon + 1, tm->tm_mday, TNC->Port); + TNC->DebugHandle = fopen(FN, "ab"); + sprintf(FN,"%s/ARDOPLog_%02d%02d_%d.txt", TNC->LogPath, tm->tm_mon + 1, tm->tm_mday, TNC->Port); + TNC->LogHandle = fopen(FN, "ab"); + + return (TNC->LogHandle != NULL); +} + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +void SendARDOPorPacketData(struct TNCINFO * TNC, int Stream, UCHAR * Buff, int txlen) +{ + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + ARDOPSendData(TNC, Buff, txlen); + STREAM->BytesTXed += txlen; + WritetoTrace(TNC, Buff, txlen); + } + else + { + // Packet. Only works over Serial + + PMSGWITHLEN buffptr; + UCHAR * buffp; + + if (TNC->ARDOPCommsMode == 'T') + return; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = txlen + 1; + + buffp = &buffptr->Data[0]; + buffp[0] = 0; // CMD/Data Flag on front + + memcpy(buffp +1, Buff, txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + } +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + char * PktPort = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + // Must start ADDR or SERIAL + + ptr = strtok(NULL, " \t\n\r"); + BPQport = Port; + p_ipad = ptr; + + if (_stricmp(buf, "ADDR") == 0 || _stricmp(buf, "TCPSERIAL") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + PktPort = strlop(p_port, '/'); + + if (PktPort) + TNC->PacketPort = atoi(PktPort); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + if (buf[0] == 'A') + TNC->ARDOPCommsMode = 'T'; // TCP + else + TNC->ARDOPCommsMode = 'E'; // TCPSERIAL (ESP01) + } + else if (_stricmp(buf, "SERIAL") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->ARDOPSerialPort = _strdup(p_ipad); + TNC->ARDOPSerialSpeed = atoi(p_port); + + TNC->ARDOPCommsMode = 'S'; + } + else if (_stricmp(buf, "I2C") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->ARDOPSerialPort = _strdup(p_ipad); + TNC->ARDOPSerialSpeed = atoi(p_port); + + TNC->ARDOPCommsMode = 'I'; + } + else + return FALSE; // Must start with ADDR + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + TNC->MaxConReq = 10; // Default + TNC->OldMode = FALSE; // Default + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + + if (_memicmp(buf, "PACKETCHANNELS", 14) == 0) // Packet Channels + TNC->PacketChannels = atoi(&buf[14]); + else + if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else + if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + if (_memicmp(buf, "ENABLEPACKET", 12) == 0) + { + if (TNC->PacketChannels == 0) + TNC->PacketChannels = 5; + // AddVirtualKISSPort(TNC, Port, buf); + } + +// else if (_memicmp(buf, "PAC ", 4) == 0 && _memicmp(buf, "PAC MODE", 8) != 0) +// { + // PAC MODE goes to TNC, others are parsed locally +// +// ConfigVirtualKISSPort(TNC, buf); +// } + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + + + return (TRUE); +} + + +void ARDOPThread(struct TNCINFO * TNC); +VOID ARDOPProcessDataSocketData(int port); +int ConnecttoARDOP(); +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ARDOPReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +unsigned short int compute_crc(unsigned char *buf,int len); + +VOID ARDOPSendPktCommand(struct TNCINFO * TNC, int Stream, char * Buff) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + UCHAR Encoded[256]; + + if (Stream == 0) + { + if (Buff[0] == 0) // Terminal Keepalive? + return; + } + else + { + if (Buff[1] == 0) // Terminal Keepalive? + return; + } + + if (TNC->PacketSock) // Packet Data over separate TCP Connectoion? + { + // Chan, Cmd/Data, Len-1 + + int SentLen; + + EncLen = sprintf(Encoded, "%c%c%c%s\r", Buff[0], 1, (int)strlen(Buff) -2, &Buff[1]); + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + return; + + } + + EncLen = sprintf(Encoded, "%s\r", Buff); + SendToTNC(TNC, Stream, Encoded, EncLen); + + return; +} + + +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue) +{ + // Encode and send to TNC. May be TCP or Serial + + // Command Formst is C:TEXT + + int i, EncLen; + UCHAR Encoded[260]; // could get 256 byte packet + + if (Buff[0] == 0) // Terminal Keepalive? + return; + + EncLen = sprintf(Encoded, "%s\r", Buff); + + // it is possible for binary data to be dumped into the command + // handler if a session disconnects in middle of transfer + + for (i = 0; i < EncLen; i++) + { + if (Encoded[i] > 0x7f) + return; + } + + SendToTNC(TNC, 12, Encoded, EncLen); // Use streams 12 aad 13 for Host Mode Schannels 32 and 33 + return; +} + +VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + int SentLen; + + if (TNC->hDevice || (TNC->ARDOPCommsMode == 'E' && TNC->TCPSock)) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return; + } + + if(TNC->ARDOPCommsMode == 'T' && TNC->TCPSock) + { + SentLen = send(TNC->TCPSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTED = FALSE; + return; + } + } +} + +VOID SendDataToTNC(struct TNCINFO * TNC, int Streem , UCHAR * Encoded, int EncLen) +{ + int SentLen; + + if (TNC->hDevice || (TNC->ARDOPCommsMode == 'E' && TNC->TCPSock)) + { + // Serial mode. Queue to Hostmode driver + + int Stream = 13; // use 12 and 13 for scs channels 32 and 33 + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return; + } + + if(TNC->TCPDataSock) + { + SentLen = send(TNC->TCPDataSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + // WINMOR doesn't seem to recover from a blocked write. For now just reset + +// if (bytes == SOCKET_ERROR) +// { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + +// if (winerr != WSAEWOULDBLOCK) +// { + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + TNC->CONNECTED = FALSE; + return; + } + } +} + +int ARDOPSenPktdData(struct TNCINFO * TNC, int Stream, char * Buff, int Len) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + + UCHAR Msg[400]; + UCHAR * Encoded = Msg; + + *(Encoded++) = Len >> 8; + *(Encoded++) = Len & 0xff; + + memcpy(Encoded, Buff, Len); + EncLen = Len + 2; + + SendDataToTNC(TNC, Stream, Msg, EncLen); + return Len; +} + + + +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + + UCHAR Msg[400]; + UCHAR * Encoded = Msg; + + *(Encoded++) = Len >> 8; + *(Encoded++) = Len & 0xff; + + memcpy(Encoded, Buff, Len); + + EncLen = Len + 2; + + SendDataToTNC(TNC, 13, Msg, EncLen); + return Len; +} + + + +VOID ARDOPChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// ARDOPSendCommand(TNC, "CODEC FALSE"); + + datalen = sprintf(TXMsg, "MYCALL %s", Call); + ARDOPSendCommand(TNC, TXMsg, TRUE); + +// ARDOPSendCommand(TNC, "CODEC TRUE"); +// TNC->StartSent = TRUE; + +// ARDOPSendCommand(TNC, "MYCALL", TRUE); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen=0; + size_t Param; + int Stream = 0; + HKEY hKey=0; + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + + switch (fn) + { + case 7: + + // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + STREAM->Disconnecting = TRUE; + } + } + + if (TNC->ARDOPCommsMode != 'T') // S I or E + { + ARDOPSCSCheckRX(TNC); + ARDOPSCSPoll(TNC); + } + + return 0; + + case 1: // poll + + // If not using serial interface, Rig Contol Frames are sent as + // ARDOP COmmand Frames. These are hex encoded + + if (TNC->ARDOPCommsMode == 'T' && TNC->BPQtoRadio_Q) + { + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoRadio_Q); + + if (TNC->CONNECTED) + { + int len = (int)buffptr->Len; + UCHAR * ptr = &buffptr->Data[0]; + char RigCommand[256] = "RADIOHEX "; + char * ptr2 = &RigCommand[9] ; + int i, j; + + if (len < 120) + { + while (len--) + { + i = *(ptr++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(ptr2++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(ptr2++) = j; + } + ARDOPSendCommand(TNC, RigCommand, FALSE); + } + } + ReleaseBuffer(buffptr); + + } + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } + + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + *ptr++ = '^'; // delimit fram ewith ^ + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '>'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '|'; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + memcpy(ptr, Buffer, datalen); + ptr += datalen; + *ptr++ = '^'; // delimit fram ewith ^ + + ARDOPSendData(TNC, FECMsg, (int)(ptr - FECMsg)); + TNC->FECPending = 1; + + ReleaseBuffer((UINT *)buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + ARDOPSendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], (int)strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(&buffptr->Data[0], "Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + fn =fn; //ARDOPSendCommand(TNC, "MODE", TRUE); + else + { +// if (time(NULL) - TNC->WinmorRestartCodecTimer > 300) // 5 mins +// { +// ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); +// ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); +// } +// else + ARDOPSendCommand(TNC, "STATE", TRUE); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } + } + + // FECPending can be set if not in FEC Mode (eg beacon) + + if (TNC->FECPending) // Check if FEC Send needed + { + if (TNC->Streams[0].BytesOutstanding) //Wait for data to be queued (async data session) + { + if (TNC->Busy == 0 && TNC->GavePermission == 0) + { + TNC->FECPending = 0; + ARDOPSendCommand(TNC,"FECSEND TRUE", TRUE); + } + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + KillTNC(TNC); + + RestartTNC(TNC); + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath && TNC->CONNECTED && 0) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + if (Stream == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", Stream); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("ARDOP New Attach Stream %d DEDStream %d", Stream, STREAM->DEDStream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + if (Stream == 0) + { + // If Pactor, stop scanning and take out of listen mode. + // if (Stream == 0) + // STREAM->DEDStream = 31; // Pactor + + // Stop Listening, and set MYCALL to user's call + + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + else + { + // Packet Connect + + } + } + + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + if (TNC->ARDOPCommsMode == 'T' && TNC->PortRecord->PORTCONTROL.PortStopped == 0) + ConnecttoARDOP(TNC); + TNC->lasttime = ltime; + } + } + + // See if any frames for this port + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->ARDOPCommsMode == 'T') + { + // For serial mode packets are taken from the queue by ARDOPSCSPoll + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->BytesTXed += txlen; + + if (Stream == 0) + { + bytes=ARDOPSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + else + { + if (TNC->PacketSock) + { + // Using Packet over TCP) + // Chan, Cmd/Data, Len-1 + + UCHAR Encoded[280]; + int EncLen; + int SentLen; + + EncLen = sprintf(Encoded, "%c%c%c%s\r", Stream, 0, txlen - 1, data); + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + } + } + ReleaseBuffer(buffptr); + } + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "No Connection to ARDOP TNC\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + if (Stream == 0) + { + bytes=ARDOPSendData(TNC, &buff->L2DATA[0], txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], txlen); + } + else + { + // Packet. Only works over Serial + + PMSGWITHLEN buffptr; + UCHAR * buffp; + + if (TNC->PacketSock) + { + // Using Packet over TCP) + // Chan, Cmd/Data, Len-1 + + UCHAR Encoded[280]; + int EncLen; + int SentLen; + + Encoded[0] = Stream; + Encoded[1] = 0; // Data + Encoded[2] = txlen - 1; + + memcpy(&Encoded[3], &buff->L2DATA[0], txlen); + + EncLen = txlen + 3; + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + // We should increment outstanding here as TCP interface can fill buffer + // very quickly + + TNC->Streams[Stream].BytesOutstanding += txlen; + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + return 0; + } + + if (TNC->ARDOPCommsMode == 'T') + return 0; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 0; // No buffers, so ignore + + buffptr->Len = txlen + 1; + buffp = &buffptr->Data[0]; + + buffp[0] = 0; // CMD/Data Flag on front + + memcpy(buffp + 1, &buff->L2DATA[0], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return 0; + } + } + else + { + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA[0]); + + ARDOPSendData(TNC, Buffer, len); + TNC->FECPending = 1; + + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + +// if (_memicmp(&buff[8], "PAC ", 4) == 0 && _memicmp(&buff[8], "PAC MODE", 8) != 0) +// { + // PAC MODE goes to TNC, others are parsed locally + +// buff[7 + txlen] = 0; +// ConfigVirtualKISSPort(TNC, &buff[8]); +// return 1; +// } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + + if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) + { + if (buff->L2DATA[9] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + // Limit connects + + int tries = atoi(&buff->L2DATA[10]); + if (tries > 10) tries = 10; + + TNC->MaxConReq = tries; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "ARQBW ", 6) == 0) + TNC->WinmorCurrentMode = 0; // So scanner will set next value + + if (_memicmp(&buff->L2DATA[0], "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "FEC\r", 4) == 0 || _memicmp(&buff->L2DATA[0], "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; +// ARDOPSendCommand(TNC,"FECRCV TRUE"); + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, reject + + if (TNC->OverrideBusy == 0) + { + // Reject + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Ping blocked by Busy\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + TNC->OverrideBusy = FALSE; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + if (Stream == 0) + { + sprintf(Connect, "ARQCALL %s %d", &buff->L2DATA[2], TNC->MaxConReq); + + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + ARDOPSendCommand(TNC, Connect, TRUE); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + // Packet Connect + + sprintf(Connect, "%cPKTCALL %s %s", Stream, &buff->L2DATA[2], STREAM->MyCall); + ARDOPSendPktCommand(TNC, Stream, Connect); + } + + STREAM->Connecting = TRUE; + return 0; + + } + buff->L2DATA[txlen - 1] = 0; // Remove CR + ARDOPSendCommand(TNC, &buff->L2DATA[0], TRUE); + } + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + //if (!(TNC->ARDOPCommsMode == 'T')) + //{ + // // if serial mode must check buffer space + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + if (TNC->Mode == 'O') // OFDM version has more buffer space + { + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else if (TNC->Mode == '3') // ARDOP3 has a bit more buffer space + { + if (Queued > 4 || Outstanding > 5000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else + { + if (Queued > 4 || Outstanding > 2000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + } + if (TNC->Streams[Stream].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { + if (TNC->WeStartedTNC) + { + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CLOSE", FALSE); + FreeSemaphore(&Semaphore); + Sleep(100); + } + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (TNC->ARDOPCommsMode == 'T') // TCP Mode + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + } + else + { + // Serial Modes + + if (!TNC->HostMode) + return 0; // No connection so no interlock + } + + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->ARDOPMode[0] == 0) + { + // Not specified, so no change from previous + + return 0; + } + + if (strcmp(Scan->ARDOPMode, TNC->ARDOPCurrentMode) != 0) + { + // Mode changed + + char CMD[32]; + + if (TNC->ARDOPCurrentMode[0] == 'S') // Skip + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + +// Debugprintf("ARDOPMODE %s", Scan->ARDOPMode); + + memcpy(TNC->ARDOPCurrentMode, Scan->ARDOPMode, 6); + + if (Scan->ARDOPMode[0] == 'S') // SKIP - Dont Allow Connects + { + if (TNC->ARDOPCurrentMode[0] != 'S') + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->ARDOPCurrentMode[0] = 'S'; + } + + TNC->WL2KMode = 0; + return 0; + } + + if (strchr(Scan->ARDOPMode, 'F')) + sprintf(CMD, "ARQBW %sORCED", Scan->ARDOPMode); + else if (strchr(Scan->ARDOPMode, 'M')) + sprintf(CMD, "ARQBW %sAX", Scan->ARDOPMode); + else + sprintf(CMD, "ARQBW %s", Scan->ARDOPMode); // ARDOPOFDM doesn't use MAX/FORCED + + ARDOPSendCommand(TNC, CMD, TRUE); + + return 0; + } + return 0; + } + return 0; +} + +VOID ARDOPReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + ARDOPChangeMYC(TNC, TNC->NodeCall); + + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Start Scanner + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command(-1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID ARDOPSuspendPort(struct TNCINFO * TNC) +{ + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); +} + +VOID ARDOPReleasePort(struct TNCINFO * TNC) +{ + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); +} + +extern char WebProcTemplate[]; +extern char sliderBit[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "ARDOP Status", "ARDOP Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE, TNC->WEB_LEVELS); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s   %s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * ARDOPExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + int Stream; + + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + if (TNC->LogPath) + ARDOPOpenLogFiles(TNC); + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(16384); + TNC->ARDOPAPRS = zalloc(512); + TNC->ARDOPAPRSLen = 0; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->Hardware = H_ARDOP; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + for (Stream = 1; Stream <= APMaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; + } + + if (TNC->PacketChannels > APMaxStreams) + TNC->PacketChannels = APMaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = ARDOPSuspendPort; + TNC->ReleasePortProc = ARDOPReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = ARDOPStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = ARDOPStopPort; + + TNC->ModemCentre = 1500; // ARDOP is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "INITIALIZE\r"); + strcat(TempScript, "VERSION\r"); + strcat(TempScript, "CWID False\r"); + strcat(TempScript, "PROTOCOLMODE ARQ\r"); + strcat(TempScript, "ARQTIMEOUT 90\r"); +// strcat(TempScript, "ROBUST False\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + +// strcat(TNC->InitScript,"FECRCV True\r"); +// strcat(TNC->InitScript,"AUTOBREAK True\r"); + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); +// strcat(TNC->InitScript,"PROCESSID\r"); +// strcat(TNC->InitScript,"CODEC TRUE\r"); +// strcat(TNC->InitScript,"LISTEN TRUE\r"); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + *ptr++ = ','; + *ptr = 0; + } + strcat(Aux, Appl); + } + } + + if (strlen(Aux) > 8) + { + Aux[strlen(Aux) - 1] = '\r'; + strcat(TNC->InitScript, Aux); + } + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_LEVELS =zalloc(32); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,72,82,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_LEVELS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 200,72,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,120,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,120,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,120,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + + if (TNC->ARDOPCommsMode == 'T') + { + Consoleprintf("ARDOP Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + ConnecttoARDOP(TNC); + } + + else if (TNC->ARDOPCommsMode == 'E') + { + Consoleprintf("ARDOP TCPSerial %s:%d", TNC->HostName, htons(TNC->destaddr.sin_port)); + SerialConnecttoTCP(TNC); + } + + else if (TNC->ARDOPCommsMode == 'S') + { + TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(TNC->ARDOPSerialPort); // for common routines + Consoleprintf("ARDOP Serial %s", TNC->ARDOPSerialPort); + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, FALSE); + } + else if (TNC->ARDOPCommsMode == 'I') + { +#ifdef WIN32 + sprintf(Msg,"ARDOP I2C is not supported on WIN32 systems\n"); + WritetoConsoleLocal(Msg); +#else +#ifdef NOI2C + sprintf(Msg,"I2C is not supported on this systems\n"); + WritetoConsoleLocal(Msg); +#else + char i2cname[30]; + int fd; + int retval; + + if (strlen(TNC->ARDOPSerialPort) < 3) + { + sprintf(i2cname, "/dev/i2c-%s", TNC->ARDOPSerialPort); + TNC->ARDOPSerialPort = _strdup(i2cname); + } + else + strcpy(i2cname, TNC->ARDOPSerialPort); + + TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(i2cname); // for common routines + + Consoleprintf("ARDOP I2C Bus %s Addr %d ", i2cname, TNC->ARDOPSerialSpeed); + + // Open and configure the i2c interface + + fd = TNC->hDevice = open(i2cname, O_RDWR); + + if (fd < 0) + printf("Cannot find i2c bus %s \n", i2cname); + else + { + retval = ioctl(TNC->hDevice, I2C_SLAVE, TNC->ARDOPSerialSpeed); + + if(retval == -1) + printf("Cannot open i2c device %x\n", TNC->ARDOPSerialSpeed); + + ioctl(TNC->hDevice, I2C_TIMEOUT, 10); //100 mS + } +#endif +#endif + } + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +VOID TNCLost(struct TNCINFO * TNC) +{ + int Stream = 0; + struct STREAMINFO * STREAM; + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + if (TNC->PacketSock) + closesocket(TNC->PacketSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->PacketSock = 0; + TNC->CONNECTED = FALSE; + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + STREAM->BytesOutstanding = 0; + + if (Stream == 0) + { + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - STREAM->BytesOutstanding, STREAM->BytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + + if (STREAM->Attached) + { + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + } + } +} + + +int ConnecttoARDOP(struct TNCINFO * TNC) +{ + _beginthread(ARDOPThread, 0, (void *)TNC); + + return 0; +} + +VOID ARDOPThread(struct TNCINFO * TNC) +{ + // Opens sockets and looks for data on control and data sockets. + + // Socket may be TCP/IP or Serial + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1, * ptr2; + PMSGWITHLEN buffptr; + char Cmd[64]; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + } +#endif + + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + if (TNC->TCPDataSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPDataSock); + closesocket(TNC->TCPDataSock); + } + + if (TNC->PacketSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->PacketSock); + closesocket(TNC->PacketSock); + } + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->PacketSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d Port %d\n", + WSAGetLastError(), htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + // Connect Data Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (TNC->PacketPort) + { + struct sockaddr_in destaddr; + + TNC->PacketSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->PacketSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Packet socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + TNC->PacketSock = 0; + } + else + { + setsockopt(TNC->PacketSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + // setsockopt(TNC->PacketSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(TNC->PacketPort); + + if (connect(TNC->PacketSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // Connected successful + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Packet socket - error code = %d\r\n", err); + WritetoConsole(Msg); + TNC->Alerted = TRUE; + } + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + } + } + + +#ifndef LINBPQ +// FreeSemaphore(&Semaphore); + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // ARDOP needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + buffptr = (PMSGWITHLEN)GetBuff(); + buffptr->Len = 0; + C_Q_ADD(&TNC->BPQtoWINMOR_Q, buffptr); + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + if (ptr2) + *(ptr2) = 0; + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATETIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd, "DATETIME %02d %02d %02d %02d %02d %02d", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + ptr1 = Cmd; + } + + ARDOPSendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to ARDOP TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to ARDOP TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->PacketSock) + { + FD_SET(TNC->PacketSock,&errorfs); + FD_SET(TNC->PacketSock,&readfs); + } + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + if (TNC->PacketSock) + ret = select((int)TNC->PacketSock + 1, &readfs, NULL, &errorfs, &timeout); + else + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("ARDOP Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->PacketSock, &readfs)) + { + int InputLen, Used; + UCHAR Buffer[4096]; + + InputLen = recv(TNC->PacketSock, Buffer, 4096, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + TNCLost(TNC); + return; + } + + // Could be more than one frame in buffer + + while (InputLen > 0) + { + GetSemaphore(&Semaphore, 52); + Used = ARDOPProcessDEDFrame(TNC, Buffer, InputLen); + FreeSemaphore(&Semaphore); + + if (Used == 0) + break; // need to check + + InputLen -= Used; + + if (InputLen > 0) + memmove(Buffer, &Buffer[Used], InputLen); + + } + + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + if (FD_ISSET(TNC->PacketSock, &errorfs)) + { + sprintf(Msg, "ARDOP Packet Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + sprintf(Msg, "ARDOP No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CODEC FALSE", FALSE); + FreeSemaphore(&Semaphore); + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + if (TNC->PID && TNC->WeStartedTNC) + { +// KillTNC(TNC); + } + return; + } + } + sprintf(Msg, "ARDOP Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +#ifndef LINBPQ + +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"ARDOP_Win ", 10) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + sprintf (wtext, "ARDOP Virtual TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 1] = 0; // Remove CR + + if (_memicmp(Buffer, "RDY", 3) == 0) + return; // RDY not used now + + if (_memicmp(Buffer, "RADIOHEX ", 9) == 0) + { + // Parameter is block to send to radio, in hex + + char c; + int val; + char * ptr1 = &Buffer[9]; + UCHAR * ptr2 = Buffer; + PMSGWITHLEN buffptr; + int Len; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == NULL) + return; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + *(ptr2) = 0; + + Len = (int)(ptr2 - Buffer); + + buffptr->Len = Len; + memcpy(&buffptr->Data[0], Buffer, Len); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + +// WriteCOMBlock(hRIGDevice, ptrParams, ptr2 - ptrParams); + return; + + } + + + if (_memicmp(Buffer, "INPUTPEAKS", 10) == 0) + { + sscanf(&Buffer[10], "%i %i", &TNC->InputLevelMin, &TNC->InputLevelMax); + sprintf(TNC->WEB_LEVELS, "Input peaks %s", &Buffer[10]); + MySetWindowText(TNC->xIDC_LEVELS, TNC->WEB_LEVELS); + return; // Response shouldn't go to user + } + + if (_memicmp(Buffer, "LISTEN NOW", 10) == 0) + return; // Response shouldn't go to user + + if (_memicmp(Buffer, "ARQCALL ", 7) == 0) + return; // Response shouldn't go to user + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + return; + } + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + TNC->PTTState = TRUE; + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->PTTState = FALSE; + 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 + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->Busy) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + TNC->ConnectPending = 6; // This comes before Pending + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 5); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + sscanf(&Buffer[7], "%d", &STREAM->BytesOutstanding); + + if (STREAM->BytesOutstanding == 0) + { + // all sent + + if (STREAM->Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 1; + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - STREAM->BytesOutstanding, STREAM->BytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Speed + + ptr = strchr(&Buffer[10], ' '); + if (ptr) + { + Speed = atoi(ptr); + + if (Speed == 200) + TNC->WL2KMode = 40; + else if (Speed == 500) + TNC->WL2KMode = 41; + else if (Speed == 1000) + TNC->WL2KMode = 42; + else if (Speed == 2000) + TNC->WL2KMode = 43; + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("ARDOP Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("ARDOP Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(&buffptr->Data[0], Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + int txlen = (int)(buffptr->Len); + SendARDOPorPacketData(TNC, 0, data, txlen); + } + + SendARDOPorPacketData(TNC, 0, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", Call); + + buffptr->Len = ReplyLen; + memcpy(&buffptr->Data[0], Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0 + || _memicmp(Buffer, "STATUS CONNECT TO", 17) == 0 + || _memicmp(Buffer, "STATUS ARQ TIMEOUT FROM PROTOCOL STATE", 24) == 0 +// || _memicmp(Buffer, "NEWSTATE DISC", 13) == 0 + || _memicmp(Buffer, "ABORT", 5) == 0) + + { + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "*** Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + if (TNC->RestartAfterFailure) + { + if (TNC->PID) + KillTNC(TNC); + + RestartTNC(TNC); + } + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session3 + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ARDOPReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + strcpy(TNC->WEB_MODE, &Buffer[5]); + MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); + return; + } + + if (_memicmp(Buffer, "STATUS ", 7) == 0) + { + return; + } + + if (_memicmp(Buffer, "RADIOMODELS", 11) == 0) + return; + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + return; + } + + // REJECTEDBW and REJECTEDBUSY are sent to both calling and called + + if (_memicmp(&Buffer[0], "REJECTEDBUSY", 12) == 0) + { + TNC->ConnectPending = FALSE; + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Connection to %s Rejected - Channel Busy\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + } + + if (_memicmp(&Buffer[0], "REJECTEDBW", 10) == 0) + { + TNC->ConnectPending = FALSE; + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Connection to %s Rejected - Incompatible Bandwidth\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + } + + + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0 + || _memicmp(&Buffer[0], "REJECTEDB", 9) == 0) //REJECTEDBUSY or REJECTEDBW + { + TNC->ConnectPending = FALSE; + return; + } + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); +// return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + TNC->WinmorRestartCodecTimer = time(NULL); + + MySetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "DISC", 4) == 0) + { + TNC->DiscPending = FALSE; + TNC->ConnectPending = FALSE; + + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + +// if (TNC->FEC1600) +// ARDOPSendCommand(TNC,"FECSEND 1600"); +// else +// ARDOPSendCommand(TNC,"FECSEND 500"); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(Buffer, "PING ", 5) == 0) + { + char Call[32]; + + // Make sure not Echoed PING + + // c:ping gm8bpq-1 5 + // c:PING GM8BPQ>GM8BPQ-1 15 98 + + if (strchr(Buffer, '>') == 0) // Echoed + return; + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release scanlock after another interval (to allow time for response to be sent) + // ?? use cancelpending TNC->ConnectPending = 1; + + + memcpy(Call, &Buffer[5], 20); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + + return; + } + + if (_memicmp(Buffer, "VERSION ", 8) == 0) + { + // If contains "OFDM" or "ARDOP3" increase data session busy level + + if (strstr(&Buffer[8], "OFDM")) + TNC->Mode = 'O'; + else if (strstr(&Buffer[8], "TNC_3")) + TNC->Mode = '3'; + else + TNC->Mode = 0; + } + + if (_memicmp(Buffer, "PINGACK ", 8) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + // Drop through to return touser + } + + if (_memicmp(Buffer, "CQ ", 3) == 0 && MsgLen > 10) + { + char Call[32]; + char * Loc; + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Update MH + { + memcpy(Call, &Buffer[3], 32); + Loc = strlop(Call, ' '); + strlop(Loc, ']'); + UpdateMHEx(TNC, Call, '!', 'I', &Loc[1], TRUE); + } + // Drop through to go to user if attached but not connected + + } + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected || TNC->Streams[0].Connecting) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} %s\r", Buffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + + // May get several messages per packet + // May get message split over packets + + // Data has a length field + // ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data” + // New standard doesnt have d: + + if (TNC->DataInputLen > 16000) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + // OFDM can return large packets (up to 10160) + + // in serial mode, data has been put in input buffer by comms code + + if (TNC->ARDOPCommsMode == 'T') + { + InputLen=recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], 16384 - TNC->DataInputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + TNCLost(TNC); + return; + } + + TNC->DataInputLen += InputLen; + } +loop: + + if (TNC->OldMode) + goto OldRX; + + else + { // No D: + + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[0] << 8) + TNC->ARDOPDataBuffer[1]; // HI First + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 2) + return; // Wait for more + + MsgLen = DataLen + 2; // Len + + memcpy(DataType, &TNC->ARDOPDataBuffer[2] , 3); + DataType[3] = 0; + + Data = &TNC->ARDOPDataBuffer[5]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->DataInputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + +OldRX: + + if (TNC->DataInputLen < 8) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPDataBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPDataBuffer[0] == 'd') + { + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[2] << 8) + TNC->ARDOPDataBuffer[3]; // HI First +// unsigned short CRC; + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 4) + return; // Wait for more + + MsgLen = DataLen + 4; // d: Len CRC + + memcpy(DataType, &TNC->ARDOPDataBuffer[4] , 3); + DataType[3] = 0; + Data = &TNC->ARDOPDataBuffer[7]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->DataInputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + else + // Duff - clear input buffer + TNC->DataInputLen = 0; + + } + return; +} + + + +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // May get several messages per packet + // May get message split over packets + + // Commands end with CR. + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + // in serial mode, data has been put in input buffer by comms code + + if (TNC->ARDOPCommsMode == 'T') + { + // I don't think it likely we will get packets this long, but be aware... + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + TNCLost(TNC); + return; + } + + TNC->InputLen += InputLen; + } + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR (no CRC in new version) + { + // Usual Case - single meg in buffer + + ARDOPProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + // buffer contains more that 1 message + + + MsgLen = TNC->InputLen - (int)(ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + ARDOPProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + return; +} + + + +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + PMSGWITHLEN buffptr; + + TNC->TimeSinceLast = 0; + + if (strcmp(Type, "IDF") == 0) + { + // Place ID frames in Monitor Window and MH + + char Call[20]; + char * Loc; + +// GM8BPQ-2:[IO68VL] +//ID:GM8BPQ-2 IO68VL : +// GM8BPQ-2:[IO68VL] +//ID:GM8BPQ-2 [IO68vl]: +//ID:HB9AVK JN47HG : + +// TX BPQ IDF GM8BPQ-2:[IO68VL] +// RX Rick IDF ID:GM8BPQ-2 [IO68vl]: + +// TX Rick IDF GM8BPQ-2:[IO68VL] +// RX BPQ IDF ID:GM8BPQ-2 IO68VL : + +//ID:GM8BPQ-2 [IO68vl] : + + Data[Length] = 0; + WritetoTrace(TNC, Data, Length); + + Debugprintf("ARDOP IDF %s", Data); + + // Loos like transmitted ID doesnt have ID: + + if (memcmp(Data, "ID:", 3) == 0) // These seem to be received ID's + { + memcpy(Call, &Data[3], 20); + Loc = strlop(Call, ' '); + strlop(Loc, ']'); + UpdateMHEx(TNC, Call, '!', 'I', &Loc[1], TRUE); + } + return; + } + + STREAM->BytesRXed += Length; + + Data[Length] = 0; + Debugprintf("ARDOP: RXD %d bytes", Length); + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - STREAM->BytesOutstanding, STREAM->BytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + + if (TNC->FECMode) + { + Length = (int)strlen(Data); + if (Data[Length - 1] == 10) + Data[Length - 1] = 13; + + } + + if (strcmp(Type, "FEC") == 0) + { + // May be an APRS Message + // These are delimired with ^ characters + // As they are likely to be split across + // FEC blocks they need to be recombined + + char * ptr = Data; + char * ptr1; + char * ptr2; + char c; + int Len = Length; + + Debugprintf(Data); + + if (*ptr == '^' || TNC->ARDOPAPRSLen) + { + // New Packet or continuation + + while (Len--) + { + c = *(ptr++); + if (c == '^') + { + // may be start or end + + Debugprintf("Start/end of beacon Len = %d", TNC->ARDOPAPRSLen); + + if (TNC->ARDOPAPRSLen == 0) + continue; // Start + + // Validate and Process Block + + Debugprintf("beacon %s", TNC->ARDOPAPRS); + + ptr1 = TNC->ARDOPAPRS; + ptr2 = strchr(ptr1, '>'); + + if (ptr2 && (ptr2 - ptr1) < 10) + { + // Could be APRS + +// if ((memcmp(ptr2 + 1, "AP", 2) == 0) || (memcmp(ptr2 + 1, "BE", 2) == 0)) + if (1) // People using other dests + { + int APLen; + + // assume it is + + char * ptr3 = strchr(ptr2, '|'); + struct _MESSAGE * buffptr; + + if (ptr3 == 0) + { + TNC->ARDOPAPRSLen = 0; + Debugprintf("no |"); + continue; + } + + buffptr = GetBuff(); + *(ptr3++) = 0; // Terminate TO call + + APLen = TNC->ARDOPAPRSLen - (int)(ptr3 - ptr1); + + TNC->ARDOPAPRSLen = 0; + + Debugprintf("Good APRS %d Left", Len); + + // Convert to ax.25 format + + if (buffptr == 0) + continue; // No buffers, so ignore + + buffptr->PORT = TNC->Port; + + ConvToAX25(ptr1, buffptr->ORIGIN); + ConvToAX25(ptr2 + 1, buffptr->DEST); + buffptr->ORIGIN[6] |= 1; // Set end of address + buffptr->CTL = 3; + buffptr->PID = 0xF0; + memcpy(buffptr->L2DATA, ptr3, APLen); + buffptr->LENGTH = 16 + MSGHDDRLEN + APLen; + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + } + else + { + Debugprintf("Not APRS"); + TNC->ARDOPAPRSLen = 0; // in case not aprs + } + } + else + { + Debugprintf("cant be APRS"); + TNC->ARDOPAPRSLen = 0; // in case not aprs + } + continue; + } + + // Normal Char + + TNC->ARDOPAPRS[TNC->ARDOPAPRSLen++] = c; + if (TNC->ARDOPAPRSLen == 512) + TNC->ARDOPAPRSLen = 0; + } + // End of packet. + + Debugprintf("End of Packet Len %d", TNC->ARDOPAPRSLen); + } + + // FEC but not APRS. Discard if connected + + if (TNC->Streams[0].Connected) + return; + } + + WritetoTrace(TNC, Data, Length); + + // We can get messages of form ARQ [ConReq2000M: GM8BPQ-2 > OE3FQU] + // Noe (V2) [ConReq2500 > G8XXX] + + // when not connected. + + if (TNC->Streams[0].Connected == FALSE) + { + if (strcmp(Type, "ARQ") == 0) + { + if (Data[1] == '[') + { + // Log to MH + + char Call[20]; + char * ptr; + + // Add a Newline for monitoring + + Data[Length++] = 13; + Data[Length] = 0; + + ptr = strchr(Data, ':'); + + if (ptr) + { + memcpy(Call, &ptr[2], 20); + strlop(Call, ' '); + UpdateMH(TNC, Call, '!', 'I'); + } + } + } + } + + if (TNC->Streams[0].Attached == 0) + return; + + // May need to fragment + + while (Length) + { + int Fraglen = Length; + + if (Length > PACLEN) + Fraglen = PACLEN; + + Length -= Fraglen; + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + memcpy(&buffptr->Data[0], Data, Fraglen); + + Data += Fraglen; + + buffptr->Len = Fraglen; + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + return; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + { + if (Stream == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", 1); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } + } +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + ARDOPSendCommand(TNC, "ABORT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", Stream); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + ARDOPReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } +} + +VOID ARDOPAbort(struct TNCINFO * TNC) +{ + ARDOPSendCommand(TNC, "ABORT", TRUE); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID ARDOPCRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + Msg[3] |= TNC->Toggle; + +// Debugprintf("ARDOP TX Toggle %x", TNC->Toggle); + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + TNC->TXLen = Len; + + Msg[0] = 170; + Msg[1] = 170; + + ARDOPWriteCommBlock(TNC); + + TNC->Retries = 5; +} + +VOID ARDOPExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x41; + TNC->TXBuffer[4] = 0x5; + memcpy(&TNC->TXBuffer[5], "JHOST0", 6); + + ARDOPCRCStuffAndSend(TNC, Poll, 11); + return; +} + + +VOID ARDOPDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + ARDOPExitHost(TNC); + TNC->Retries = 1; + + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + ARDOPDoTNCReinit(TNC); // See if worked + return; + } + + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +VOID ARDOPDoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + Poll[0] = 13; + Poll[1] = 0x1B; + TNC->TXLen = 2; + +// Debugprintf("Sending CR ESC, Mode %c", TNC->ARDOPCommsMode); + + if (TNC->ARDOPCommsMode == 'E') + { + if (TNC->TCPCONNECTED) + { + int SentLen = send(TNC->TCPSock, TNC->TXBuffer, TNC->TXLen, 0); + + if (SentLen != TNC->TXLen) + { + // ARDOP doesn't seem to recover from a blocked write. For now just reset + + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->TCPCONNECTED = FALSE; + } + } + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Timeout = 20; // 2 secs + TNC->Retries = 1; + return; + } + + if (TNC->hDevice == 0) // Dont try to init if device not open + { + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, TRUE); + + TNC->Timeout = 100; // 10 secs + TNC->Retries = 1; + return; + } + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + if (ARDOPWriteCommBlock(TNC) == FALSE) + { + if (TNC->hDevice) + { + Debugprintf("ARDOPWriteCommBlock Failed Mode %c", TNC->ARDOPCommsMode); + CloseCOMPort(TNC->hDevice); + } + if (TNC->ARDOPCommsMode == 'S') + { + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, TRUE); + } + else + { +#ifdef WIN32 +#else +#ifdef NOI2C +#else + char i2cname[30]; + int fd; + int retval; + + // Open and configure the i2c interface + + if (strlen(TNC->ARDOPSerialPort) < 3) + sprintf(i2cname, "/dev/i2c-%s", TNC->ARDOPSerialPort); + else + strcpy(i2cname, TNC->ARDOPSerialPort); + + fd = TNC->hDevice = open(i2cname, O_RDWR); + + if (fd < 0) + printf("Cannot find i2c bus %s\n", i2cname); + else + { + retval = ioctl(fd, I2C_SLAVE, TNC->ARDOPSerialSpeed); + + if(retval == -1) + printf("Cannot open i2c device %x\n", TNC->ARDOPSerialSpeed); + + ioctl(fd, I2C_TIMEOUT, 10); + } +#endif +#endif + } + } + TNC->Retries = 1; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + Debugprintf("DOTNCReinit Complete - Entering Hostmode"); + + TNC->TXBuffer[2] = 0; + TNC->Toggle = 0; + + memcpy(Poll, "JHOST4\r", 7); + + TNC->TXLen = 7; + ARDOPWriteCommBlock(TNC); + + // Timeout will enter host mode + + TNC->Timeout = 1; + TNC->Retries = 1; + TNC->Toggle = 0; + TNC->ReinitState = 3; // Set toggle force bit + TNC->OKToChangeFreq = 1; // In case failed whilst waiting for permission + TNC->CONNECTED = TRUE; + + return; + } +} + +VOID ARDOPProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + char * ptr1, * ptr2; + int len; + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + + // Send ARDOP to make sure TNC is in a known state + + strcpy(Poll, "ARDOP\r"); + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, Poll, 7); +// CloseLogFile(TNC->Port); + + TNC->TXLen = 6; + ARDOPWriteCommBlock(TNC); + + TNC->Timeout = 60; // 6 secs + + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + // Send INIT script + + ptr1 = &TNC->InitScript[0]; + +/* GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + void ** buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + FreeSemaphore(&Semaphore, 52); +*/ + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + + if (ptr2 == 0) + break; + + len = (int)(ptr2 - ptr1) + 1; + + memcpy(Poll, ptr1, len); + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATETIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + len = sprintf(Poll, "DATETIME %02d %02d %02d %02d %02d %02d\r", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100, + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + + // if RADIOPTTON ? or RADIOPTTOFF ? replace ? + // with correct string + + if (_memicmp(ptr1, "RADIOPTTOFF ?", 13) == 0) + { + int Len = TNC->RIG->PTTOffLen; + UCHAR * Cmd = TNC->RIG->PTTOff; + char Hex[256]; + char * hexptr = Hex; + int i, j; + while(Len--) + { + i = *(Cmd++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + } + *(hexptr++) = 0; + + len = sprintf(Poll, "RADIOPTTOFF %s\r", Hex); + } + + if (_memicmp(ptr1, "RADIOPTTON ?", 12) == 0) + { + int Len = TNC->RIG->PTTOnLen; + UCHAR * Cmd = TNC->RIG->PTTOn; + char Hex[256]; + char * hexptr = Hex; + int i, j; + while(Len--) + { + i = *(Cmd++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + } + *(hexptr++) = 0; + + len = sprintf(Poll, "RADIOPTTON %s\r", Hex); + } + + TNC->TXLen = len; + ARDOPWriteCommBlock(TNC); + + Sleep(50); + + TNC->Timeout = 60; // 6 secs + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + + + // All Sent - enter Host Mode + + ARDOPDoTNCReinit(TNC); // Send Next Command + return; + } +} + +int ARDOPProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen) +{ + PMSGWITHLEN buffptr; + UCHAR * Buffer; // Data portion of frame + unsigned int Stream = 0, RealStream; + + if (Msg[0] == 255 && Msg[1] == 255) + { + goto tcpHostFrame; + } + + if (TNC->HostMode == 0) + return framelen; + + // Check toggle + +// Debugprintf("ARDOP RX Toggle = %x MSG[3] = %x", TNC->Toggle, Msg[3]); + + if (TNC->Toggle != (Msg[3] & 0x80)) + { + Debugprintf("ARDOP PTC Seq Error"); + return framelen; // should check if retrying + } + + // Any valid frame is an ACK + + TNC->Toggle ^= 0x80; // update toggle + + TNC->Timeout = 0; + + Msg[3] &= 0x7f; // remove toggle + + if (TNC->TNCOK == FALSE) + { + // Just come up + + TNC->TNCOK = TRUE; + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + +tcpHostFrame: + + Stream = RealStream = Msg[2]; + + // See if Poll Reply or Data + + if (Msg[3] == 1 && Stream > 0 && Stream <= APMaxStreams) + { + // Ardop Packet Data. Probably Buffer Status + + int Len = (int)strlen(&Msg[4]); + + if (memcmp(&Msg[4], "Queued ", 7) == 0) + { + int Count = atoi(&Msg[11]); + TNC->Streams[Stream].BytesOutstanding = Count; + } + + return Len + 5; + } + + if (Stream == 32) // Native Mode Command Response + { + if (Msg[3] == 1) // Null terminated response + { + int Len = (int)strlen(&Msg[4]) + 1; + ARDOPProcessResponse(TNC, &Msg[4], Len); + return Len + 5; + } + if (Msg[3] == 0) // Success, no response + return 5; + + if (Msg[3] == 7) // Status Reports + { + int Len = Msg[4] + 1; + + // may have more than one message in buffer, + // so pass to unpack + + memcpy(&TNC->ARDOPBuffer[TNC->InputLen],&Msg[5], Len); + TNC->InputLen += Len; + + ARDOPProcessReceivedControl(TNC); + return Len + 5; + } + return 0; + } + if (Stream == 33) // Native Mode Data + { + // May be connected, FEC or ID + + if (Msg[3] == 1) // Null terminated response + return 0; + + if (Msg[3] == 0) // Success, no response + return 0; + + if (Msg[3] == 7) // Data + { + int Len = Msg[4] + 1; + + // may have more than one message in buffer, + // so pass to unpack + + memcpy(&TNC->ARDOPDataBuffer[TNC->DataInputLen],&Msg[5], Len); + TNC->DataInputLen += Len; + + ARDOPProcessReceivedData(TNC); + return 0; + } + return 0; + + } + + if (Stream == 34) // Native Mode Log + { + int Len = Msg[4] + 1; + char timebuf[32]; + char Line[256]; + char * ptr, * ptr2; +#ifdef WIN32 + SYSTEMTIME st; +#else + struct timespec tp; + int hh; + int mm; + int ss; +#endif + if (TNC->LogHandle == 0 && TNC->DebugHandle == 0) + return 0; + +#ifdef WIN32 + GetSystemTime(&st); + sprintf(timebuf, "%02d:%02d:%02d.%03d ", + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + clock_gettime(CLOCK_REALTIME, &tp); + ss = tp.tv_sec % 86400; // Secs int day + hh = ss / 3600; + mm = (ss - (hh * 3600)) / 60; + ss = ss % 60; + + sprintf(timebuf, "%02d:%02d:%02d.%03d ", + hh, mm, ss, (int)tp.tv_nsec/1000000); +#endif + // Messages may be blocked with top bit of first byte set + + ptr = &Msg[5]; + ptr2 = Line; + + while(Len--) + { + int c = (*ptr++); + + if (c & 0x80) + { + // New Message + + c &= 0x7f; + + *ptr2 = 0; + fputs(Line, TNC->DebugHandle); // rest of last line + if (TNC->LastLogType < '7') + fputs(Line, TNC->LogHandle); + + TNC->LastLogType = c; + + // Timestamp new message and add type + + fputs(timebuf, TNC->DebugHandle); + fputc(c, TNC->DebugHandle); + fputc(' ', TNC->DebugHandle); + if (TNC->LastLogType < '7') + { + fputs(timebuf, TNC->LogHandle); + fputc(c, TNC->LogHandle); + fputc(' ', TNC->LogHandle); + } + ptr2 = Line; + } + else + *(ptr2++) = c; + } + *ptr2 = 0; + + fputs(Line, TNC->DebugHandle); // rest of last line + fflush(TNC->DebugHandle); + + if (TNC->LastLogType < '7') + { + fputs(Line, TNC->LogHandle); + fflush(TNC->LogHandle); + } + + return 0; + } + + if (Msg[3] == 4 || Msg[3] == 5) + { + MESSAGE Monframe; + + // Packet Monitor Data. + // DED Host uses 4 and 5 as Null Terminated ascii encoded header + // and 6 byte count format info. + + // In ARDOP Native mode I pass both header and data + // in byte count raw format, as there is no point + // in ascii coding then converting back to pass to + // monitor code + + // The First byte is TX/RX Flag + + int Len = Msg[4]; // Would be +1 but first is Flag + + memset(&Monframe, 0, sizeof(Monframe)); + + memcpy(Monframe.DEST, &Msg[6], Len); + Monframe.LENGTH = Len + MSGHDDRLEN; + Monframe.PORT = TNC->Port | Msg[5]; // or in TX Flag + + time(&Monframe.Timestamp); + + if (Msg[3] == 5) // More to come + { + // Save the header till the data arrives + + if (TNC->Monframe) + free(TNC->Monframe); + + TNC->Monframe = malloc(sizeof(MESSAGE)); + + if (TNC->Monframe) + memcpy(TNC->Monframe, &Monframe, sizeof(MESSAGE)); + + return Len + 6; + } + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + return Len + 6; + } + + if (Msg[3] == 6) + { + // Second part of I or UI + + int Len = Msg[4] + 1; + + MESSAGE Monframe; + UCHAR * ptr = (UCHAR *)&Monframe; + + memset(&Monframe, 0, sizeof(Monframe)); + + if (TNC->Monframe) + { + memcpy(&Monframe, TNC->Monframe, TNC->Monframe->LENGTH); + memcpy(&ptr[TNC->Monframe->LENGTH], &Msg[5], Len); + + Monframe.LENGTH += Len; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, TRUE); + + free(TNC->Monframe); + TNC->Monframe = NULL; + } + return Len + 6; + } + + if (Msg[3] == 0) + { + // Success - Nothing Follows + + if (Stream < 32) + if (TNC->Streams[Stream].CmdSet) + return 4; // Response to Command Set + + if ((TNC->TXBuffer[3] & 1) == 0) // Data + return 4; + + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Response + + return 4; + } + + // If the response to a Command, then we should convert to a text OK" for forward scripts, etc + + if (TNC->TXBuffer[5] == 'G') // Poll + return 4; + + if (TNC->TXBuffer[5] == 'C') // Connect - reply we need is async + return 4; + + if (TNC->TXBuffer[5] == 'L') // Shouldnt happen! + return 4; + + if (TNC->TXBuffer[5] == '#') // Shouldnt happen! + return 4; + + if (TNC->TXBuffer[5] == '%' && TNC->TXBuffer[6] == 'W') // Scan Control - Response to W1 + if (TNC->InternalCmd) + return 4; // Just Ignore + + if (TNC->TXBuffer[5] == 'J') // JHOST + { + if (TNC->TXBuffer[10] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return 4; + } + } + + if (TNC->Streams[Stream].Connected) + return 4; + + buffptr = GetBuff(); + + if (buffptr == NULL) return 4; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0],"ARDOP} Ok\r"); + + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); +// C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 4; + } + + if (Msg[3] > 0 && Msg[3] < 6) + { + // Success with message - null terminated + + UCHAR * ptr; + int len; + + if (Msg[2] == 0xff) // General Poll Response + { + UCHAR * Poll = TNC->TXBuffer; + UCHAR Chan = Msg[4] - 1; + + if (Chan == 255) // Nothing doing + return 0; + + if (Msg[5] != 0) + { + // More than one to poll - save the list of channels to poll + + strcpy(TNC->NexttoPoll, &Msg[5]); + } + + // Poll the channel that had data + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + ARDOPCRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return 0; + } + + Buffer = &Msg[4]; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return 0; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return 0; + + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Mode Response. Could be command response or status. + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + PMSGWITHLEN buffptr; + + if (strstr(Buffer, "Incoming")) + { + // incoming call. Check which application it is for + + char Call[11]; + char TargetCall[11] = ""; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + TRANSPORTENTRY * SESS; + + Buffer[len-1] = 0; + WritetoTrace(TNC, Buffer, len); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + memcpy(Call, &Buffer[19], 10); + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strstr(&Buffer[19], " to "); + if (ptr) + { + memcpy(TargetCall, ptr + 4, 10); + ptr = strchr(TargetCall, 13); + if (ptr) + *ptr = 0; + } + + ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + TidyClose(TNC, 0); + Debugprintf("ARDOP Packet Call from %s rejected", Call); + return 0; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + TidyClose(TNC, 0); + Debugprintf("ARDOP Packet Call from %s not in ValidCalls - rejected", Call); + return 0; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char ApplCmd[80]; + int Len = sprintf(ApplCmd, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return len + 5; // No buffers, so ignore + } + + buffptr->Len = Len; + memcpy(&buffptr->Data[0], ApplCmd, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + int txlen = (int)buffptr->Len; + SendARDOPorPacketData(TNC, Stream, data, txlen); + ReleaseBuffer(buffptr); + } + + SendARDOPorPacketData(TNC, Stream, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + STREAM->Connected = TRUE; + return len + 5; + } + + // Send to host + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) + return len + 5; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Buffer); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + // Unless Connected response close session + + STREAM->Connecting = FALSE; + + if (strstr(Buffer, "Connected")) + STREAM->Connected = TRUE; + else + if (strstr(Buffer, "Failure with")) + STREAM->ReportDISC = 10; // Gives time for failure message to display + else + if (strstr(Buffer, "Busy from")) + STREAM->ReportDISC = 10; // Gives time for failure message to display + else + if (strstr(Buffer, "Disconnected from")) + { + if (STREAM->Disconnecting) // We requested disconnect + { + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->Disconnecting = FALSE; + } + else + { + STREAM->Connected = 0; + STREAM->ReportDISC = 10; + } + } + else + STREAM->NeedDisc = 10; + + return len + 5; + } + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (Msg[3] < 3) // 1 or 2 - Success or Fail + { + return 0; + } + + if (Msg[3] == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + return 0; + } + + // 1, 2, 4, 5 - pass to Appl + + return 0; + } + + if (Msg[3] == 6) + { + return 0; + } + + if (Msg[3] == 7) // Data + { + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Response + + int len = Msg[4] + 1; + + if (TNC->Streams[Stream].Connected == 0) + return len + 5; + + buffptr = GetBuff(); + + if (buffptr == NULL) return 0; // No buffers, so ignore + + buffptr->Len = len; + memcpy((UCHAR *)&buffptr->Data[0], &Msg[5], buffptr->Len); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return len + 5; + } + + if (Stream == 32) // Command string + { + int Len = Msg[4] + 1; + + ARDOPProcessResponse(TNC, &Msg[5], Len); + + return 0; + } + + if (Msg[2] == 0xfe) // Status Poll Response + { + return 0; + } + + if (Msg[2] == 248) // Log Message + { + // Monitor Data - Length format + // first 4 bytes contain a 32 bits long timestamp. + // That timestamp holds the number of seconds that elapsed since date 01.01.2000 at 00:00:00. + // The MS byte is sent first. The timestamp can be corrected to the usual C timestamp (seconds + //since 01.01.1970, 00:00:00) simply by adding 946684800 (seconds) to it. + // Teminated with LF + + int datalen = Msg[4] + 1; + time_t timestamp = (Msg[5] << 24) + (Msg[6] << 16) + + (Msg[7] << 8) + Msg[8] + 946684800; + char c; + char timebuf[32] = "HH:MM:SS.MMM"; + struct tm * tm; + + if (TNC->LogHandle == 0 || TNC->DebugHandle == 0) + return 0; + + tm = gmtime(×tamp); + + sprintf(timebuf, "%02d:%02d:%02d. ", + tm->tm_hour, tm->tm_min, tm->tm_sec); + + // ARDOP Messages have a millisec time in first 3 bytes + // and a log type in 4th + + c = Msg[12]; // Type + + memcpy(&timebuf[9], &Msg[9], 3); // copy millisecs + fputs(timebuf, TNC->DebugHandle); + fputc(c, TNC->DebugHandle); + fputc(' ', TNC->DebugHandle); + fwrite(&Msg[13], 1, datalen - 8, TNC->DebugHandle); + fflush(TNC->DebugHandle); + + if (c < '7') + { + // All types below debug go to log file + + fputs(timebuf, TNC->LogHandle); + fputc(c, TNC->LogHandle); + fputc(' ', TNC->LogHandle); + fwrite(&Msg[13], 1, datalen - 8, TNC->LogHandle); + fflush(TNC->LogHandle); + } + return 0; + } + + if (Msg[2] == 253) // Rig Port Response + { + // Queue for Rig Control Driver + + int datalen = Msg[4] + 1; + PMSGWITHLEN buffptr; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return 0; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = datalen; + memcpy(&buffptr->Data[0], &Msg[5], datalen); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + } + return 0; + } + + if (Msg[2] == 250) // KISS + { + // Pass to KISS Code + + int datalen = Msg[4] + 1; + void ** buffptr = NULL; + + ProcessKISSBytes(TNC, &Msg[5], datalen); + return datalen + 5; + } + + return 0; + } + return 0; +} + +void ARDOPSCSCheckRX(struct TNCINFO * TNC) +{ + int Length, Len = 0; + unsigned short crc; + char UnstuffBuffer[500]; + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + if (TNC->ARDOPCommsMode == 'I') + { + unsigned char Buffer[33]; + BOOL Error; + int gotThisTime = 0, i2clen; + + // i2c mode always returns as much as requested or error + // First two bytes of block are length + +// if (TNC->hDevice < 0) +// return; + + while ((TNC->RXLen + Len) < 460) + { + i2clen = ReadCOMBlockEx(TNC->hDevice, Buffer, 33, &Error); + + if (i2clen < 33 || i2clen == 5) + return; + + if (Error) + { + Debugprintf("ARDOP i2c returned %d bytes Error %d", i2clen, Error); + return; + } + gotThisTime = Buffer[0]; + + + if (gotThisTime == 0) + { + if (Len) + break; // Something to process + + return; // No More + } + +// if (gotThisTime != 7) +// Debugprintf("ARDOP i2c Len %d RXL %d %x %x %x %x %x %x %x %x %x %x %x %x", +// gotThisTime, TNC->RXLen + Len, +// Buffer[0], Buffer[1], Buffer[2], Buffer[3], +// Buffer[4], Buffer[5], Buffer[6], Buffer[7], +// Buffer[8], Buffer[9], Buffer[10], Buffer[11]); + + memcpy(&TNC->RXBuffer[TNC->RXLen + Len], &Buffer[1], gotThisTime); + + Len += gotThisTime; + + if (Buffer[0] < 32) + break; // no more + } + } + + else if (TNC->ARDOPCommsMode =='E') //Serial over TCP + Len = SerialGetTCPMessage(TNC, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + else + if (TNC->hDevice) + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + + if (TNC->RXBuffer[0] != 170) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) + + return; // Wait for rest of frame + + // Complete Char Mode Frame + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); +// CloseLogFile(TNC->Port); + + TNC->RXLen = 0; // Ready for next frame + + if (TNC->HostMode == 0) + { + // We think TNC is in Terminal Mode + ARDOPProcessTermModeResponse(TNC); + return; + } + // We thought it was in Host Mode, but are wrong. + + TNC->HostMode = FALSE; + return; + } + + if (TNC->HostMode == FALSE) + { + TNC->RXLen = 0; // clear input and wait for char mode response + return; + } + + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[2] == 170) + { + // Retransmit Request + + TNC->RXLen = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + TNC->RXLen = 0; + return; // Ignore for now + } + + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + TNC->RXLen = 0; // Ready for next frame + UnstuffBuffer[0] = 0; // Make sure not seen as TCP Frame + ARDOPProcessDEDFrame(TNC, UnstuffBuffer, Length); + + // If there are more channels to poll (more than 1 entry in general poll response, + // and link is not active, poll the next one + + if (TNC->Timeout == 0) + { + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->NexttoPoll[0]) + { + UCHAR Chan = TNC->NexttoPoll[0] - 1; + + memmove(&TNC->NexttoPoll[0], &TNC->NexttoPoll[1], 19); + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + ARDOPCRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return; + } + else + { + // if last message wasn't a general poll, send one now + + if (TNC->PollSent) + return; + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + ARDOPCRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + } + } + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + +VOID ARDOPSCSPoll(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + int Stream = 0; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + TNC->Retries--; + + if(TNC->Retries >= 0) + { + if (TNC->HostMode) + Debugprintf("ARDOP Timeout - Retransmit PTC Block"); + ARDOPWriteCommBlock(TNC); // Retransmit Block + return; + } + + // Retried out. + + if (TNC->HostMode == 0) + { + ARDOPDoTermModeTimeout(TNC); + return; + } + + // Retried out in host mode - Clear any connection and reinit the TNC + + Debugprintf("ARDOP - Link to TNC Lost"); + TNC->TNCOK = FALSE; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + void ** buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + TNC->HostMode = 0; + TNC->ReinitState = 0; + TNC->CONNECTED = FALSE; + + // Disconenct any attached sessions + + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + if (TNC->Timeout) + return; // We've sent something + + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + ARDOPDoTNCReinit(TNC); + return; + } + + TNC->PollSent = FALSE; + + if (TNC->TNCOK && TNC->BPQtoRadio_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoRadio_Q); + datalen = (int)buffptr->Len; + Poll[2] = 253; // Radio Channel + Poll[3] = 0; // Data? + Poll[4] = datalen - 1; + memcpy(&Poll[5], &buffptr->Data[0], datalen); + + ReleaseBuffer(buffptr); + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + + for (Stream = 0; Stream < 14; Stream++) // Priority to commands + { + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + TNC->Streams[Stream].FramesQueued--; + + datalen = (int)buffptr->Len; + Buffer = &buffptr->Data[0]; // Data portion of frame + + Poll[3]= 0; + + if (Stream > 11) + Poll[2] = Stream + 20; // 12 and 13 to Channels 32 and 33 + else + if (Stream == 0) + Poll[2] = 33; + else + { + // Packet Frame + + Poll[2] = Stream; + Poll[3] = Buffer[0]; // First Byte is CMD/Data FLag + datalen--; + Buffer++; + } + + Poll[4] = datalen - 1; + + memcpy(&Poll[5], Buffer, datalen); + + ReleaseBuffer(buffptr); + + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + } + +//0x04421CB0 aa aa 21 00 07 00 06 48 65 6c 6c 6f 0d c8 3e 38 42 50 51 2d 32 20 35 0d 4a 8a 4d 38 42 50 51 ªª!....Hello.È>8BPQ-2 5.JŠM8BPQ +//0x04421CCF 2d 31 30 2c 47 4d 38 42 50 51 2d 35 2c 47 4d 38 42 50 51 2c 47 4d 38 42 50 51 2d 31 35 0d 00 -10,GM8BPQ-5,GM8BPQ,GM8BPQ-15.. +//0x04421CEE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ............................... + + + if (TNC->TNCOK && TNC->KISSTX_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->KISSTX_Q); + datalen = (int)buffptr->Len; + Poll[2] = 250; // KISS Channel + Poll[3] = 0; // Data + Poll[4] = datalen - 1; + memcpy(&Poll[5], &buffptr->Data[0], datalen); + + ReleaseBuffer(buffptr); + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + if (TNC->ReinitState == 3) + { + TNC->ReinitState = 0; + Poll[3] = 0x41; + } + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + ARDOPCRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + + return; + +} + +// ARDOP Serial over TCP Routines + +// Probably only for Teensy with ESP01. Runs SCS Emulator over a TCP Link + + +VOID SerialConnecttoTCPThread(struct TNCINFO * TNC); + +int SerialConnecttoTCP(struct TNCINFO * TNC) +{ + _beginthread(SerialConnecttoTCPThread, 0, (void *)TNC); + + return 0; +} + +VOID SerialConnecttoTCPThread(struct TNCINFO * TNC) +{ + char Msg[255]; + int i; + u_long param = 1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + SOCKADDR_IN sinx; + int addrlen=sizeof(sinx); + + if (TNC->HostName == NULL) + return; + + Sleep(5000); // Allow init to complete + + while(1) + { + if (TNC->TCPCONNECTED) + { + Sleep(57000); + continue; + } + + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + Sleep (57000); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + + TNC->TCPCONNECTED = TRUE; + ioctl(TNC->TCPSock, FIONBIO, ¶m); + Debugprintf("ARDOP TCPSerial connected, Socket %d", TNC->TCPSock); + } + else + { + if (TNC->Alerted == FALSE) + { + sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d Port %d\n", + WSAGetLastError(), htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + Sleep (57000); // 1 Mins + } + } +} + +int SerialGetTCPMessage(struct TNCINFO * TNC, unsigned char * Buffer, int Len) +{ + int index=0; + ULONG param = 1; + + if (TNC->TCPCONNECTED) + { + int InputLen; + + // Poll TCP COnnection for data + + // May have several messages per packet, or message split over packets + + InputLen = recv(TNC->TCPSock, Buffer, Len, 0); + + if (InputLen < 0) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + { + InputLen = 0; + return 0; + } + Debugprintf("ARDOP Serial TCP RX Error %d received for socket %d", err, TNC->TCPSock); + + TNC->TCPCONNECTED = 0; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return 0; + } + + if (InputLen > 0) + return InputLen; + else + { + Debugprintf("ARDOP Serial TCP Close received for socket %d", TNC->TCPSock); + + TNC->TCPCONNECTED = 0; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return 0; + } + } + + return 0; +} + +int ARDOPWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->ARDOPCommsMode == 'E') + { + if (TNC->TCPCONNECTED) + { + int SentLen = send(TNC->TCPSock, TNC->TXBuffer, TNC->TXLen, 0); + + if (SentLen != TNC->TXLen) + { + // ARDOP doesn't seem to recover from a blocked write. For now just reset + + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->TCPCONNECTED = FALSE; + } + } + TNC->Timeout = 20; // 2 secs + return 1; + } + if (TNC->hDevice) + return (WriteCommBlock(TNC)); + else + { + TNC->Timeout = 20; // 2 secs + return 0; + } +} + +// Teensy Combined ARDOP/AX.25 Support + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD +/* + +VOID ARAXINIT(struct PORTCONTROL * PORT) +{ + char Msg[80] = ""; + + memcpy(Msg, PORT->PORTDESCRIPTION, 30); + sprintf(Msg, "%s\n", Msg); + + WritetoConsoleLocal(Msg); +} + +VOID ARAXTX(struct PORTCONTROL * PORT, PMESSAGE Buffer) +{ + // KISS Encode and Queue to Host Mode KISS Queue + + UINT * TXMsg = NULL; // KISS Message to queue to Hostmode KISS Queue + UCHAR * TXPtr; + int TXLen = 0; + struct _LINKTABLE * ACKWORD = Buffer->Linkptr; + struct _MESSAGE * Message = (struct _MESSAGE *)Buffer; + UCHAR c; + + char * ptr1; + int Len; + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC && TNC->CONNECTED) // Have a Host Session + TXMsg = GetBuff(); // KISS Message to queue to Hostmode KISS Queue + + if (TXMsg == NULL) // No Session or No buffers + { + // Reset any ACKMODE Timer and release buffer C_Q_ADD(&TRACE_Q, Buffer); + + struct _LINKTABLE * LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + C_Q_ADD(&TRACE_Q, Buffer); + return; + } + + TXPtr = (UCHAR *)&TXMsg[2]; + + ptr1 = &Message->DEST[0]; + Len = Message->LENGTH - 7; + *(TXPtr++) = FEND; + + if (ACKWORD) // Frame Needs ACK + { + *TXPtr++ = 0x0c; // ACK OPCODE + ACKWORD -= (UINT)LINKS; // Con only send 16 bits, so use offset into LINKS + *TXPtr++ = ACKWORD & 0xff; + *TXPtr++ = (ACKWORD >> 8) &0xff; + + // have to reset flag so trace doesnt clear it + + Buffer->Linkptr = 0; + TXLen = 4;struct _LINKTABLE * + } + else + { + *TXPtr++ = 0; + TXLen = 2; + } + + while (Len--) + { + c = *(ptr1++); + + switch (c) + { + case FEND: + (*TXPtr++) = FESC; + (*TXPtr++) = TFEND; + TXLen += 2; + break; + + case FESC: + (*TXPtr++) = FESC; + (*TXPtr++) = TFESC; + TXLen += 2; + break; + + default: + (*TXPtr++) = c; + TXLen++; + } + if (TXLen > 250) + { + // Queue frame to KISS Channel and get another buffer + // can take up to 256, but sometimes add 2 at a time + TXMsg[1] = (int)(TXPtr - (UCHAR *)&TXMsg[2]); + } + } + + (*TXPtr++) = FEND; + TXLen++; + + TXMsg[1] = TXLen; + + C_Q_ADD(&TNC->KISSTX_Q, TXMsg); + + // Pass buffer to trace routines + + C_Q_ADD(&TRACE_Q, Buffer); + +} + + +VOID ARAXRX() +{ +} + + +VOID ARAXTIMER() +{ +} + +VOID ARAXCLOSE() +{ +} + +BOOL ARAXTXCHECK() +{ + return 0; +} + + +#define DATABYTES 400000 // WAS 320000 +extern UCHAR * NEXTFREEDATA; // ADDRESS OF NEXT FREE BYTE in shared memory +extern UCHAR DATAAREA[DATABYTES]; + + + +VOID AddVirtualKISSPort(struct TNCINFO * TNC, int ARDOPPort, char * buf) +{ + // Adds a Virtual KISS port for simultaneous ARDOP and Packet on Teensy TNC + // or ARDOP_PTC ovet a single host mode port. + + // Not needed if using TCP interface as that uses KISS over TCP + + struct PORTCONTROL * PORTVEC=PORTTABLE; + struct PORTCONTROL * PORT; + int pl = sizeof(struct PORTCONTROL); + int mh = MHENTRIES * sizeof(MHSTRUC); + int space = (int)(&DATAAREA[DATABYTES] - NEXTFREEDATA); + char Msg[64]; + unsigned char * ptr3; + unsigned int3; + int newPortNumber = 0; + + if (TNC->ARDOPCommsMode == 'T') // TCP + return; + + if (buf[12] == '=') + newPortNumber = atoi(&buf[13]); + + if (space < (pl + mh)) + { + WritetoConsoleLocal("Insufficient space to add ARDOP/Packet Port\n"); + return; + } + + + while (PORTVEC->PORTPOINTER) + { + PORTVEC=PORTVEC->PORTPOINTER; + } + + // PORTVEC is now last port in chain + + ptr3 = NEXTFREEDATA; + + PORT = (struct PORTCONTROL *)ptr3; + + ptr3 += sizeof (struct PORTCONTROL); + + // Round to word boundary (for ARM5 etc) + + int3 = (int)ptr3; + int3 += 3; + int3 &= 0xfffffffc; + ptr3 = (UCHAR *)int3; + + PORTVEC->PORTPOINTER = PORT; // Chain to previous last port + + if (newPortNumber == 0) + newPortNumber = 32;; + + if (GetPortTableEntryFromPortNum(newPortNumber)) + { + // Number in use + + // If user specified search up, if default search down + + if (newPortNumber == 32) + while(newPortNumber && GetPortTableEntryFromPortNum(newPortNumber)) // Try next lower + newPortNumber--; + else + while(newPortNumber < 32 && GetPortTableEntryFromPortNum(newPortNumber)) // Try next highest + newPortNumber++; + + } + + if (newPortNumber == 0 || newPortNumber > 32) + { + WritetoConsoleLocal("No free Port Number to add ARDOP/Packet Port\n"); + return; + } + + + + NUMBEROFPORTS++; + + PORT->PORTNUMBER = newPortNumber; + PORT->PortSlot = PORTVEC->PortSlot + 1; + + sprintf(Msg, "Packet Port for ARDOP Port %d ", ARDOPPort); + memcpy(PORT->PORTDESCRIPTION, Msg, 30); + + PORT->TNC = TNC; + TNC->VirtualPORT = PORT; // Link TNC and PORT both ways + PORT->PORTINITCODE = ARAXINIT; + PORT->PORTTIMERCODE = ARAXTIMER; + PORT->PORTRXROUTINE = ARAXRX; + PORT->PORTTXROUTINE = ARAXTX; + PORT->PORTCLOSECODE = ARAXCLOSE; + PORT->PORTTXCHECKCODE = ARAXTXCHECK; + + // Default L2 Params + + PORT->PORTN2 = 5; + PORT->PORTT1 = 5000/333; // FRACK 5 secs + + // As we use IPoll we can set RESPTIME very long and FRACK short + + PORT->PORTT2 = 20000/333; // RESPTIME + PORT->PORTPACLEN = 128; + PORT->PORTWINDOW = 1; + + // ADD MH AREA IF NEEDED + + NEEDMH = 1; // Include MH in Command List + + PORT->PORTMHEARD = (PMHSTRUC)ptr3; + + ptr3 += MHENTRIES * sizeof(MHSTRUC); + + // Round to word boundary (for ARM5 etc) + + int3 = (int)ptr3; + int3 += 3; + int3 &= 0xfffffffc; + ptr3 = (UCHAR *)int3; + + NEXTFREEDATA = ptr3; + + return; +} + +int ConfigVirtualKISSPort(struct TNCINFO * TNC, char * Cmd) +{ + struct PORTCONTROL * PORT = TNC->VirtualPORT; + void ** buffptr = GetBuff(); + char * Context; + char * Param; + char * Command; + int Value; + int Stream = 0; + if (buffptr == NULL) + return 0; + + if (PORT == NULL) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Packet Mode nor Enabled\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + _strupr(Cmd); + + Command = strtok_s(&Cmd[4], " \n\r", &Context); + Param = strtok_s(NULL, " \n\r", &Context); + + if (Param) + Value = atoi(Param); + + if (strcmp(Command, "PACLEN") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTPACLEN); + else + { + if (Value > 0 && Value <= 256) + PORT->PORTPACLEN = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTPACLEN); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "RETRIES") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTN2); + else + { + if (Value > 0 && Value <= 16) + PORT->PORTN2 = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTN2); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "WINDOW") == 0 || strcmp(Command, "MAXFRAME") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTWINDOW); + else + { + if (Value > 0 && Value <= 7) + PORT->PORTWINDOW = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTWINDOW); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "FRACK") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d mS\r", Command, PORT->PORTT1 * 333); + else + { + if (Value > 0 && Value <= 20000) + PORT->PORTT1 = Value / 333; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d mS\r", Command, PORT->PORTT1 * 333); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "RESPTIME") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d mS\r", Command, PORT->PORTT2 * 333); + else + { + if (Value > 0 && Value <= 20000) + PORT->PORTT2 = Value / 333; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d mS\r", Command, PORT->PORTT2 * 333); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Invalid Command %s\r", Cmd); + + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + + +} +*/ +void ProcessKISSBytes(struct TNCINFO * TNC, UCHAR * Data, int Len) +{ + // Kiss data received from TNC but not necessarrily a full packet + // and could be multiple packets + + // The TNC record is for the ARDOP Port, but we need to queue data + // to the linked Virtual Packet Port + + struct PORTCONTROL * PORT = TNC->VirtualPORT; + UCHAR * KISSBuffer = TNC->KISSBuffer; + UCHAR c; + UCHAR * inptr = Data; + int outptr = TNC->KISSInputLen; + + if (PORT == NULL) + return; + + while(Len--) + { + c = *(inptr++); + + if (TNC->ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + TNC->ESCFLAG = FALSE; + + if (c == TFESC) + c = FESC; + + if (c == TFEND) + c = FEND; + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + if (outptr == 0) + { + // Start of Message. If polling, extend timeout + + continue; + } + + ProcessKISSPacket(TNC, KISSBuffer, outptr); + outptr = 0; + return; + + case FESC: + + TNC->ESCFLAG = TRUE; + continue; + + } + } + + // + // Ok, a normal char + // + + KISSBuffer[outptr++] = c; + } + + if (outptr > 510) + outptr = 0; // Protect Buffer + + TNC->KISSInputLen = outptr; + + return; +} + +void ProcessKISSPacket(struct TNCINFO * TNC, UCHAR * KISSBuffer, int Len) +{ + if (KISSBuffer[0] == 0x0c) // ACK Frame + { + // ACK FRAME - reset link timer + + struct _LINKTABLE * LINK; + UINT ACKWORD = KISSBuffer[1] | KISSBuffer[2] << 8; + + LINK = LINKS + ACKWORD; + + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + return; + } + if (KISSBuffer[0] == 0) // Data Frame + { + PDATAMESSAGE Buffer = (PDATAMESSAGE)GetBuff(); + + if (Buffer) + { + memcpy(&Buffer->PID, &KISSBuffer[1], --Len); + Len += sizeof(void *) + 3; + + PutLengthinBuffer(Buffer, Len); + + C_Q_ADD(&TNC->VirtualPORT->PORTRX_Q, (UINT *)Buffer); + } + } +} diff --git a/ARDOPAX25.c b/ARDOPAX25.c new file mode 100644 index 0000000..9fe7e81 --- /dev/null +++ b/ARDOPAX25.c @@ -0,0 +1,2377 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use ARDOP Virtual TNC in a form +// of ax.25 + +// Uses a lot of routines in ARDOP.c + +// Includes its own ax.25 L2 stack, as I want to do dynamic packet size and +// maxframe + +// Actually may be better to use existing L2, but modify for dynamic paclen, but we +// need tighter coupling than the KISS driver provides. So this may become a "SuperKISS" TNC +// as a minimum I think this needs channel busy info connected to L2. Not sure yet!! + + +#define _CRT_SECURE_NO_DEPRECATE +#define _USE_32BIT_TIME_T + +#include +#include + + +#include "CHeaders.h" + +#ifdef WIN32 +#include +#endif + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int ARDOPKillTNC(struct TNCINFO * TNC); +int ARDOPRestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len); +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID SendToTNC(struct TNCINFO * TNC, UCHAR * Encoded, int EncLen); +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length); + +#ifndef LINBPQ +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="ARDOPSTATUS"; +static char WindowTitle[] = "ARDOP"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +unsigned long _beginthread( void( *start_address )(), unsigned stack_size, int arglist); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + +static ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->WINMORHostName = malloc(strlen(p_ipad)+1); + + if (TNC->WINMORHostName == NULL) return TRUE; + + strcpy(TNC->WINMORHostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); +// if (p_cmd) TNC->ProgramPath = _strdup(_strupr(p_cmd)); + } + } + + TNC->MaxConReq = 10; // Default + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else + if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + + else + if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + + else + if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + + + +void ARDOPThread(int port); +VOID ARDOPProcessDataSocketData(int port); +int ConnecttoARDOP(); +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ARDOPReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static int ExtProc(int fn, int port,unsigned char * buff) +{ + int datalen; + UINT * buffptr; + char txbuff[500]; + unsigned int bytes,txlen=0; + int Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[256] = ""; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + if (TNC->CONNECTED == 0) + { + // discard if not connected + + ReleaseBuffer(buffptr); + continue; + } + + datalen = buffptr->LENGTH - 7; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + ConvFromAX25(Buffer + 7, Call); // Origin + strlop(Call, ' '); + strcat(FECMsg, Call); + strcat(FECMsg, ">"); + + ConvFromAX25(Buffer, Call); // Dest + strlop(Call, ' '); + strcat(FECMsg, Call); + + Buffer += 14; // TO Digis + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + Call[0] = ','; + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(FECMsg, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + strcat(FECMsg, "|"); + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + } + strcat(FECMsg, Buffer); + + ARDOPSendData(TNC, FECMsg, strlen(FECMsg)); + TNC->FECPending = 1; + + ReleaseBuffer((UINT *)buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + ARDOPSendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + UINT * buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr[1]=39; + memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + fn =fn; //ARDOPSendCommand(TNC, "MODE", TRUE); + else + { +// if (time(NULL) - TNC->WinmorRestartCodecTimer > 300) // 5 mins +// { +// ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); +// ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); +// } +// else + ARDOPSendCommand(TNC, "STATE", TRUE); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } + } + + // FECPending can be set if not in FEC Mode (eg beacon) + + if (TNC->FECPending) // Check if FEC Send needed + { + if (TNC->Streams[0].BytesOutstanding) //Wait for data to be queued (async data session) + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + ARDOPSendCommand(TNC,"FECSEND TRUE", TRUE); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->WIMMORPID) + { + ARDOPKillTNC(TNC); + ARDOPRestartTNC(TNC); + } + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, "WINMOR TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + ARDOPKillTNC(TNC); + ARDOPRestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff[4] = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + ConnecttoARDOP(port); + TNC->lasttime = ltime; + } + } + + // See if any frames for this port + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + UINT * buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen=buffptr[1]; + memcpy(txbuff,buffptr+2,txlen); + bytes = ARDOPSendData(TNC, &txbuff[0], txlen); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, txbuff, txlen); + ReleaseBuffer(buffptr); + } + + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen=buffptr[1]; + + buff[4] = 0; // Compatibility with Kam Driver + buff[7] = 0xf0; + memcpy(&buff[8],buffptr+2,datalen); // Data goes to +7, but we have an extra byte + datalen+=8; + buff[5]=(datalen & 0xff); + buff[6]=(datalen >> 8); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + return 0; // Don't try if not connected + } + + + txlen=(buff[6]<<8) + buff[5] - 7; + + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff[8 + txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff[8]); + + ARDOPSendData(TNC, Buffer, len); + TNC->FECPending = 1; + + return 0; + } + + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + return 0; + break; + + case 4: // reinit + + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CLOSE", FALSE); + FreeSemaphore(&Semaphore); + Sleep(100); + } + shutdown(TNC->WINMORSock, SD_BOTH); + Sleep(100); + closesocket(TNC->WINMORSock); + return 0; + + case 6: // Scan Stop Interface + + Param = (int)buff; + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + if (!TNC->ConnectPending) + return 0; // OK to Change + +// ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + + return TRUE; + } + + if (Param == 2) // Check Permission + { + if (TNC->ConnectPending) + { + TNC->ConnectPending--; + return -1; // Skip Interval + } + return 1; // OK to change + } + + if (Param == 3) // Release Permission + { +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (strcmp(Scan->ARDOPMode, TNC->ARDOPCurrentMode) != 0) + { + // Mode changed + + char CMD[32]; + + if (TNC->ARDOPCurrentMode[0] == 0) + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + strcpy(TNC->ARDOPCurrentMode, Scan->ARDOPMode); + + + if (Scan->ARDOPMode[0] == 'S') // SKIP - Dont Allow Connects + { + if (TNC->ARDOPCurrentMode[0] != 0) + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->ARDOPCurrentMode[0] = 0; + } + + TNC->WL2KMode = 0; + return 0; + } + + if (strchr(Scan->ARDOPMode, 'F')) + sprintf(CMD, "ARQBW %sORCED", Scan->ARDOPMode); + else + sprintf(CMD, "ARQBW %sAX", Scan->ARDOPMode); + + return 0; + } + return 0; + } + return 0; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "ARDOP Status" + "

ARDOP Status

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID ARDOPSuspendPort(struct TNCINFO * TNC); +VOID ARDOPReleasePort(struct TNCINFO * TNC); + + +UINT ARDOPAXExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return (int) ExtProc; + } + + PORT->PORTT1 = 15; + PORT->PORTT2 = 3; + PORT->PORTN2 = 5; + PORT->PORTPACLEN = 128; + + TNC->Port = port; + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(8192); + + if (TNC->ProgramPath) + TNC->WeStartedTNC = ARDOPRestartTNC(TNC); + + TNC->Hardware = H_AXARDOP; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 0; // KISS ??? + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 0; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = ARDOPSuspendPort; + TNC->ReleasePortProc = ARDOPReleasePort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "INITIALIZE\r"); + strcat(TempScript, "VERSION\r"); + strcat(TempScript, "CWID False\r"); + strcat(TempScript, "PROTOCOLMODE ARQ\r"); + strcat(TempScript, "ARQTIMEOUT 90\r"); +// strcat(TempScript, "ROBUST False\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + +// strcat(TNC->InitScript,"FECRCV True\r"); +// strcat(TNC->InitScript,"AUTOBREAK True\r"); + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); +// strcat(TNC->InitScript,"PROCESSID\r"); +// strcat(TNC->InitScript,"CODEC TRUE\r"); + strcat(TNC->InitScript,"LISTEN FALSE\r"); // Will use TNC in FEC mode only + strcat(TNC->InitScript,"MYCALL\r"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->WINMORHostName=malloc(10); + + if (TNC->WINMORHostName != NULL) + strcpy(TNC->WINMORHostName,"127.0.0.1"); + + } + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after each Connection"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + Consoleprintf("ARDOPAX Host %s %d", TNC->WINMORHostName, htons(TNC->destaddr.sin_port)); + + ConnecttoARDOP(port); + + time(&TNC->lasttime); // Get initial time value + + return ((int) ExtProc); +} + +static int ConnecttoARDOP(int port) +{ + _beginthread(ARDOPThread,0,port); + + return 0; +} + +static VOID ARDOPThread(port) +{ + // Opens sockets and looks for data on control and data sockets. + + // Socket may be TCP/IP or Serial + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1, * ptr2; + UINT * buffptr; + char Cmd[32]; + + if (TNC->WINMORHostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(5000); // Allow init to complete + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->WIMMORPID)) +// { +// ARDOPRestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->WINMORHostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->WINMORHostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->WINMORHostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->WINMORSock); +// closesocket(TNC->WINMORDataSock); + + TNC->WINMORSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->WINMORSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->WINMORDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->WINMORDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->WINMORSock); + + return; + } + + + setsockopt(TNC->WINMORSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->WINMORDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->WINMORDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + +/* if (bind(TNC->WINMORSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->WINMORSock); + TNC->CONNECTING = FALSE; + + return; + } +*/ + if (connect(TNC->WINMORSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->WINMORSock); + TNC->WINMORSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + // Connect Data Port + + if (connect(TNC->WINMORDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->WINMORSock); + closesocket(TNC->WINMORDataSock); + TNC->WINMORSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + +#ifndef LINBPQ +// FreeSemaphore(&Semaphore); + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // ARDOP needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + buffptr = GetBuff(); + buffptr[1] = 0; + C_Q_ADD(&TNC->BPQtoWINMOR_Q, buffptr); + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + if (ptr2) + *(ptr2) = 0; + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATE", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd,"DATE %02d%02d%02d\r", tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100); + ptr1 = Cmd; + } + else if (_memicmp(ptr1, "TIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd,"TIME %02d%02d%02d\r", tm->tm_hour, tm->tm_min, tm->tm_sec); + ptr1 = Cmd; + } + + ARDOPSendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to ARDOP TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to ARDOP TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->WINMORSock,&readfs); + FD_SET(TNC->WINMORSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->WINMORDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->WINMORDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->WINMORDataSock,&errorfs); + + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select(TNC->WINMORDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("ARDOP Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->WINMORSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->WINMORDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->WINMORSock, &errorfs)) + { +Lost: + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->WINMORDataSock); + TNC->WINMORSock = 0; + return; + } + + if (FD_ISSET(TNC->WINMORDataSock, &errorfs)) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->WINMORSock); + closesocket(TNC->WINMORDataSock); + TNC->WINMORSock = 0; + return; + } + + + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + sprintf(Msg, "ARDOP No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CODEC FALSE", FALSE); + FreeSemaphore(&Semaphore); + + Sleep(100); + shutdown(TNC->WINMORSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->WINMORDataSock); + + Sleep(100); + shutdown(TNC->WINMORDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->WINMORDataSock); + + if (TNC->WIMMORPID && TNC->WeStartedTNC) + { + ARDOPKillTNC(TNC); + } + return; + } + } + sprintf(Msg, "ARDOP Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + + +static VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; +// unsigned int CRC; + +// CRC = checkcrc16(&Buffer[2], MsgLen - 2); + + Buffer[MsgLen - 1] = 0; // Remove CR + +// if (CRC == 0) +// { +// Debugprintf("ADDOP CRC Error %s", Buffer); +// return; +// } + + Buffer+=2; // Skip c: + + if (_memicmp(Buffer, "RDY", 3) == 0) + { + // Command ACK. Remove from bufer and send next if any + + UINT * buffptr; + UINT * Q; + + + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + + // See if another + + // Leave on Queue till acked + + // Q may not be word aligned, so copy as bytes (for ARM5) + + Q = (UINT *)&TNC->BPQtoWINMOR_Q; + + buffptr = (UINT *)Q[0]; + + if (buffptr) + SendToTNC(TNC, (UCHAR *)&buffptr[2], buffptr[1]); + + return; + } + + if (_memicmp(Buffer, "CRCFAULT", 8) == 0) + { + // Command NAK. Resend + + UINT * buffptr; + UINT * Q; + + // Leave on Queue till acked + + // Q may not be word aligned, so copy as bytes (for ARM5) + + Q = (UINT *)&TNC->BPQtoWINMOR_Q; + + buffptr = (UINT *)Q[0]; + + if (buffptr) + SendToTNC(TNC, (UCHAR *)&buffptr[2], buffptr[1]); + + Debugprintf("ARDP CRCFAULT Received"); + return; + } + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + Debugprintf(Buffer); + + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + return; + } + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, TRUE); + + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + TNC->ConnectPending = 6; // This comes before Pending + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 3); + memcpy(TNC->TargetCall, &Buffer[7], 10); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 5); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + Debugprintf(Buffer); + + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } +// else +// if (TNC->TXRXState == 'S') +// ARDOPSendCommand(TNC,"OVER"); + + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %s", + STREAM->BytesTXed, STREAM->BytesRXed, &Buffer[7]); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 3); + + ARDOPSendCommand(TNC, "RDY", FALSE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + +// if (TNC->StartInRobust) +// ARDOPSendCommand(TNC, "ROBUST TRUE"); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Speed + + ptr = strchr(&Buffer[10], ' '); + if (ptr) + { + Speed = atoi(ptr); + + if (Speed == 200) + TNC->WL2KMode = 40; + else if (Speed == 500) + TNC->WL2KMode = 41; + else if (Speed == 1000) + TNC->WL2KMode = 42; + else if (Speed == 2000) + TNC->WL2KMode = 43; + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incomming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[32]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("ARDOP Call from %s rejected", Call); + return; + } + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + ARDOPSendData(TNC, Msg, strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", Call); + + buffptr[1] = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0 + || _memicmp(Buffer, "STATUS CONNECT TO", 17) == 0 + || _memicmp(Buffer, "STATUS ARQ TIMEOUT FROM PROTOCOL STATE", 24) == 0) + { + Debugprintf(Buffer); + + ARDOPSendCommand(TNC, "RDY", FALSE); + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "ARDOP} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + +// ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); // !!!! Temp bug workaround !!!! + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 3); + + // Release Session3 + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ARDOPReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + Debugprintf(Buffer); + + if (_memicmp(Buffer, "STATUS ", 7) == 0) + { + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "RADIOMODELS", 11) == 0) + return; + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + // Debugprintf("WINMOR RX: %s", Buffer); + + strcpy(TNC->WEB_MODE, &Buffer[5]); + SetWindowText(TNC->xIDC_MODE, &Buffer[5]); + return; + } + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + ARDOPSendCommand(TNC, "RDY", FALSE); + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + return; + } + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0 + || _memicmp(&Buffer[0], "REJECTEDB", 9) == 0) //REJECTEDBUSY or REJECTEDBW + { + ARDOPSendCommand(TNC, "RDY", FALSE); + TNC->ConnectPending = FALSE; + return; + } + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); +// return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + ARDOPSendCommand(TNC, "RDY", FALSE); + + TNC->WinmorRestartCodecTimer = time(NULL); + + MySetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "DISCONNECTING", 13) == 0) // So we can timout stuck discpending + { + TNC->DiscPending = 600; + return; + } + if (_memicmp(&Buffer[9], "DISCONNECTED", 12) == 0) + { + TNC->DiscPending = FALSE; + TNC->ConnectPending = FALSE; + + if (TNC->RestartAfterFailure) + { + if (TNC->HadConnect) + { + TNC->HadConnect = FALSE; + + if (TNC->WIMMORPID) + { + ARDOPKillTNC(TNC); + ARDOPRestartTNC(TNC); + } + } + } + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->WIMMORPID = atoi(&Buffer[10]); + +#ifndef LINBPQ + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->WIMMORPID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + // Set Window Title to reflect BPQ Port Description + + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +#endif + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + +// if (TNC->FEC1600) +// ARDOPSendCommand(TNC,"FECSEND 1600"); +// else +// ARDOPSendCommand(TNC,"FECSEND 500"); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); + return; + } + + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "ARDOP} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); +} + +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; +// char * ptr, * ptr2; +// char Buffer[4096]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + // Both command and data arrive here, which complicated things a bit + + // Commands start with c: and end with CR. + // Data starts with d: and has a length field + // “d:ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data, +2 Byte CRC” + + // As far as I can see, shortest frame is “c:RDY + 2 byte CRC” = 8 bytes + + if (TNC->DataInputLen > 8000) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + // I don't think it likely we will get packets this long, but be aware... + + // We can get pretty big ones in the faster + + InputLen=recv(TNC->WINMORDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], 8192 - TNC->DataInputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + +// closesocket(TNC->WINMORSock); + closesocket(TNC->WINMORDataSock); + + TNC->WINMORDataSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return; + } + + TNC->DataInputLen += InputLen; + +loop: + + if (TNC->DataInputLen < 8) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPDataBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPDataBuffer[0] == 'd') + { + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[2] << 8) + TNC->ARDOPDataBuffer[3]; // HI First +// unsigned short CRC; + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 4) + return; // Wait for more + + MsgLen = DataLen + 4; // d: Len CRC + + // Check CRC + +// CRC = compute_crc(&TNC->ARDOPBuffer[2], DataLen + 4); + +// CRC = checkcrc16(&TNC->ARDOPBuffer[2], DataLen + 4); + +// if (CRC == 0) +// { +// Debugprintf("ADDOP CRC Error %s", &TNC->ARDOPBuffer[2]); +// return; +// } + + + memcpy(DataType, &TNC->ARDOPDataBuffer[4] , 3); + DataType[3] = 0; + Data = &TNC->ARDOPDataBuffer[7]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + +// ARDOPSendCommand(TNC, "RDY", FALSE); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->InputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + } + return; +} + + + +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + // Both command and data arrive here, which complicated things a bit + + // Commands start with c: and end with CR. + // Data starts with d: and has a length field + // “d:ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data, +2 Byte CRC” + + // As far as I can see, shortest frame is “c:RDY + 2 byte CRC” = 8 bytes + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + // I don't think it likely we will get packets this long, but be aware... + + // We can get pretty big ones in the faster + + InputLen=recv(TNC->WINMORSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->WINMORSock); + + TNC->WINMORSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return; + } + + TNC->InputLen += InputLen; + +loop: + + if (TNC->InputLen < 6) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPBuffer[0] == 'c') + { + // Command = look for CR + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR (no CRC in new version) + { + // Usual Case - single meg in buffer + + ARDOPProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + // buffer contains more that 1 message + + // I dont think this should happen, but... + + MsgLen = TNC->InputLen - (ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + ARDOPProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + } + return; +} + + + +static VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + UINT * buffptr; + + TNC->TimeSinceLast = 0; + + if (strcmp(Type, "IDF") == 0) + { + // Place ID frames in Monitor Window and MH + + char Call[20]; + + Data[Length] = 0; + WritetoTrace(TNC, Data, Length); + + if (memcmp(Data, "ID:", 3) == 0) // These seem to be transmitted ID's + { + memcpy(Call, &Data[3], 20); + strlop(Call, ':'); + UpdateMH(TNC, Call, '!', 'I'); + } + return; + } + + STREAM->BytesRXed += Length; + + Data[Length] = 0; + Debugprintf("ARDOP: RXD %d bytes", Length); + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed, STREAM->BytesRXed,STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + + if (TNC->FECMode) + { + Length = strlen(Data); + if (Data[Length - 1] == 10) + Data[Length - 1] = 13; + + } + + if (strcmp(Type, "FEC") == 0) + { + // Make sure it at least looks like an ax.25 format message + + + char * ptr1 = Data; + char * ptr2 = strchr(ptr1, '>'); + int Len = 80; + + if (ptr2 && (ptr2 - ptr1) < 10) + { + // Could be APRS + + if (memcmp(ptr2 + 1, "AP", 2) == 0) + { + // assume it is + + char * ptr3 = strchr(ptr2, '|'); + struct _MESSAGE * buffptr = GetBuff(); + + if (ptr3 == 0) + return; + + *(ptr3++) = 0; // Terminate TO call + + Len = strlen(ptr3); + + // Convert to ax.25 format + + if (buffptr == 0) + return; // No buffers, so ignore + + buffptr->PORT = TNC->Port; + + ConvToAX25(ptr1, buffptr->ORIGIN); + ConvToAX25(ptr2 + 1, buffptr->DEST); + buffptr->ORIGIN[6] |= 1; // Set end of address + buffptr->CTL = 3; + buffptr->PID = 0xF0; + memcpy(buffptr->L2DATA, ptr3, Len); + buffptr->LENGTH = 23 + Len; + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + + return; + + } + } + + // FEC but not AX.25 APRS. Discard if connected + + return; + } + + // Discard ARQ frames + + return; +} + +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + ARDOPSendCommand(TNC, "ABORT", TRUE); +} + +static VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ARDOPReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } +} + diff --git a/Alloc.c b/Alloc.c new file mode 100644 index 0000000..6080388 --- /dev/null +++ b/Alloc.c @@ -0,0 +1,129 @@ +/* Alloc.c -- Memory allocation functions + 2008-09-24 + Igor Pavlov + Public domain */ + +#ifdef _WIN32 +#include +#endif +#include + + + +#include "Alloc.h" + +/* #define _SZ_ALLOC_DEBUG */ + +/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ +#ifdef _SZ_ALLOC_DEBUG +#include +int g_allocCount = 0; +int g_allocCountMid = 0; +int g_allocCountBig = 0; +#endif + +void *MyAlloc(size_t size) +{ + if (size == 0) + return 0; +#ifdef _SZ_ALLOC_DEBUG + { + void *p = malloc(size); + fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); + return p; + } +#else + return malloc(size); +#endif +} + +void MyFree(void *address) +{ +#ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); +#endif + free(address); +} + +#ifdef _WIN32 + +void *MidAlloc(size_t size) +{ + if (size == 0) + return 0; +#ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); +#endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void MidFree(void *address) +{ +#ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); +#endif + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#ifndef MEM_LARGE_PAGES +#undef _7ZIP_LARGE_PAGES +#endif + +#ifdef _7ZIP_LARGE_PAGES +SIZE_T g_LargePageSize = 0; +typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); +#endif + +void SetLargePageSize() +{ +#ifdef _7ZIP_LARGE_PAGES + SIZE_T size = 0; + GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); + if (largePageMinimum == 0) + return; + size = largePageMinimum(); + if (size == 0 || (size & (size - 1)) != 0) + return; + g_LargePageSize = size; +#endif +} + + +void *BigAlloc(size_t size) +{ + if (size == 0) + return 0; +#ifdef _SZ_ALLOC_DEBUG + fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); +#endif + +#ifdef _7ZIP_LARGE_PAGES + if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) + { + void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), + MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + if (res != 0) + return res; + } +#endif + return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +} + +void BigFree(void *address) +{ +#ifdef _SZ_ALLOC_DEBUG + if (address != 0) + fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); +#endif + + if (address == 0) + return; + VirtualFree(address, 0, MEM_RELEASE); +} + +#endif diff --git a/Alloc.h b/Alloc.h new file mode 100644 index 0000000..a396c6b --- /dev/null +++ b/Alloc.h @@ -0,0 +1,32 @@ +/* Alloc.h -- Memory allocation functions +2008-03-13 +Igor Pavlov +Public domain */ + +#ifndef __COMMON_ALLOC_H +#define __COMMON_ALLOC_H + +#include + +void *MyAlloc(size_t size); +void MyFree(void *address); + +#ifdef _WIN32 + +void SetLargePageSize(); + +void *MidAlloc(size_t size); +void MidFree(void *address); +void *BigAlloc(size_t size); +void BigFree(void *address); + +#else + +#define MidAlloc(size) MyAlloc(size) +#define MidFree(address) MyFree(address) +#define BigAlloc(size) MyAlloc(size) +#define BigFree(address) MyFree(address) + +#endif + +#endif diff --git a/BBSHTMLConfig.c b/BBSHTMLConfig.c new file mode 100644 index 0000000..d327367 --- /dev/null +++ b/BBSHTMLConfig.c @@ -0,0 +1,3007 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include "bpqmail.h" + +#ifdef WIN32 +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#endif + +extern char NodeTail[]; +extern char BBSName[10]; + +extern char LTFROMString[2048]; +extern char LTTOString[2048]; +extern char LTATString[2048]; + +//static UCHAR BPQDirectory[260]; + +extern ConnectionInfo Connections[]; +extern int NumberofStreams; +extern time_t MaintClock; // Time to run housekeeping + +extern int SMTPMsgs; + +extern int ChatApplNum; +extern int MaxChatStreams; + +extern char Position[81]; +extern char PopupText[251]; +extern int PopupMode; + +#define MaxCMS 10 // Numbr of addresses we can keep - currently 4 are used. + +struct UserInfo * BBSLIST[NBBBS + 1]; + +int MaxBBS = 0; + +#define MAIL +#include "httpconnectioninfo.h" + +struct TCPINFO * TCP; + +VOID ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, int * RLen); +static struct HTTPConnectionInfo * FindSession(char * Key); +VOID ProcessUserUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID ProcessMsgFwdUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key); +VOID ProcessConfUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID ProcessUIUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SendUserSelectPage(char * Reply, int * ReplyLen, char * Key); +VOID SendFWDSelectPage(char * Reply, int * ReplyLen, char * Key); +int EncryptPass(char * Pass, char * Encrypt); +VOID ProcessFWDUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SendStatusPage(char * Reply, int * ReplyLen, char * Key); +VOID SendUIPage(char * Reply, int * ReplyLen, char * Key); +VOID GetParam(char * input, char * key, char * value); +BOOL GetConfig(char * ConfigName); +VOID ProcessDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +int APIENTRY SessionControl(int stream, int command, int param); +int SendMessageDetails(struct MsgInfo * Msg, char * Reply, char * Key); +VOID ProcessMsgUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID ProcessMsgAction(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +int APIENTRY GetNumberofPorts(); +int APIENTRY GetPortNumber(int portslot); +UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +VOID SendHouseKeeping(char * Reply, int * ReplyLen, char * Key); +VOID SendWelcomePage(char * Reply, int * ReplyLen, char * Key); +VOID SaveWelcome(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key); +VOID GetMallocedParam(char * input, char * key, char ** value); +VOID SaveMessageText(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SaveHousekeeping(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key); +VOID SaveWP(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key); +int SendWPDetails(WPRec * WP, char * Reply, char * Key); +int SendUserDetails(struct HTTPConnectionInfo * Session, char * Reply, char * Key); +int SetupNodeMenu(char * Buff); +VOID SendFwdSelectPage(char * Reply, int * ReplyLen, char * Key); +VOID SendFwdDetails(struct UserInfo * User, char * Reply, int * ReplyLen, char * Key); +VOID SetMultiStringValue(char ** values, char * Multi); +VOID SendFwdMainPage(char * Reply, int * ReplyLen, char * Key); +VOID SaveFwdCommon(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SaveFwdDetails(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +char ** SeparateMultiString(char * MultiString, BOOL NoToUpper); +VOID TidyPrompts(); +char * GetTemplateFromFile(int Version, char * FN); +VOID FormatTime(char * Time, time_t cTime); +struct MsgInfo * GetMsgFromNumber(int msgno); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); +int MulticastStatusHTML(char * Reply); +void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen); +int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session); +struct UserInfo * FindBBS(char * Name); +void ReleaseWebMailStruct(WebMailInfo * WebMail); +VOID TidyWelcomeMsg(char ** pPrompt); + +char UNC[] = ""; +char CHKD[] = "checked=checked "; +char sel[] = "selected"; + +char Sent[] = "#98FFA0"; +char ToSend[] = "#FFFF00"; +char NotThisOne[] = "#FFFFFF"; + +static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +static char BusyError[] = "

Sorry, No sessions available - please try later

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

BPQ32 Mail Server %s Access

" + "

Please enter Callsign and Password to access the BBS

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

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

BPQ32 BBS %s

" + "

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

BPQ32 BBS %s

" + "

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

" +"
User     Callsign   Stream Queue
" +"


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

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

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

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

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



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

Update User %s

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

Update Forwarding for BBS %s

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

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

" +" " +"

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

Message %d

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

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

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

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

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

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

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

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

Copy From BBS %s not found

", ptr2); + return; + } + + // Set current info from OldBBS +// +// SetForwardingPage(hDlg, OldBBS); // moved to separate routine as also called from copy config + + SendFwdDetails(OldBBS, Reply, RLen, Session->Key); + return; + } + // Fwd update + + ptr2 = input + 4; + ptr1 = GetNextParam(&ptr2); // TO + FWDInfo->TOCalls = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // AT + FWDInfo->ATCalls = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // TIMES + FWDInfo->FWDTimes = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // FWD SCRIPT + FWDInfo->ConnectScript = SeparateMultiString(ptr1, TRUE); + + ptr1 = GetNextParam(&ptr2); // HRB + FWDInfo->Haddresses = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // HRP + FWDInfo->HaddressesP = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // BBSHA + if (FWDInfo->BBSHA) + free(FWDInfo->BBSHA); + + FWDInfo->BBSHA = _strdup(_strupr(ptr1)); + + ptr1 = GetNextParam(&ptr2); // EnF + if (strcmp(ptr1, "true") == 0) FWDInfo->Enabled = TRUE; else FWDInfo->Enabled = FALSE; + + ptr1 = GetNextParam(&ptr2); // Interval + FWDInfo->FwdInterval = atoi(ptr1); + + ptr1 = GetNextParam(&ptr2); // EnR + if (strcmp(ptr1, "true") == 0) FWDInfo->ReverseFlag = TRUE; else FWDInfo->ReverseFlag = FALSE; + + ptr1 = GetNextParam(&ptr2); // RInterval + FWDInfo->RevFwdInterval = atoi(ptr1); + + ptr1 = GetNextParam(&ptr2); // No Wait + if (strcmp(ptr1, "true") == 0) FWDInfo->SendNew = TRUE; else FWDInfo->SendNew = FALSE; + + ptr1 = GetNextParam(&ptr2); // Blocked + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowBlocked = TRUE; else FWDInfo->AllowBlocked = FALSE; + + ptr1 = GetNextParam(&ptr2); // FBB Block + FWDInfo->MaxFBBBlockSize = atoi(ptr1); + + ptr1 = GetNextParam(&ptr2); // Personals + if (strcmp(ptr1, "true") == 0) FWDInfo->PersonalOnly = TRUE; else FWDInfo->PersonalOnly = FALSE; + ptr1 = GetNextParam(&ptr2); // Binary + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowCompressed = TRUE; else FWDInfo->AllowCompressed = FALSE; + ptr1 = GetNextParam(&ptr2); // B1 + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowB1 = TRUE; else FWDInfo->AllowB1 = FALSE; + ptr1 = GetNextParam(&ptr2); // B2 + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowB2 = TRUE; else FWDInfo->AllowB2 = FALSE; + ptr1 = GetNextParam(&ptr2); // CTRLZ + if (strcmp(ptr1, "true") == 0) FWDInfo->SendCTRLZ = TRUE; else FWDInfo->SendCTRLZ = FALSE; + ptr1 = GetNextParam(&ptr2); // Connect Timeout + FWDInfo->ConTimeout = atoi(ptr1); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + ReinitializeFWDStruct(Session->User); + + SendFwdDetails(Session->User, Reply, RLen, Session->Key); + } +} + + + +VOID ProcessUserUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = Session->User; + int SSID, Mask = 0; + char * ptr1, *ptr2; + int skipRMSExUser = 0; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel")) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + if (strstr(input, "Delete")) + { + int n; + + for (n = 1; n <= NumberofUsers; n++) + { + if (Session->User == UserRecPtr[n]) + break; + } + + if (n <= NumberofUsers) + { + USER = Session->User; + + for (n = n; n < NumberofUsers; n++) + { + UserRecPtr[n] = UserRecPtr[n+1]; // move down all following entries + } + + NumberofUsers--; + + if (USER->flags & F_BBS) // was a BBS? + DeleteBBS(USER); + + free(USER); + + SaveUserDatabase(); + + Session->User = UserRecPtr[1]; + + SendUserSelectPage(Reply, RLen, Session->Key); + return; + } + } + + if (strstr(input, "Add=")) + { + char * Call; + + Call = input + 8; + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + // Null or exists + + SendUserSelectPage(Reply, RLen, Session->Key); + return; + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + SendUserSelectPage(Reply, RLen, Session->Key); + return; + + } + + // User update + + ptr2 = input + 4; + ptr1 = GetNextParam(&ptr2); // BBS + + // If BBS Flag has changed, must set up or delete forwarding info + + if (strcmp(ptr1, "true") == 0) + { + if ((USER->flags & F_BBS) == 0) + { + // New BBS + + if(SetupNewBBS(USER)) + { + USER->flags |= F_BBS; + USER->flags &= ~F_Temp_B2_BBS; // Clear RMS Express User + skipRMSExUser = 1; // Dont read old value + } + else + { + // Failed - too many bbs's defined + + //sprintf(InfoBoxText, "Cannot set user to be a BBS - you already have 80 BBS's defined"); + //DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + USER->flags &= ~F_BBS; + //CheckDlgButton(hDlg, IDC_BBSFLAG, (user->flags & F_BBS)); + } + } + } + else + { + if (USER->flags & F_BBS) + { + //was a BBS + + USER->flags &= ~F_BBS; + DeleteBBS(USER); + } + } + + ptr1 = GetNextParam(&ptr2); // Permit Email + if (strcmp(ptr1, "true") == 0) USER->flags |= F_EMAIL; else USER->flags &= ~F_EMAIL; + + ptr1 = GetNextParam(&ptr2); // PMS + if (strcmp(ptr1, "true") == 0) USER->flags |= F_PMS; else USER->flags &= ~F_PMS; + + ptr1 = GetNextParam(&ptr2); // RMS EX User + if (strcmp(ptr1, "true") == 0 && !skipRMSExUser) USER->flags |= F_Temp_B2_BBS; else USER->flags &= ~F_Temp_B2_BBS; + ptr1 = GetNextParam(&ptr2); // SYSOP + if (strcmp(ptr1, "true") == 0) USER->flags |= F_SYSOP; else USER->flags &= ~F_SYSOP; + ptr1 = GetNextParam(&ptr2); // PollRMS + if (strcmp(ptr1, "true") == 0) USER->flags |= F_POLLRMS; else USER->flags &= ~F_POLLRMS; + ptr1 = GetNextParam(&ptr2); // Expert + if (strcmp(ptr1, "true") == 0) USER->flags |= F_Expert; else USER->flags &= ~F_Expert; + + ptr1 = GetNextParam(&ptr2); // SSID1 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + ptr1 = GetNextParam(&ptr2); // SSID2 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + ptr1 = GetNextParam(&ptr2); // SSID3 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + ptr1 = GetNextParam(&ptr2); // SSID4 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + Session->User->RMSSSIDBits = Mask; + + ptr1 = GetNextParam(&ptr2); // Excluded + if (strcmp(ptr1, "true") == 0) USER->flags |= F_Excluded; else USER->flags &= ~F_Excluded; + ptr1 = GetNextParam(&ptr2); // Hold + if (strcmp(ptr1, "true") == 0) USER->flags |= F_HOLDMAIL; else USER->flags &= ~F_HOLDMAIL; + ptr1 = GetNextParam(&ptr2); // SYSOP gets LM + if (strcmp(ptr1, "true") == 0) USER->flags |= F_SYSOP_IN_LM; else USER->flags &= ~F_SYSOP_IN_LM; + ptr1 = GetNextParam(&ptr2); // Dont add winlink.org + if (strcmp(ptr1, "true") == 0) USER->flags |= F_NOWINLINK; else USER->flags &= ~F_NOWINLINK; + ptr1 = GetNextParam(&ptr2); // Allow Bulls + if (strcmp(ptr1, "true") == 0) USER->flags &= ~F_NOBULLS; else USER->flags |= F_NOBULLS; // Inverted flag + ptr1 = GetNextParam(&ptr2); // NTS Message Pickup Station + if (strcmp(ptr1, "true") == 0) USER->flags |= F_NTSMPS; else USER->flags &= ~F_NTSMPS; + ptr1 = GetNextParam(&ptr2); // APRS Mail For + if (strcmp(ptr1, "true") == 0) USER->flags |= F_RMSREDIRECT; else USER->flags &= ~F_RMSREDIRECT; + ptr1 = GetNextParam(&ptr2); // Redirect to RMS + + if (strcmp(ptr1, "true") == 0) USER->flags |= F_APRSMFOR; else USER->flags &= ~F_APRSMFOR; + + ptr1 = GetNextParam(&ptr2); // APRS SSID + SSID = atoi(ptr1); + SSID &= 15; + USER->flags &= 0x0fffffff; + USER->flags |= (SSID << 28); + + + ptr1 = GetNextParam(&ptr2); // Last Listed + USER->lastmsg = atoi(ptr1); + ptr1 = GetNextParam(&ptr2); // Name + strcpy(USER->Name, ptr1); + ptr1 = GetNextParam(&ptr2); // Pass + strcpy(USER->pass, ptr1); + ptr1 = GetNextParam(&ptr2); // CMS Pass + if (memcmp("****************", ptr1, strlen(ptr1) != 0)) + { + strcpy(USER->CMSPass, ptr1); + } + + ptr1 = GetNextParam(&ptr2); // QTH + strcpy(USER->Address, ptr1); + ptr1 = GetNextParam(&ptr2); // ZIP + strcpy(USER->ZIP, ptr1); + ptr1 = GetNextParam(&ptr2); // HomeBBS + strcpy(USER->HomeBBS, ptr1); + _strupr(USER->HomeBBS); + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + *RLen = SendUserDetails(Session, Reply, Session->Key); + } +} + +VOID ProcessMsgAction(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + int BBSNumber = 0; + struct MsgInfo * Msg = Session->Msg; + char * ptr1; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && Msg) + { + ptr1 = input + 4; + *RLen = SendMessageDetails(Msg, Reply, Session->Key); + } +} + +VOID SaveMessageText(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + struct MsgInfo * Msg = Session->Msg; + char * ptr, * ptr1, * ptr2, *input; + char c; + int MsgLen, WriteLen; + char MsgFile[256]; + FILE * hFile; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = sprintf(Reply, "%s", ""); + return; + } + + ptr = strstr(input, "&Save="); + + if (ptr) + { + *ptr = 0; + + // Undo any % transparency + + ptr1 = ptr2 = input + 8; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + MsgLen = (int)strlen(input + 8); + + Msg->datechanged = time(NULL); + Msg->length = MsgLen; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = (int)fwrite(input + 8, 1, Msg->length, hFile); + fclose(hFile); + } + + if (WriteLen != Msg->length) + { + char Mess[80]; + sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + CriticalErrorHandler(Mess); + + return; + } + + SaveMessageDatabase(); + + *RLen = sprintf(Reply, "%s", ""); + + } + } + return; + +} + + +VOID ProcessMsgUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + int BBSNumber = 0; + struct MsgInfo * Msg = Session->Msg; + char * ptr1, * ptr2; + char OldStatus = Msg->status; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && Msg) + { + ptr1 = input + 4; + ptr2 = strchr(ptr1, '|'); + if (ptr2) + { + *(ptr2++) = 0; + strcpy(Msg->from, ptr1); + ptr1 = ptr2; + } + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->to, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->bid, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->emailfrom, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->via, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->title, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;Msg->type = *ptr1;ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;Msg->status = *ptr1;ptr1 = ptr2;} + + if (Msg->status != OldStatus) + { + // Need to take action if killing message + + if (Msg->status == 'K') + FlagAsKilled(Msg, FALSE); // Clear forwarding bits + } + + Msg->datechanged = time(NULL); + SaveMessageDatabase(); + } + + *RLen = SendMessageDetails(Msg, Reply, Session->Key); +} + + + + +VOID ProcessMsgFwdUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + int BBSNumber = 0; + struct UserInfo * User; + struct MsgInfo * Msg = Session->Msg; + BOOL toforward, forwarded; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && Msg) + { + BBSNumber = atoi(input + 4); + User = BBSLIST[BBSNumber]; + + if (User == NULL) + return; + + toforward = check_fwd_bit(Msg->fbbs, BBSNumber); + forwarded = check_fwd_bit(Msg->forw, BBSNumber); + + if (forwarded) + { + // Changing to not this BBS + + clear_fwd_bit(Msg->forw, BBSNumber); + } + else if (toforward) + { + // Change to Forwarded + + clear_fwd_bit(Msg->fbbs, BBSNumber); + User->ForwardingInfo->MsgCount--; + set_fwd_bit(Msg->forw, BBSNumber); + } + else + { + // Change to to forward + + set_fwd_bit(Msg->fbbs, BBSNumber); + User->ForwardingInfo->MsgCount++; + clear_fwd_bit(Msg->forw, BBSNumber); + if (FirstMessageIndextoForward > Msg->number) + FirstMessageIndextoForward = Msg->number; + + } + *RLen = SendMessageDetails(Msg, Reply, Session->Key); + } + SaveMessageDatabase(); +} + + + + +VOID SetMultiStringValue(char ** values, char * Multi) +{ + char ** Calls; + char * ptr = &Multi[0]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '\r'; + *(ptr++) = '\n'; + Calls++; + } + *(ptr) = 0; + } +} + + + +VOID SendFwdDetails(struct UserInfo * User, char * Reply, int * ReplyLen, char * Key) +{ + int Len; + struct BBSForwardingInfo * FWDInfo = User->ForwardingInfo; + char TO[2048] = ""; + char AT[2048] = ""; + char TIMES[2048] = ""; + char FWD[100000] = ""; + char HRB[2048] = ""; + char HRP[2048] = ""; + + SetMultiStringValue(FWDInfo->TOCalls, TO); + SetMultiStringValue(FWDInfo->ATCalls, AT); + SetMultiStringValue(FWDInfo->FWDTimes, TIMES); + SetMultiStringValue(FWDInfo->ConnectScript, FWD); + SetMultiStringValue(FWDInfo->Haddresses, HRB); + SetMultiStringValue(FWDInfo->HaddressesP, HRP); + + if (FwdDetailTemplate == NULL) + FwdDetailTemplate = GetTemplateFromFile(3, "FwdDetail.txt"); + + Len = sprintf(Reply, FwdDetailTemplate, User->Call, + CountMessagestoForward (User), Key, + TO, AT, TIMES , FWD, HRB, HRP, + (FWDInfo->BBSHA) ? FWDInfo->BBSHA : "", + (FWDInfo->Enabled) ? CHKD : UNC, + FWDInfo->FwdInterval, + (FWDInfo->ReverseFlag) ? CHKD : UNC, + FWDInfo->RevFwdInterval, + (FWDInfo->SendNew) ? CHKD : UNC, + (FWDInfo->AllowBlocked) ? CHKD : UNC, + FWDInfo->MaxFBBBlockSize, + (FWDInfo->PersonalOnly) ? CHKD : UNC, + (FWDInfo->AllowCompressed) ? CHKD : UNC, + (FWDInfo->AllowB1) ? CHKD : UNC, + (FWDInfo->AllowB2) ? CHKD : UNC, + (FWDInfo->SendCTRLZ) ? CHKD : UNC, + FWDInfo->ConTimeout); + + *ReplyLen = Len; + +} + +VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len; + + char HF[2048] = ""; + char HT[2048] = ""; + char HA[2048] = ""; + char HB[2048] = ""; + char RF[2048] = ""; + char RT[2048] = ""; + char RA[2048] = ""; + char RB[2048] = ""; + char WPTO[10000] = ""; + + SetMultiStringValue(RejFrom, RF); + SetMultiStringValue(RejTo, RT); + SetMultiStringValue(RejAt, RA); + SetMultiStringValue(RejBID, RB); + SetMultiStringValue(HoldFrom, HF); + SetMultiStringValue(HoldTo, HT); + SetMultiStringValue(HoldAt, HA); + SetMultiStringValue(HoldBID, HB); + SetMultiStringValue(SendWPAddrs, WPTO); + + + Len = sprintf(Reply, ConfigTemplate, + BBSName, Key, Key, Key, Key, Key, Key, Key, Key, Key, + BBSName, SYSOPCall, HRoute, + (SendBBStoSYSOPCall) ? CHKD : UNC, + BBSApplNum, MaxStreams, + (SendSYStoSYSOPCall) ? CHKD : UNC, + (RefuseBulls) ? CHKD : UNC, + (EnableUI) ? CHKD : UNC, + MailForInterval, + (DontHoldNewUsers) ? CHKD : UNC, + (DefaultNoWINLINK) ? CHKD : UNC, + (AllowAnon) ? CHKD : UNC, + (DontNeedHomeBBS) ? CHKD : UNC, + (DontCheckFromCall) ? CHKD : UNC, + (UserCantKillT) ? UNC : CHKD, // Reverse logic + (ForwardToMe) ? CHKD : UNC, + (OnlyKnown) ? CHKD : UNC, + POP3InPort, SMTPInPort, NNTPInPort, + (RemoteEmail) ? CHKD : UNC, + AMPRDomain, + (SendAMPRDirect) ? CHKD : UNC, + (ISP_Gateway_Enabled) ? CHKD : UNC, + MyDomain, ISPSMTPName, ISPSMTPPort, ISPEHLOName, ISPPOP3Name, ISPPOP3Port, + ISPAccountName, ISPAccountPass, ISPPOP3Interval, + (SMTPAuthNeeded) ? CHKD : UNC, + (SendWP) ? CHKD : UNC, + (FilterWPBulls) ? CHKD : UNC, + (SendWPType == 0) ? CHKD : UNC, + (SendWPType == 1) ? CHKD : UNC, + WPTO, + RF, RT, RA, RB, HF, HT, HA, HB); + + *ReplyLen = Len; +} +VOID SendHouseKeeping(char * Reply, int * ReplyLen, char * Key) +{ + char FromList[1000]= "", ToList[1000]= "", AtList[1000] = ""; + char Line[80]; + struct Override ** Call; + + if (LTFROM) + { + Call = LTFROM; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(FromList, Line); + Call++; + } + } + if (LTTO) + { + Call = LTTO; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(ToList, Line); + Call++; + } + } + + if (LTAT) + { + Call = LTAT; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(AtList, Line); + Call++; + } + } + + *ReplyLen = sprintf(Reply, HousekeepingTemplate, + BBSName, Key, Key, Key, Key, Key, Key, Key, Key, Key, + MaintTime, MaintInterval, MaxMsgno, BidLifetime, LogAge, UserLifetime, + (DeletetoRecycleBin) ? CHKD : UNC, + (SendNonDeliveryMsgs) ? CHKD : UNC, + (SuppressMaintEmail) ? CHKD : UNC, + (GenerateTrafficReport) ? CHKD : UNC, + PR, PUR, PF, PNF, BF, BNF, NTSD, NTSF, NTSU, + FromList, ToList, AtList, + (OverrideUnsent) ? CHKD : UNC); + + return; + +} + + +VOID SendWelcomePage(char * Reply, int * ReplyLen, char * Key) +{ + int Len; + + Len = SendHeader(Reply, Key); + + Len += sprintf(&Reply[Len], Welcome, Key, WelcomeMsg, NewWelcomeMsg, ExpertWelcomeMsg, + Prompt, NewPrompt, ExpertPrompt, SignoffMsg); + *ReplyLen = Len; +} + +VOID SendFwdMainPage(char * Reply, int * RLen, char * Key) +{ + char ALIASES[16384]; + + SetMultiStringValue(AliasText, ALIASES); + + *RLen = sprintf(Reply, FwdTemplate, Key, Key, BBSName, + Key, Key, Key, Key, Key, Key, Key, Key, + Key, MaxTXSize, MaxRXSize, MaxAge, + (WarnNoRoute) ? CHKD : UNC, + (Localtime) ? CHKD : UNC, + (SendPtoMultiple) ? CHKD : UNC, + ALIASES); +} + + +char TenSpaces[] = "          "; + +VOID SendUIPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len, i; + + Len = SendHeader(Reply, Key); + Len += sprintf(&Reply[Len], UIHddr, Key, MailForText); + + for (i = 1; i <= GetNumberofPorts(); i++) + { + char PortNo[512]; + char PortDesc[31]; + int n; + + // Only allow UI on ax.25 ports + + struct _EXTPORTDATA * PORTVEC; + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + if (PORTVEC->PORTCONTROL.UICAPABLE == 0) + continue; + + + GetPortDescription(i, PortDesc); + n = sprintf(PortNo, "Port %2d %s", GetPortNumber(i), PortDesc); + + while (PortNo[--n] == ' '); + + PortNo[n + 1] = 0; + + while (n++ < 38) + strcat(PortNo, " "); + + Len += sprintf(&Reply[Len], UILine, + (UIEnabled[i])?CHKD:UNC, i, + PortNo, + (UIDigi[i])?UIDigi[i]:"", i, + (UIMF[i])?CHKD:UNC, i, + (UIHDDR[i])?CHKD:UNC, i, + (UINull[i])?CHKD:UNC, i); + } + + Len += sprintf(&Reply[Len], UITail, Key); + + *ReplyLen = Len; +} + +VOID SendStatusPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len; + char msg[1024]; + CIRCUIT * conn; + int i,n, SYSOPMsgs = 0, HeldMsgs = 0; + char Name[80]; + + SMTPMsgs = 0; + + Len = sprintf(Reply, RefreshMainPage, BBSName, BBSName, Key, Key, Key, Key, Key, Key, Key, Key); + + Len += sprintf(&Reply[Len], StatusPage, Key); + + for (n = 0; n < NumberofStreams; n++) + { + conn=&Connections[n]; + + if (!conn->Active) + { + strcpy(msg,"Idle          " + "         " + "         \r\n"); + } + else + { + { + if (conn->UserPointer == 0) + strcpy(msg,"Logging in\r\n"); + else + { + strcpy(Name, conn->UserPointer->Name); + Name[9] = 0; + + i=sprintf_s(msg, sizeof(msg), "%s%s%s%s%2d %5d\r\n", + Name, + &TenSpaces[strlen(Name) * 6], + conn->UserPointer->Call, + &TenSpaces[strlen(conn->UserPointer->Call) * 6], + conn->BPQStream, + conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + Len += sprintf(&Reply[Len], StatusLine, conn->BPQStream, msg); + } + + n = 0; + + for (i=1; i <= NumberofMessages; i++) + { + if (MsgHddrPtr[i]->status == 'N') + { + if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) + SYSOPMsgs++; + else + if (MsgHddrPtr[i]->to[0] == 0) + SMTPMsgs++; + } + else + { + if (MsgHddrPtr[i]->status == 'H') + HeldMsgs++; + } + } + + Len += sprintf(&Reply[Len], StreamEnd, + NumberofMessages, SYSOPMsgs, HeldMsgs, SMTPMsgs); + + // If there are any active multicast transfers, display them. + + Len += MulticastStatusHTML(&Reply[Len]); + + Len += sprintf(&Reply[Len], StatusTail, + NumberofMessages, SYSOPMsgs, HeldMsgs, SMTPMsgs); + + *ReplyLen = Len; +} + +VOID SendFwdSelectPage(char * Reply, int * ReplyLen, char * Key) +{ + struct UserInfo * USER; + int i = 0; + int Len = 0; + + for (USER = BBSChain; USER; USER = USER->BBSNext) + { + Len += sprintf(&Reply[Len], "%s|", USER->Call); + } + + *ReplyLen = Len; +} + +VOID SendUserSelectPage(char * Reply, int * ReplyLen, char * Key) +{ + struct UserInfo * USER; + int i = 0, n; + int Len = 0; + struct UserInfo * users[10000]; + + // Get array of addresses + + for (n = 1; n <= NumberofUsers; n++) + { + users[i++] = UserRecPtr[n]; + if (i > 9999) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + for (n = 0; n < NumberofUsers; n++) + { + USER = users[n]; + Len += sprintf(&Reply[Len], "%s|", USER->Call); + } + *ReplyLen = Len; +} + +int SendUserDetails(struct HTTPConnectionInfo * Session, char * Reply, char * Key) +{ + char SSID[16][16] = {""}; + char ASSID[16]; + int i, n, s, Len; + struct UserInfo * User = Session->User; + unsigned int flags = User->flags; + int RMSSSIDBits = Session->User->RMSSSIDBits; + char HiddenPass[20] = ""; + + int ConnectsIn; + int ConnectsOut; + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; + int MsgsRejectedOut; + int BytesForwardedIn; + int BytesForwardedOut; +// char MsgsIn[80]; +// char MsgsOut[80]; +// char BytesIn[80]; +// char BytesOut[80]; +// char RejIn[80]; +// char RejOut[80]; + + i = 0; + + ConnectsIn = User->Total.ConnectsIn - User->Last.ConnectsIn; + ConnectsOut = User->Total.ConnectsOut - User->Last.ConnectsOut; + + MsgsReceived = MsgsSent = MsgsRejectedIn = MsgsRejectedOut = BytesForwardedIn = BytesForwardedOut = 0; + + for (n = 0; n < 4; n++) + { + MsgsReceived += User->Total.MsgsReceived[n] - User->Last.MsgsReceived[n]; + MsgsSent += User->Total.MsgsSent[n] - User->Last.MsgsSent[n]; + BytesForwardedIn += User->Total.BytesForwardedIn[n] - User->Last.BytesForwardedIn[n]; + BytesForwardedOut += User->Total.BytesForwardedOut[n] - User->Last.BytesForwardedOut[n]; + MsgsRejectedIn += User->Total.MsgsRejectedIn[n] - User->Last.MsgsRejectedIn[n]; + MsgsRejectedOut += User->Total.MsgsRejectedOut[n] - User->Last.MsgsRejectedOut[n]; + } + + + for (s = 0; s < 16; s++) + { + if (RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(&SSID[i++][0], "%d", s); + else + SSID[i++][0] = 0; + } + } + + memset(HiddenPass, '*', strlen(User->CMSPass)); + + i = (flags >> 28); + sprintf(ASSID, "%d", i); + + if (i == 0) + ASSID[0] = 0; + + if (!UserDetailTemplate) + UserDetailTemplate = GetTemplateFromFile(4, "UserDetail.txt"); + + Len = sprintf(Reply, UserDetailTemplate, Key, User->Call, + (flags & F_BBS)?CHKD:UNC, + (flags & F_EMAIL)?CHKD:UNC, + (flags & F_PMS)?CHKD:UNC, + (flags & F_Temp_B2_BBS)?CHKD:UNC, + (flags & F_SYSOP)?CHKD:UNC, + (flags & F_POLLRMS)?CHKD:UNC, + (flags & F_Expert)?CHKD:UNC, + SSID[0], SSID[1], SSID[2], SSID[3], + (flags & F_Excluded)?CHKD:UNC, + (flags & F_HOLDMAIL)?CHKD:UNC, + (flags & F_SYSOP_IN_LM)?CHKD:UNC, + (flags & F_NOWINLINK)?CHKD:UNC, + (flags & F_NOBULLS)?UNC:CHKD, // Inverted flag + (flags & F_NTSMPS)?CHKD:UNC, + (flags & F_RMSREDIRECT)?CHKD:UNC, + (flags & F_APRSMFOR)?CHKD:UNC, ASSID, + + ConnectsIn, MsgsReceived, MsgsRejectedIn, + ConnectsOut, MsgsSent, MsgsRejectedOut, + BytesForwardedIn, FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + BytesForwardedOut, User->lastmsg, + User->Name, + User->pass, + HiddenPass, + User->Address, + User->ZIP, + User->HomeBBS); + + return Len; +} + +#ifdef WIN32 + +static char PipeFileName[] = "\\\\.\\pipe\\BPQMailWebPipe"; + +static DWORD WINAPI InstanceThread(LPVOID lpvParam) + +// This routine is a thread processing function to read from and reply to a client +// via the open pipe connection passed from the main loop. Note this allows +// the main loop to continue executing, potentially creating more threads of +// of this procedure to run concurrently, depending on the number of incoming +// client connections. +{ + DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; + BOOL fSuccess = FALSE; + HANDLE hPipe = NULL; + char Buffer[250000]; + char OutBuffer[250000]; + char * MsgPtr; + int InputLen = 0; + int OutputLen = 0; + struct HTTPConnectionInfo Session; + char URL[100001]; + char * Context, * Method; + int n; + + char * ptr; + +// Debugprintf("InstanceThread created, receiving and processing messages."); + +// The thread's parameter is a handle to a pipe object instance. + + hPipe = (HANDLE) lpvParam; + + // Read client requests from the pipe. This simplistic code only allows messages + // up to BUFSIZE characters in length. + + n = ReadFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL); + fSuccess = ReadFile(hPipe, Buffer, 250000, &InputLen, NULL); + + if (!fSuccess || InputLen == 0) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + Debugprintf("InstanceThread: client disconnected.", GetLastError()); + else + Debugprintf("InstanceThread ReadFile failed, GLE=%d.", GetLastError()); + } + else + { + Buffer[InputLen] = 0; + + MsgPtr = &Buffer[0]; + + strcpy(URL, MsgPtr); + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + Method = strtok_s(URL, " ", &Context); + + ProcessMailHTTPMessage(&Session, Method, Context, MsgPtr, OutBuffer, &OutputLen, InputLen); + + WriteFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL); + WriteFile(hPipe, OutBuffer, OutputLen, &cbWritten, NULL); + + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + } + return 1; +} + +static DWORD WINAPI PipeThreadProc(LPVOID lpvParam) +{ + BOOL fConnected = FALSE; + DWORD dwThreadId = 0; + HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL; + +// The main loop creates an instance of the named pipe and +// then waits for a client to connect to it. When the client +// connects, a thread is created to handle communications +// with that client, and this loop is free to wait for the +// next client connect request. It is an infinite loop. + + for (;;) + { + hPipe = CreateNamedPipe( + PipeFileName, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_BYTE | // message type pipe + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + 4096, // output buffer size + 4096, // input buffer size + 0, // client time-out + NULL); // default security attribute + + if (hPipe == INVALID_HANDLE_VALUE) + { + Debugprintf("CreateNamedPipe failed, GLE=%d.\n", GetLastError()); + return -1; + } + + // Wait for the client to connect; if it succeeds, + // the function returns a nonzero value. If the function + // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. + + fConnected = ConnectNamedPipe(hPipe, NULL) ? + TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + if (fConnected) + { + // Create a thread for this client. + + hThread = CreateThread( + NULL, // no security attribute + 0, // default stack size + InstanceThread, // thread proc + (LPVOID) hPipe, // thread parameter + 0, // not suspended + &dwThreadId); // returns thread ID + + if (hThread == NULL) + { + Debugprintf("CreateThread failed, GLE=%d.\n", GetLastError()); + return -1; + } + else CloseHandle(hThread); + } + else + // The client could not connect, so close the pipe. + CloseHandle(hPipe); + } + + return 0; +} + +BOOL CreatePipeThread() +{ + DWORD ThreadId; + CreateThread(NULL, 0, PipeThreadProc, 0, 0, &ThreadId); + return TRUE; +} + +#endif + +char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + +VOID FormatTime(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); +} + + + + + + + diff --git a/BBSUtilities.c b/BBSUtilities.c new file mode 100644 index 0000000..6ecad7a --- /dev/null +++ b/BBSUtilities.c @@ -0,0 +1,14057 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Utility Routines + +#include "bpqmail.h" +#ifdef WIN32 +#include "Winspool.h" +#else +#include +#endif + + +BOOL Bells; +BOOL FlashOnBell; // Flash instead of Beep +BOOL StripLF; + +BOOL WarnWrap; +BOOL FlashOnConnect; +BOOL WrapInput; +BOOL CloseWindowOnBye; + +RECT ConsoleRect; + +BOOL OpenConsole; +BOOL OpenMon; + +extern struct ConsoleInfo BBSConsole; + +extern char LOC[7]; + +//#define BBSIDLETIME 120 +//#define USERIDLETIME 300 + + +#define BBSIDLETIME 900 +#define USERIDLETIME 900 + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +UCHAR * GetLogDirectory(); +DllExport int APIENTRY SessionStateNoAck(int stream, int * state); +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; +typedef char * (WINAPI FAR *FARPROCZ)(); +FARPROCZ pGetLOC; + +#endif + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +void DeletetoRecycle(char * FN); +VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID TidyPrompts(); +char * ReadMessageFileEx(struct MsgInfo * MsgRec); +char * APIENTRY GetBPQDirectory(); +BOOL SendARQMail(CIRCUIT * conn); +int APIENTRY ChangeSessionIdletime(int Stream, int idletime); +int APIENTRY GetApplNum(int Stream); +VOID FormatTime(char * Time, time_t cTime); +BOOL CheckifPacket(char * Via); +char * APIENTRY GetVersionString(); +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +int GetCMSHash(char * Challenge, char * Password); +BOOL SendAMPRSMTP(CIRCUIT * conn); +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); +VOID MCastTimer(); +VOID MCastConTimer(ConnectionInfo * conn); +int FindFreeBBSNumber(); +VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL ProcessYAPPMessage(CIRCUIT * conn); +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void YAPPSendData(ConnectionInfo * conn); +VOID CheckBBSNumber(int i); +struct UserInfo * FindAMPR(); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +char *stristr (char *ch1, char *ch2); +BOOL CheckforMessagetoServer(struct MsgInfo * Msg); +void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); +void ListCategories(ConnectionInfo * conn); +void RebuildNNTPList(); +long long GetInt64Value(config_setting_t * group, char * name); +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +int ReformatSyncMessage(CIRCUIT * conn); +char * initMultipartUnpack(char ** Input); + +config_t cfg; +config_setting_t * group; + +extern ULONG BBSApplMask; + +//static int SEMCLASHES = 0; + +char SecureMsg[80] = ""; // CMS Secure Signon Response + +int NumberofStreams; + +extern char VersionStringWithBuild[50]; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +extern ConnectionInfo Connections[MaxSockets+1]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +extern char MailDir[MAX_PATH]; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern BIDRec ** TempBIDRecPtr; +extern int NumberofTempBIDs; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern char ** BadWords; +extern int NumberofBadWords; +extern char * BadFile; + +extern int LatestMsg; +extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg +extern int HighestBBSNumber; + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; + +extern int ProgramErrors; + +extern BOOL MonBBS; +extern BOOL MonCHAT; +extern BOOL MonTCP; + +BOOL SendNewUserMessage = TRUE; +BOOL AllowAnon = FALSE; +BOOL UserCantKillT = FALSE; + +#define BPQHOSTSTREAMS 64 + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +extern char WL2KModes [54][18]; +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; + + +char WL2KModes [54][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; +#endif + + + + + +FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; + +time_t LastLogTime[4] = {0, 0, 0, 0}; + +char FilesNames[4][100] = {"", "", "", ""}; + +char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + + +BOOL OpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +typedef int (WINAPI FAR *FARPROCX)(); + +extern FARPROCX pDllBPQTRACE; + +struct SEM LogSEM = {0, 0}; + +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t LT; +// struct _EXCEPTION_POINTERS exinfo; + + // Write to Node BPQTRACE system + + if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = 33; + Monframe.LENGTH = 12 + MsgLen; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], Msg, MsgLen); + Monframe.DEST[1 + MsgLen] = 0; + + time(&Monframe.Timestamp); +#ifdef LINBPQ + GetSemaphore(&Semaphore, 88); + BPQTRACE(&Monframe, FALSE); + FreeSemaphore(&Semaphore); +#else + if (pDllBPQTRACE) + pDllBPQTRACE(&Monframe, FALSE); +#endif + } +#ifndef LINBPQ + __try + { +#endif + + + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_TCP && MonTCP) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_CHAT && MonCHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_BBS && MonBBS) + { + WritetoMonitorWindow((char *)&Flag, 1); + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUG_X) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + } +#endif + + if (Flags == LOG_TCP && !LogTCP) + return; + if (Flags == LOG_BBS && !LogBBS) + return; + if (Flags == LOG_CHAT && !LogCHAT) + return; + + GetSemaphore(&LogSEM, 0); + + if (LogHandle[Flags] == NULL) + OpenLogfile(Flags); + + if (LogHandle[Flags] == NULL) + { + FreeSemaphore(&LogSEM); + return; + } + LT = time(NULL); + tm = gmtime(<); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + // Don't close/reopen logs every time + + if ((LT - LastLogTime[Flags]) > 60) + { + LastLogTime[Flags] = LT; + fclose(LogHandle[Flags]); + LogHandle[Flags] = NULL; + } + FreeSemaphore(&LogSEM); + +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif +} + +int CriticalErrorHandler(char * error) +{ + Debugprintf("Critical Error %s", error); + ProgramErrors = 25; + CheckProgramErrors(); // Force close + return 0; +} + +BOOL CheckForTooManyErrors(ConnectionInfo * conn) +{ + conn->ErrorCount++; + + if (conn->ErrorCount > 4) + { + BBSputs(conn, "Too many errors - closing\r"); + conn->CloseAfterFlush = 20; + return TRUE; + } + return FALSE; +} + + + + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); +#ifndef LINBPQ + WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); +#endif + // #ifdef _DEBUG + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + +// #endif + return; +} + +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); + WriteLogLine(conn, InOut, Mess, Len, LogMode); + + return; +} + +struct MsgInfo * GetMsgFromNumber(int msgno) +{ + if (msgno < 1 || msgno > 999999) + return NULL; + + return MsgnotoMsg[msgno]; +} + +struct UserInfo * AllocateUserRecord(char * Call) +{ + struct UserInfo * User = zalloc(sizeof (struct UserInfo)); + + strcpy(User->Call, Call); + User->Length = sizeof (struct UserInfo); + + GetSemaphore(&AllocSemaphore, 0); + + UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); + UserRecPtr[NumberofUsers]= User; + + FreeSemaphore(&AllocSemaphore); + + return User; +} + +struct MsgInfo * AllocateMsgRecord() +{ + struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); + + GetSemaphore(&AllocSemaphore, 0); + + MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); + MsgHddrPtr[NumberofMessages] = Msg; + + FreeSemaphore(&AllocSemaphore); + + return Msg; +} + +BIDRec * AllocateBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); + BIDRecPtr[NumberofBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +BIDRec * AllocateTempBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); + TempBIDRecPtr[NumberofTempBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +struct UserInfo * LookupCall(char * Call) +{ + struct UserInfo * ptr = NULL; + int i; + + for (i=1; i <= NumberofUsers; i++) + { + ptr = UserRecPtr[i]; + + if (_stricmp(ptr->Call, Call) == 0) return ptr; + + } + + return NULL; +} + +int GetNetInt(char * Line) +{ + char temp[1024]; + char * ptr = strlop(Line, ','); + int n = atoi(Line); + if (ptr == NULL) + Line[0] = 0; + else + { + strcpy(temp, ptr); + strcpy(Line, temp); + } + return n; +} + +VOID GetUserDatabase() +{ + struct UserInfo UserRec; + + FILE * Handle; + size_t ReadLen; + struct UserInfo * user; + time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep + int i; + + // See if user config is in main config + + group = config_lookup (&cfg, "BBSUsers"); + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * stats; + struct MsgStats * Stats; + char * ptr, * ptr2; + + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + UserRecPtr = malloc(sizeof(void *)); + UserRecPtr[0] = malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + while (entry) + { + char call[16]; + + // entry->name is call, will have * in front if a call stating woth number + + if (entry->name[0] == '*') + strcpy(call, &entry->name[1]); + else + strcpy(call, entry->name); + + user = AllocateUserRecord(call); + + ptr = entry->value.sval; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Name, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Address, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->HomeBBS, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->QRA, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->pass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->ZIP, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->CMSPass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->lastmsg = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->flags = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->PageLen = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->BBSNumber = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->RMSSSIDBits = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->WebSeqNo = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->TimeLastConnected = atol(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) Stats = &user->Total; + stats = ptr; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + Stats = &user->Last; + stats = ptr2; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + + if ((user->flags & F_BBS) == 0) // Not BBS - Check Age + { + if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected + { + if (user->TimeLastConnected < UserLimit) + { + // Too Old - ignore + + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + } + } + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + + entry = config_setting_get_elem (group, index++); + } + } + else + { + Handle = fopen(UserDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&UserRec, 0, sizeof (struct UserInfo)); + UserRec.Length = sizeof (struct UserInfo); + } + else + { + // See if format has changed + + if (UserRec.Length == 0) + { + // Old format without a Length field + + struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; + int Users = OldRec->ConnectsIn; // User Count in control record + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak + + Handle = fopen(UserDatabasePath, "rb"); + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +OldNext: + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); + + if (ReadLen > 0) + { + if (OldRec->Call[0] < '0') + goto OldNext; // Blank record + + user = AllocateUserRecord(OldRec->Call); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + // Copy info from Old record + + user->lastmsg = OldRec->lastmsg; + user->Total.ConnectsIn = OldRec->ConnectsIn; + user->TimeLastConnected = OldRec->TimeLastConnected; + user->flags = OldRec->flags; + user->PageLen = OldRec->PageLen; + user->BBSNumber = OldRec->BBSNumber; + memcpy(user->Name, OldRec->Name, 18); + memcpy(user->Address, OldRec->Address, 61); + user->Total.MsgsReceived[0] = OldRec->MsgsReceived; + user->Total.MsgsSent[0] = OldRec->MsgsSent; + user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject + user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end + user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; + user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; + user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out + user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS + memcpy(user->HomeBBS, OldRec->HomeBBS, 41); + memcpy(user->QRA, OldRec->QRA, 7); + memcpy(user->pass, OldRec->pass, 13); + memcpy(user->ZIP, OldRec->ZIP, 9); + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Defined as BBS - allocate and initialise forwarding structure + + // Add to BBS Chain; + + user->BBSNext = BBSChain; + BBSChain = user; + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; + } + goto OldNext; + } + + SortBBSChain(); + fclose(Handle); + + return; + } + } + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +Next: + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen > 0) + { + if (UserRec.Call[0] < '0') + goto Next; // Blank record + + if (UserRec.TimeLastConnected == 0) + UserRec.TimeLastConnected = UserRec.xTimeLastConnected; + + if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age + if (UserLifetime) // if limit set + if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected + if (UserRec.TimeLastConnected < UserLimit) + goto Next; // Too Old - ignore + + user = AllocateUserRecord(UserRec.Call); + memcpy(user, &UserRec, sizeof (UserRec)); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + user->ForwardingInfo = NULL; // In case left behind on crash + user->BBSNext = NULL; + user->POP3Locked = FALSE; + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + goto Next; + } + fclose(Handle); + } + + // Setting up BBS struct has been moved until all user record + // have been read so we can fix corrupt BBSNUmber + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Add to BBS Chain; + + if (user->BBSNumber == NBBBS) // Fix corrupt records + { + user->BBSNumber = FindFreeBBSNumber(); + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + } + + user->BBSNext = BBSChain; + BBSChain = user; + +// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) + HighestBBSNumber = user->BBSNumber; + } + } + + // Check for dulicate BBS numbers + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_BBS) + CheckBBSNumber(user->BBSNumber); + } + + SortBBSChain(); +} + +VOID CopyUserDatabase() +{ + return; // User config now in main config file +/* + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak +*/ +} + +VOID CopyConfigFile(char * ConfigName) +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak +} + + + +VOID SaveUserDatabase() +{ + SaveConfig(ConfigName); // User config is now in main config file + GetConfig(ConfigName); + +/* + FILE * Handle; + size_t WriteLen; + int i; + + Handle = fopen(UserDatabasePath, "wb"); + + UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; + + for (i=0; i <= NumberofUsers; i++) + { + WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); + } + + fclose(Handle); +*/ + return; +} + +VOID GetMessageDatabase() +{ + struct MsgInfo MsgRec; + FILE * Handle; + size_t ReadLen; + struct MsgInfo * Msg; + char * MsgBytes; + int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating + BOOL Reformatting = FALSE; + char HEX[3] = ""; + int n; + + // See if Message Database is in main config + + group = config_lookup (&cfg, "MSGS"); + +// group = 0; + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * ptr, * ptr2; + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + if (entry) + { + // First Record has current message number + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + ptr2 = strlop(ptr2, '|'); + if (ptr2) + LatestMsg = atoi(ptr2); + } + + entry = config_setting_get_elem (group, index++); + + while (entry) + { + // entry->name is MsgNo with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + memset(&MsgRec, 0, sizeof(struct MsgInfo)); + + MsgRec.number = atoi(&entry->name[1]); + MsgRec.type = ptr[0]; + + ptr = ptr2; + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + ptr2 = strlop(ptr, '|'); + MsgRec.status = ptr[0]; + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.length = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datereceived = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bbsfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.via, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.from, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.to, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bid, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.B2Flags = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datecreated = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datechanged = atol(ptr); + + ptr = ptr2; + if (ptr) ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.emailfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.UTF8 = atoi(ptr); + + ptr = ptr2; + + if (ptr) + { + strcpy(MsgRec.title, ptr); + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + free(MsgBytes); + Msg = AllocateMsgRecord(); + memcpy(Msg, &MsgRec, sizeof (MsgRec)); + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + BuildNNTPList(Msg); // Build NNTP Groups list + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + } + } + entry = config_setting_get_elem (group, index++); + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + return; + } + + Handle = fopen(MsgDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + return; + } + + // Get First Record + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&MsgRec, 0, sizeof (MsgRec)); + MsgRec.status = 2; + } + + // Set up control record + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= malloc(sizeof (MsgRec)); + memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); + + LatestMsg=MsgHddrPtr[0]->length; + + NumberofMessages = 0; + + if (MsgRec.status == 1) // Used as file format version + // 0 = original, 1 = Extra email from addr, 2 = More BBS's. + { + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat + + Handle = fopen(MsgDatabasePath, "rb"); + + FileRecsize = sizeof(struct OldMsgInfo); + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + MsgHddrPtr[0]->status = 2; + } + +Next: + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen > 0) + { + // Validate Header + + if (FileRecsize == sizeof(struct MsgInfo)) + { + if (MsgRec.type == 0 || MsgRec.number == 0) + goto Next; + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + // MsgRec.length = strlen(MsgBytes); + free(MsgBytes); + } + else + goto Next; + + Msg = AllocateMsgRecord(); + + memcpy(Msg, &MsgRec, +sizeof (MsgRec)); + } + else + { + // Resizing - record from file is an OldRecInfo + + struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; + + if (OldMessage->type == 0) + goto Next; + + if (OldMessage->number > 99999 || OldMessage->number < 1) + goto Next; + + Msg = AllocateMsgRecord(); + + + Msg->B2Flags = OldMessage->B2Flags; + memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); + memcpy(Msg->bid, OldMessage->bid, 13); + Msg->datechanged = OldMessage->datechanged; + Msg->datecreated = OldMessage->datecreated; + Msg->datereceived = OldMessage->datereceived; + memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); + memcpy(Msg->fbbs , OldMessage->fbbs, 10); + memcpy(Msg->forw , OldMessage->forw, 10); + memcpy(Msg->from, OldMessage->from, 7); + Msg->length = OldMessage->length; + Msg->nntpnum = OldMessage->nntpnum; + Msg->number = OldMessage->number; + Msg->status = OldMessage->status; + memcpy(Msg->title, OldMessage->title, 61); + memcpy(Msg->to, OldMessage->to, 7); + Msg->type = OldMessage->type; + memcpy(Msg->via, OldMessage->via, 41); + } + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + // Move Dates if first run with new format + + if (Msg->datecreated == 0) + Msg->datecreated = Msg->xdatecreated; + + if (Msg->datereceived == 0) + Msg->datereceived = Msg->xdatereceived; + + if (Msg->datechanged == 0) + Msg->datechanged = Msg->xdatechanged; + + BuildNNTPList(Msg); // Build NNTP Groups list + + Msg->Locked = 0; // In case left locked + Msg->Defered = 0; // In case left set. + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + + goto Next; + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + fclose(Handle); +} + +VOID CopyMessageDatabase() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + +// return; + + // Keep 4 Generations + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak"); + + CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak + +} + +VOID SaveMessageDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + char Key[16]; + struct MsgInfo *Msg; +// char CfgName[MAX_PATH]; + char HEXString1[64]; + char HEXString2[64]; + int n; +// char * CfgBuffer; + char Cfg[1024]; +// int CfgLen = 0; +// FILE * hFile; + +// SaveConfig(ConfigName); // Message Headers now in main config +// return; + + Handle = fopen(MsgDatabasePath, "wb"); + + if (Handle == NULL) + { + CriticalErrorHandler("Failed to open message database"); + return; + } + + MsgHddrPtr[0]->status = 2; + MsgHddrPtr[0]->number = NumberofMessages; + MsgHddrPtr[0]->length = LatestMsg; + + for (i=0; i <= NumberofMessages; i++) + { + WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); + + if (WriteLen != sizeof(struct MsgInfo)) + { + CriticalErrorHandler("Failed to write message database record"); + return; + } + } + + if (fclose(Handle) != 0) + CriticalErrorHandler("Failed to close message database"); + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d:\r\n", i); + + n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + } + + return; +} + +VOID GetBIDDatabase() +{ + BIDRec BIDRec; + FILE * Handle; + size_t ReadLen; + BIDRecP BID; + int index = 0; + char * ptr, * ptr2; + + // If BID info is in main config file, use it + + group = config_lookup (&cfg, "BIDS"); + + if (group) + { + config_setting_t * entry = config_setting_get_elem (group, index++); + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + while (entry) + { + // entry->name is Bid with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + if (ptr && ptr2) + { + BID = AllocateBIDRecord(); + strcpy(BID->BID, &entry->name[1]); + BID->mode = atoi(ptr); + BID->u.timestamp = atoi(ptr2); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + } + entry = config_setting_get_elem (group, index++); + } + return; + } + + Handle = fopen(BIDDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&BIDRec, 0, sizeof (BIDRec)); + } + + // Set up control record + + BIDRecPtr = malloc(sizeof(void *)); + BIDRecPtr[0] = malloc(sizeof (BIDRec)); + memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); + + NumberofBIDs = 0; + +Next: + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen > 0) + { + BID = AllocateBIDRecord(); + memcpy(BID, &BIDRec, sizeof (BIDRec)); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + goto Next; + } + + fclose(Handle); +} + +VOID CopyBIDDatabase() +{ + char Backup[MAX_PATH]; + +// return; + + + strcpy(Backup, BIDDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(BIDDatabasePath, Backup, FALSE); +} + +VOID SaveBIDDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + +// return; // Bids are now in main config and are saved when message is saved + + Handle = fopen(BIDDatabasePath, "wb"); + + BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size + + for (i=0; i <= NumberofBIDs; i++) + { + WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); + } + + fclose(Handle); + + return; +} + +BIDRec * LookupBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofBIDs; i++) + { + ptr = BIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) + return ptr; + } + + return NULL; +} + +BIDRec * LookupTempBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofTempBIDs; i++) + { + ptr = TempBIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) return ptr; + } + + return NULL; +} + +VOID RemoveTempBIDS(CIRCUIT * conn) +{ + // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed + + if (NumberofTempBIDs == 0) + return; + else + { + BIDRec * ptr = NULL; + BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); + int i = 0, n; + + GetSemaphore(&AllocSemaphore, 0); + + for (n = 1; n <= NumberofTempBIDs; n++) + { + ptr = TempBIDRecPtr[n]; + + if (ptr) + { + if (ptr->u.conn == conn) + // Remove this entry + free(ptr); + else + NewTempBIDRecPtr[++i] = ptr; + } + } + + NumberofTempBIDs = i; + + free(TempBIDRecPtr); + + TempBIDRecPtr = NewTempBIDRecPtr; + FreeSemaphore(&AllocSemaphore); + } + +} + +VOID GetBadWordFile() +{ + FILE * Handle; + DWORD FileSize; + char * ptr1, * ptr2; + struct stat STAT; + + if (stat(BadWordsPath, &STAT) == -1) + return; + + FileSize = STAT.st_size; + + Handle = fopen(BadWordsPath, "rb"); + + if (Handle == NULL) + return; + + // Release old info in case a re-read + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + BadWords = NULL; + BadFile = NULL; + NumberofBadWords = 0; + + BadFile = malloc(FileSize+1); + + fread(BadFile, 1, FileSize, Handle); + + fclose(Handle); + + BadFile[FileSize]=0; + + _strlwr(BadFile); // Compares are case-insensitive + + ptr1 = BadFile; + + while (ptr1) + { + if (*ptr1 == '\n') ptr1++; + + ptr2 = strtok_s(NULL, "\r\n", &ptr1); + if (ptr2) + { + if (*ptr2 != '#') + { + BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); + BadWords[NumberofBadWords] = ptr2; + } + } + else + break; + } +} + +BOOL CheckBadWord(char * Word, char * Msg) +{ + char * ptr1 = Msg, * ptr2; + size_t len = strlen(Word); + + while (*ptr1) // Stop at end + { + ptr2 = strstr(ptr1, Word); + + if (ptr2 == NULL) + return FALSE; // OK + + // Only bad if it ia not part of a longer word + + if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before + if (!(isalpha(*(ptr2 + len)))) // No alpha after + return TRUE; // Bad word + + // Keep searching + + ptr1 = ptr2 + len; + } + + return FALSE; // OK +} + +BOOL CheckBadWords(char * Msg) +{ + char * dupMsg = _strlwr(_strdup(Msg)); + int i; + + for (i = 1; i <= NumberofBadWords; i++) + { + if (CheckBadWord(BadWords[i], dupMsg)) + { + free(dupMsg); + return TRUE; // Bad + } + } + + free(dupMsg); + return FALSE; // OK + +} + +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); + else + ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); + + if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) + BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); + +// if (user->flags & F_Temp_B2_BBS) +// nodeprintf(conn, "%s CMS >\r", BBSName); +// else + SendPrompt(conn, user); +} + +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->Temp->ListSuspended) + return; // Dont send prompt if pausing a listing + + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); + else + ExpandAndSendMessage(conn, Prompt, LOG_BBS); + +// if (user->flags & F_Expert) +// nodeprintf(conn, "%s\r", ExpertPrompt); +// else if (conn->NewUser) +// nodeprintf(conn, "%s\r", NewPrompt); +// else +// nodeprintf(conn, "%s\r", Prompt); +} + + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + +BOOL isAMPRMsg(char * Addr) +{ + // See if message is addressed to ampr.org and is either + // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) + + size_t toLen = strlen(Addr); + + if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) + { + // message is for ampr.org + + char toCall[48]; + char * via; + + strcpy(toCall, _strupr(Addr)); + + via = strlop(toCall, '@'); + + if (_stricmp(via, AMPRDomain) == 0) + { + // message is for us. + + return TRUE; + } + + if (SendAMPRDirect) + { + // We want to send ampr mail direct to host. Queue to BBS AMPR + + if (FindAMPR()) + { + // We have bbs AMPR + + return TRUE; + } + } + } + return FALSE; +} + +struct UserInfo * FindAMPR() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "AMPR") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindRMS() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "RMS") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindBBS(char * Name) +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, Name) == 0) + return bbs; + } + + return NULL; +} + +int CountConnectionsOnPort(int CheckPort) +{ + int n, Count = 0; + CIRCUIT * conn; + int port, sesstype, paclen, maxframe, l4window; + char callsign[11]; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active) + { + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + if (port == CheckPort) + Count++; + } + } + + return Count; +} + + +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type) +{ + char ** Calls; + + if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) + return TRUE; + + if (RejFrom && From) + { + Calls = RejFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (RejTo && To) + { + Calls = RejTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (RejAt && ATBBS) + { + Calls = RejAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (RejBID && BID) + { + Calls = RejBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0])) + return TRUE; + } + + Calls++; + } + } + return FALSE; // Ok to accept +} + +BOOL CheckValidCall(char * From) +{ + unsigned int i; + + if (DontCheckFromCall) + return TRUE; + + if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || + strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) + return TRUE; + + for (i = 1; i < strlen(From); i++) // skip first which may also be digit + { + if (isdigit(From[i])) + { + // Has a digit. Check Last is not digit + + if (isalpha(From[strlen(From) - 1])) + return TRUE; + } + } + + // No digit, return false + + return FALSE; +} + +BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) +{ + char ** Calls; + + if (HoldFrom && From) + { + Calls = HoldFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldTo && To) + { + Calls = HoldTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldAt && ATBBS) + { + Calls = HoldAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldBID && BID) + { + Calls = HoldBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0])) + return TRUE; + } + + Calls++; + } + } + return FALSE; // Ok to accept +} + +BOOL CheckifLocalRMSUser(char * FullTo) +{ + struct UserInfo * user = LookupCall(FullTo); + + if (user) + if (user->flags & F_POLLRMS) + return TRUE; + + return FALSE; + +} + + + +int check_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); + else + return 0; +} + + +void set_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); +} + + +void clear_fwd_bit (char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); +} + +VOID BBSputs(CIRCUIT * conn, char * buf) +{ + // Sends to user and logs + + WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); + + QueueMsg(conn, buf, (int)strlen(buf)); +} + +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + return; +} + +// nodeprintfEx add a LF if NEEFLF is set + +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + if (conn->BBSFlags & NEEDLF) + QueueMsg(conn, "\r", 1); + + return; +} + + +int compare( const void *arg1, const void *arg2 ); + +VOID SortBBSChain() +{ + struct UserInfo * user; + struct UserInfo * users[161]; + int i = 0, n; + + // Get array of addresses + + for (user = BBSChain; user; user = user->BBSNext) + { + users[i++] = user; + if (i > 160) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + BBSChain = NULL; + + // Rechain (backwards, as entries ate put on front of chain) + + for (n = i-1; n >= 0; n--) + { + users[n]->BBSNext = BBSChain; + BBSChain = users[n]; + } +} + +int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int CountMessagesTo(struct UserInfo * user, int * Unread) +{ + int i, Msgs = 0; + UCHAR * Call = user->Call; + + *Unread = 0; + + for (i = NumberofMessages; i > 0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) + { + Msgs++; + if (MsgHddrPtr[i]->status == 'N') + *Unread = *Unread + 1; + } + } + return(Msgs); +} + + + +// Custimised message handling routines. +/* + Variables - a subset of those used by FBB + + $C : Number of the next message. + $I : First name of the connected user. + $L : Number of the latest message. + $N : Number of active messages + $U : Callsign of the connected user. + $W : Inserts a carriage return. + $Z : Last message read by the user (L command). + %X : Number of messages for the user. + %x : Number of new messages for the user. +*/ + +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + size_t len; + char Dollar[] = "$"; + char CR[] = "\r"; + char num[20]; + int Msgs = 0, Unread = 0; + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = ptr - OldP; // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + case 'L': // Number of the latest message. + + sprintf(num, "%d", LatestMsg); + pptr = num; + break; + + case 'N': // Number of active messages. + + sprintf(num, "%d", NumberofMessages); + pptr = num; + break; + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + case 'Z': // Last message read by the user (L command). + + sprintf(num, "%d", conn->UserPointer->lastmsg); + pptr = num; + break; + + case 'X': // Number of messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + case 'x': // Number of new messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Unread); + pptr = num; + break; + + case 'F': // Number of new messages to forward to this BBS. + + Msgs = CountMessagestoForward(conn->UserPointer); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + WriteLogLine(conn, '>', NewMessage, (int)len, LOG); + QueueMsg(conn, NewMessage, (int)len); +} + +BOOL isdigits(char * string) +{ + // Returns TRUE id sting is decimal digits + + size_t i, n = strlen(string); + + for (i = 0; i < n; i++) + { + if (isdigit(string[i]) == FALSE) return FALSE; + } + return TRUE; +} + +BOOL wildcardcompare(char * Target, char * Match) +{ + // Do a compare with string *string string* *string* + + // Strings should all be UC + + char Pattern[100]; + char * firststar; + + strcpy(Pattern, Match); + firststar = strchr(Pattern,'*'); + + if (firststar) + { + size_t Len = strlen(Pattern); + + if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end + { + Pattern[Len - 1] = 0; + return !(strstr(Target, &Pattern[1]) == NULL); + } + if (Pattern[0] == '*') // * at start + { + // Compare the last len - 1 chars of Target + + size_t Targlen = strlen(Target); + size_t Comparelen = Targlen - (Len - 1); + + if (Len == 1) // Just * + return TRUE; + + if (Comparelen < 0) // Too Short + return FALSE; + + return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); + } + + // Must be * at end - compare first Len-1 char + + return (memcmp(Target, Pattern, Len - 1) == 0); + } + + // No WildCards - straight strcmp + return (strcmp(Target, Pattern) == 0); +} + +#ifndef LINBPQ + +PrintMessage(HDC hDC, struct MsgInfo * Msg); + +PrintMessages(HWND hDlg, int Count, int * Indexes) +{ + int i, CurrentMsgIndex; + char MsgnoText[10]; + int Msgno; + struct MsgInfo * Msg; + int Len = MAX_PATH; + BOOL hResult; + PRINTDLG pdx = {0}; + HDC hDC; + +// CHOOSEFONT cf; + LOGFONT lf; + HFONT hFont; + + + // Initialize the PRINTDLG structure. + + pdx.lStructSize = sizeof(PRINTDLG); + pdx.hwndOwner = hWnd; + pdx.hDevMode = NULL; + pdx.hDevNames = NULL; + pdx.hDC = NULL; + pdx.Flags = PD_RETURNDC | PD_COLLATE; + pdx.nMinPage = 1; + pdx.nMaxPage = 1000; + pdx.nCopies = 1; + pdx.hInstance = 0; + pdx.lpPrintTemplateName = NULL; + + // Invoke the Print property sheet. + + hResult = PrintDlg(&pdx); + + memset(&lf, 0, sizeof(LOGFONT)); + + /* + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = pdx.hDC; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = PRINTER_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. +*/ + + lf.lfHeight = -56; + lf.lfWeight = 600; + lf.lfOutPrecision = 3; + lf.lfClipPrecision = 2; + lf.lfQuality = 1; + lf.lfPitchAndFamily = '1'; + strcpy (lf.lfFaceName, "Courier New"); + + hFont = CreateFontIndirect(&lf); + + if (hResult) + { + // User clicked the Print button, so use the DC and other information returned in the + // PRINTDLG structure to print the document. + + DOCINFO pdi; + + pdi.cbSize = sizeof(DOCINFO); + pdi.lpszDocName = "BBS Message Print"; + pdi.lpszOutput = NULL; + pdi.lpszDatatype = "RAW"; + pdi.fwType = 0; + + hDC = pdx.hDC; + + SelectObject(hDC, hFont); + + StartDoc(hDC, &pdi); + StartPage(hDC); + + for (i = 0; i < Count; i++) + { + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); + + Msgno = atoi(MsgnoText); + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + PrintMessage(hDC, Msg); + break; + } + } + } + + EndDoc(hDC); + } + + if (pdx.hDevMode != NULL) + GlobalFree(pdx.hDevMode); + if (pdx.hDevNames != NULL) + GlobalFree(pdx.hDevNames); + + if (pdx.hDC != NULL) + DeleteDC(pdx.hDC); + + return 0; +} + +PrintMessage(HDC hDC, struct MsgInfo * Msg) +{ + int Len = MAX_PATH; + char * MsgBytes; + char * Save; + int Msglen; + + StartPage(hDC); + + Save = MsgBytes = ReadMessageFile(Msg->number); + + Msglen = Msg->length; + + if (MsgBytes) + { + char Hddr[1000]; + char FullTo[100]; + int HRes, VRes; + char * ptr1, * ptr2; + int LineLen; + + RECT Rect; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MsgBytes, "Body:"); + if (ptr) + { + Msglen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + MsgBytes = ptr + 4; + } + + HRes = GetDeviceCaps(hDC, HORZRES) - 50; + VRes = GetDeviceCaps(hDC, VERTRES) - 50; + + Rect.top = 50; + Rect.left = 50; + Rect.right = HRes; + Rect.bottom = VRes; + + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); + + // process message a line at a time. When page is full, output a page break + + ptr1 = MsgBytes; + ptr2 = ptr1; + + while (Msglen-- > 0) + { + if (*ptr1++ == '\r') + { + // Output this line + + // First check if it will fit + + Rect.top = Rect.bottom; + Rect.right = HRes; + Rect.bottom = VRes; + + LineLen = ptr1 - ptr2 - 1; + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + + if (Rect.bottom >= VRes) + { + EndPage(hDC); + StartPage(hDC); + + Rect.top = 50; + Rect.bottom = VRes; + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + } + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); + + if (*(ptr1) == '\n') + { + ptr1++; + Msglen--; + } + + ptr2 = ptr1; + } + } + + free(Save); + + EndPage(hDC); + + } + return 0; +} + +#endif + + +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) +{ + char FileName[MAX_PATH] = "Messages.in"; + int Files = 0; + int WriteLen=0; + FILE *in; + CIRCUIT dummyconn; + struct UserInfo User; + int Index = 0; + + char Buffer[100000]; + char *buf = Buffer; + + if (FN[0]) // Name supplled + strcpy(FileName, FN); + + else + { +#ifndef LINBPQ + OPENFILENAME Ofn; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = MainWnd; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = BaseDir; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + Ofn.lpstrTitle = NULL;//; + + if (!GetOpenFileName(&Ofn)) + return 0; +#endif + } + + in = fopen(FileName, "rb"); + + if (!(in)) + { + char msg[500]; + sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); + if (conn) + nodeprintf(conn, "%s\r", msg); +#ifdef WIN32 + else + if (Nopopup == FALSE) + MessageBox(NULL, msg, "BPQMailChat", MB_OK); +#endif + return 0; + } + + memset(&dummyconn, 0, sizeof(CIRCUIT)); + memset(&User, 0, sizeof(struct UserInfo)); + + if (conn == 0) + { + conn = &dummyconn; + + dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. + strcpy(User.Call, "IMPORT"); + User.flags |= F_EMAIL; + dummyconn.sysop = TRUE; + dummyconn.BBSFlags = BBS; + + strcpy(dummyconn.Callsign, "IMPORT"); + } + + while(fgets(Buffer, 99999, in)) + { + // First line should start SP/SB ?ST? + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * Msg; + char To[100]= ""; + int msglen; + char * Context; + char * Arg1, * Cmd; + +NextMessage: + + From = NULL; + BID = NULL; + ATBBS = NULL; + To[0]= 0; + + Sleep(100); + + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (Buffer[0] == 0) //Blank Line + continue; + + WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); + + if (dummyconn.sysop == 0) + { + nodeprintf(conn, "%s\r", Buffer); + Flush(conn); + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + fclose(in); + return Files; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + if (dummyconn.sysop) + Debugprintf("Bad Import Line %s", Buffer); + else + nodeprintf(conn, "Bad Import Line %s\r", Buffer); + + fclose(in); + return Files; + } + + strcpy(To, Arg1); + + if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + { + if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) + { + Msg = conn->TempMsg; + + // SP is Ok, read message; + + ClearQueue(conn); + + fgets(Buffer, 99999, in); + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + if (strlen(Buffer) > 60) + Buffer[60] = 0; + + strcpy(Msg->title, Buffer); + + // Read the lines + + conn->Flags |= GETTINGMESSAGE; + + Buffer[0] = 0; + + fgets(Buffer, 99999, in); + + while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + msglen = (int)strlen(Buffer); + Buffer[msglen++] = 13; + ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + + // Message completed (or off end of file) + + Files ++; + + ClearQueue(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + else + { + fclose(in); + return Files; + } + } + else + { + // Create failed + + Flush(conn); + } + } + + // Search for next message + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + while (Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (_stricmp(Buffer, "/EX") == 0) + { + // Found end + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + } + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + } + + fclose(in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + return Files; +} +char * ReadMessageFileEx(struct MsgInfo * MsgRec) +{ + // Sets Message Size from File Size + + int msgno = MsgRec->number; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + MsgRec->length = FileSize; + + return MsgBytes; +} + +char * ReadMessageFile(int msgno) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + + return MsgBytes; +} + + +int QueueMsg(ConnectionInfo * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); + + if (conn->OutputQueue == NULL) + { + // relloc failed - should never happen, but clean up + + CriticalErrorHandler("realloc failed to expand output queue"); + FreeSemaphore(&OutputSEM); + return 0; + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + FreeSemaphore(&OutputSEM); + + return len; +} + +void TrytoSend() +{ + // call Flush on any connected streams with queued data + + ConnectionInfo * conn; + struct ConsoleInfo * Cons; + + int n; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + Flush(conn); + + // if an FLARQ mail has been sent see if queues have cleared + + if (conn->BBSFlags & YAPPTX) + { + YAPPSendData(conn); + } + else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) + { + int n = TXCount(conn->BPQStream); // All Sent and Acked? + + if (n == 0) + { + struct MsgInfo * Msg = conn->FwdMsg; + + conn->ARQClearCount--; + + if (conn->ARQClearCount <= 0) + { + Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); + + // Mark mail as sent, and look for more + + clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); + set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + Msg->status = 'F'; // Mark as forwarded + Msg->datechanged=time(NULL); + } + + conn->BBSFlags &= ~ARQMAILACK; + conn->UserPointer->ForwardingInfo->MsgCount--; + + SaveMessageDatabase(); + SendARQMail(conn); // See if any more - close if not + } + } + else + conn->ARQClearCount = 10; + } + } + } +#ifndef LINBPQ + for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) + { + if (Cons->Console) + Flush(Cons->Console); + } +#endif +} + + +void Flush(CIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + conn->ErrorCount = 0; + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent=0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 15) + return; // Busy + + if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting + return; + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + return; + + if (tosend <= conn->paclen) + len=tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + if (conn->Paging) + { + // look for CR chars in message to send. Increment LinesSent, and stop if at limit + + UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; + UCHAR * ptr2; + int lenleft = len; + + ptr2 = memchr(ptr1, 0x0d, len); + + while (ptr2) + { + conn->LinesSent++; + ptr2++; + lenleft = len - (int)(ptr2 - ptr1); + + if (conn->LinesSent >= conn->PageLen) + { + len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + conn->OutputGetPointer+=len; + tosend-=len; + SendUnbuffered(conn->BPQStream, "bort, Continue..>", 25); + FreeSemaphore(&OutputSEM); + return; + + } + ptr2 = memchr(ptr2, 0x0d, lenleft); + } + } + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer+=len; + + FreeSemaphore(&OutputSEM); + + tosend-=len; + sent++; + + if (sent > 15) + return; + } + + // All Sent. Free buffers and reset pointers + + conn->LinesSent = 0; + + ClearQueue(conn); +} + +VOID ClearQueue(ConnectionInfo * conn) +{ + if (conn->OutputQueue == NULL) + return; + + GetSemaphore(&OutputSEM, 0); + + free(conn->OutputQueue); + + conn->OutputQueue=NULL; + conn->OutputGetPointer=0; + conn->OutputQueueLength=0; + + FreeSemaphore(&OutputSEM); +} + + + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) +{ + struct UserInfo * user; + + Msg->status='K'; + Msg->datechanged=time(NULL); + + // Remove any forwarding references + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount--; + clear_fwd_bit(Msg->fbbs, user->BBSNumber); + } + } + } + if (SaveDB) + SaveMessageDatabase(); + RebuildNNTPList(); +} + +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + struct MsgInfo * Msg; + + while (Arg1) + { + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + goto Next; + } + + if (Msg->type != 'T') + { + nodeprintf(conn, "Message %d not an NTS Message\r", msgno); + goto Next; + } + + if (Msg->status == 'N') + nodeprintf(conn, "Warning - Message has status N\r"); + + Msg->status = 'D'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + + nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); + Next: + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + // Param is either ALL or a list of numbers + + if (Arg1 == NULL) + { + nodeprintf(conn, "No message number\r"); + return; + } + + if (_stricmp(Arg1, "ALL") == 0) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", Msg->number); + } + } + return; + } + + while (Arg1) + { + msgno = atoi(Arg1); + Msg = GetMsgFromNumber(msgno); + + if (Msg) + { + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", msgno); + } + else + { + nodeprintf(conn, "Message #%d was not held\r", msgno); + } + } + else + nodeprintf(conn, "Message #%d not found\r", msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + switch (toupper(Cmd[1])) + { + + case 0: // Just K + + while (Arg1) + { + msgno = atoi(Arg1); + KillMessage(conn, user, msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + SaveMessageDatabase(); + return; + + case 'M': // Kill Mine + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + { + if (Msg->type == 'P' && Msg->status == 'Y') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + + SaveMessageDatabase(); + return; + + case 'H': // Kill Held + + if (conn->sysop) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + SaveMessageDatabase(); + return; + + case '>': // K> - Kill to + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesTo(conn, user, Arg1) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + } + + case '<': + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesFrom(conn, user, Arg1) == 0); + BBSputs(conn, "No Messages found\r"); + + return; + } + } + + nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); + + return; + +} + +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) +{ + if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) + return TRUE; + + if (Msg->type == 'P') + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + + if (Msg->type == 'B') + if (_stricmp(Msg->from, Call) == 0) + return TRUE; + + return FALSE; +} + +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL || Msg->status == 'K') + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (OkToKillMessage(conn->sysop, user->Call, Msg)) + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", msgno); + } + else + nodeprintf(conn, "Not your message\r"); +} + + +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) +{ + char FullFrom[80]; + char FullTo[80]; + + strcpy(FullFrom, Msg->from); + + if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || + Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) + strcat(FullFrom, Msg->emailfrom); + + if (_stricmp(Msg->to, "RMS") == 0) + { + sprintf(FullTo, "RMS:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + else + + if (Msg->to[0] == 0 && Msg->via[0] != 0) + { + sprintf(FullTo, "smtp:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + + else + if (Msg->via[0] != 0) + { + char Via[80]; + strcpy(Via, Msg->via); + strlop(Via, '.'); // Only show first part of via + nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); + } + else + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); + + // if paging, stop two before page lengh. This lets us send the continue prompt, save status + // and exit without triggering the system paging code. We can then read a message then resume listing + + if (Temp->ListActive && conn->Paging) + { + Temp->LinesSent++; + + if ((Temp->LinesSent + 1) >= conn->PageLen) + { + nodeprintf(conn, "bort, , = Continue..>"); + Temp->LastListedInPagedMode = Msg->number; + Temp->ListSuspended = TRUE; + return TRUE; + } + } + + return FALSE; +} + +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) +{ + struct TempUserInfo * Temp = user->Temp; + struct MsgInfo * Msg; + + // Allow compound selection, eg LTN or LFP + + // types P N T + // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New + // Status N Y H F K D + + // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. + + Temp->ListActive = TRUE; + Temp->LinesSent = 0; + + if (Resuming) + { + // Entered after a paging pause. Selection fields are already set up + + // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode + +// Start = Temp->LastListedInPagedMode; + Temp->ListSuspended = FALSE; + } + else + { + Temp->ListRangeEnd = LatestMsg; + Temp->ListRangeStart = 1; + Temp->LLCount = 0; + Temp->SendFullFrom = 0; + Temp->ListType = 0; + Temp->ListStatus = 0; + Temp->ListSelector = 0; + Temp->UpdateLatest = 0; + Temp->LastListParams[0] = 0; + Temp->IncludeKilled = 1; // SYSOP include Killed except LM + + //Analyse L params. + + _strupr(Cmd); + + if (strcmp(Cmd, "LC") == 0) // List Bull Categories + { + ListCategories(conn); + return; + } + + // if command is just L or LR start from last listed + + if (Arg1 == NULL) + { + if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) + { + if (LatestMsg == conn->lastmsg) + { + BBSputs(conn, "No New Messages\r"); + return; + } + + Temp->UpdateLatest = 1; + Temp->ListRangeStart = conn->lastmsg; + } + } + + if (strchr(Cmd, 'V')) // Verbose + Temp->SendFullFrom = 'V'; + + if (strchr(Cmd, 'R')) + Temp->ListDirn = 'R'; + else + Temp->ListDirn = '*'; // Default newest first + + Cmd++; // skip L + + if (strchr(Cmd, 'T')) + Temp->ListType = 'T'; + else if (strchr(Cmd, 'P')) + Temp->ListType = 'P'; + else if (strchr(Cmd, 'B')) + Temp->ListType = 'B'; + + if (strchr(Cmd, 'N')) + Temp->ListStatus = 'N'; + else if (strchr(Cmd, 'Y')) + Temp->ListStatus = 'Y'; + else if (strchr(Cmd, 'F')) + Temp->ListStatus = 'F'; + else if (strchr(Cmd, '$')) + Temp->ListStatus = '$'; + else if (strchr(Cmd, 'H')) + Temp->ListStatus = 'H'; + else if (strchr(Cmd, 'K')) + Temp->ListStatus = 'K'; + else if (strchr(Cmd, 'D')) + Temp->ListStatus = 'D'; + + // H or K only by Sysop + + switch (Temp->ListStatus) + { + case 'K': + case 'H': // List Status + + if (conn->sysop) + break; + + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + return; + } + + if (strchr(Cmd, '<')) + Temp->ListSelector = '<'; + else if (strchr(Cmd, '>')) + Temp->ListSelector = '>'; + else if (strchr(Cmd, '@')) + Temp->ListSelector = '@'; + else if (strchr(Cmd, 'M')) + { + Temp->ListSelector = 'M'; + Temp->IncludeKilled = FALSE; + } + + // Param could be single number, number range or call + + if (Arg1) + { + if (strchr(Cmd, 'L')) // List Last + { + // Param is number + + if (Arg1) + Temp->LLCount = atoi(Arg1); + } + else + { + // Range nnn-nnn or single value or callsign + + char * Arg2, * Arg3, * Range; + char seps[] = " \t\r"; + UINT From=LatestMsg, To=0; + + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Temp->ListSelector && Temp->ListSelector != 'M') + { + // < > or @ - first param is callsign + + strcpy(Temp->LastListParams, Arg1); + + // Just possible number range + + Arg1 = Arg2; + Arg2 = Arg3; + Arg3 = strtok_s(NULL, seps, &Context); + } + + if (Arg1) + { + Range = strchr(Arg1, '-'); + + // A number could be a Numeric Bull Dest (eg 44) + // I think this can only resaonably be > + + if (isdigits(Arg1)) + To = From = atoi(Arg1); + + if (Arg2) + From = atoi(Arg2); + else + { + if (Range) + { + Arg3 = strlop(Arg1, '-'); + + To = atoi(Arg1); + + if (Arg3 && Arg3[0]) + From = atoi(Arg3); + else + From = LatestMsg; + } + } + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + Temp->ListRangeStart = To; + Temp->ListRangeEnd = From; + } + } + } + } + + // Run through all messages (either forwards or backwards) and list any that match all selection criteria + + while (1) + { + if (Temp->ListDirn == 'R') + Msg = GetMsgFromNumber(Temp->ListRangeStart); + else + Msg = GetMsgFromNumber(Temp->ListRangeEnd); + + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message + { + // Check filters + + if (Temp->ListStatus && Temp->ListStatus != Msg->status) + goto skip; + + if (Temp->ListType && Temp->ListType != Msg->type) + goto skip; + + if (Temp->ListSelector == '<') + if (_stricmp(Msg->from, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '>') + if (_stricmp(Msg->to, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '@') + if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && + (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) + goto skip; + + if (Temp->ListSelector == 'M') + if (_stricmp(Msg->to, user->Call) != 0 && + (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) + + goto skip; + + if (ListMessage(Msg, conn, Temp)) + { + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + return; // Hit page limit + } + + if (Temp->LLCount) + { + Temp->LLCount--; + if (Temp->LLCount == 0) + return; // LL count reached + } +skip:; + } + + if (Temp->ListRangeStart == Temp->ListRangeEnd) + { + // if using L or LR (list new) update last listed field + + if (Temp->UpdateLatest) + conn->lastmsg = LatestMsg; + + return; + } + + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! + return; + + } + +/* + + switch (Cmd[0]) + { + + case '*': // Just L + case 'R': // LR = List Reverse + + if (Arg1) + { + // Range nnn-nnn or single value + + char * Arg2, * Arg3; + char * Context; + char seps[] = " -\t\r"; + UINT From=LatestMsg, To=0; + char * Range = strchr(Arg1, '-'); + + Arg2 = strtok_s(Arg1, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Arg2) + To = From = atoi(Arg2); + + if (Arg3) + From = atoi(Arg3); + else + if (Range) + From = LatestMsg; + + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + if (Cmd[1] == 'R') + { + if (Start) + To = Start + 1; + + ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + else + { + if (Start) + From = Start - 1; + + ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + } + else + + if (LatestMsg == conn->lastmsg) + BBSputs(conn, "No New Messages\r"); + else if (Cmd[1] == 'R') + ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + else + ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + + conn->lastmsg = LatestMsg; + + return; + + + case 'L': // List Last + + if (Arg1) + { + int i = atoi(Arg1); + int m = NumberofMessages; + + if (Resuming) + i = Temp->LLCount; + else + Temp->LLCount = i; + + for (; i>0 && m != 0; i--) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + i++; + continue; + } + + Temp->LLCount--; + + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + } + return; + + case 'M': // LM - List Mine + + if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + return; + + case '>': // L> - List to + + if (Arg1) + if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + + return; + + case '<': + + if (Arg1) + if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case '@': + + if (Arg1) + if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case 'N': + case 'Y': + case 'F': + case '$': + case 'D': // Delivered NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (Temp->ListType) + { + if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + else + { + if (MsgHddrPtr[m]->status == toupper(Cmd[1])) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + m--; + } + } + } + return; + + case 'K': + case 'H': // List Status + + if (conn->sysop) + { + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (MsgHddrPtr[i]->status == toupper(Cmd[1])) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) + return; // Hit page limit + + } + } + + if (Msgs == 0) + BBSputs(conn, "No Messages found\r"); + } + else + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + + return; + + case 'C': + { + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; + Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; + } + } + + // Could be P B or T if specified without a status + + switch (Temp->ListType) + { + case 'P': + case 'B': + case 'T': // NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + + return; + } + } + +*/ + nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); + +} + +void ListCategories(ConnectionInfo * conn) +{ + // list bull categories + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; +Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; +} + +/* +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || + ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && + _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} + +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + return Msgs; // Hit page limit + + } + } + + return(Msgs); +} + +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || + (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} +*/ +int GetUserMsg(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'K') + { + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m--; + + } while (m> 0); + + return 0; +} + + +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) +{ + // Return TRUE if user is allowed to read message + + if (Msg->status == 'K' && IncludeKilled == 0) + return FALSE; + + if (SYSOP) + return TRUE; // Sysop can list or read anything + + if ((Msg->status != 'K') && (Msg->status != 'H')) + { + if (Msg->type == 'B' || Msg->type == 'T') return TRUE; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + } + } + + return FALSE; +} +/* +int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->status != 'K') + { + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m++; + + } while (m <= NumberofMessages); + + return 0; + +} + + +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m >= End; m--) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + + } +} + + +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m <= End; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + } +} +*/ + +void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + + switch (toupper(Cmd[1])) + { + case 0: // Just R + + while (Arg1) + { + msgno = atoi(Arg1); + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + ReadMessage(conn, user, msgno); + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; + + case 'M': // Read Mine (Unread Messages) + + if (toupper(Cmd[2]) == 'R') + { + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + else + { + for (i = NumberofMessages; i > 0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + + return; + } + + nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); + + return; +} + +int RemoveLF(char * Message, int len) +{ + // Remove lf chars and nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + +int RemoveNulls(char * Message, int len) +{ + // Remove nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + char * MsgBytes, * Save; + char FullTo[100]; + int Index = 0; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) + { + nodeprintf(conn, "Message %d not for you\r", msgno); + return; + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + MsgBytes = Save = ReadMessageFile(msgno); + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + if (MsgBytes) + { + int Length = Msg->length; + + if (Msg->B2Flags & B2Msg) + { + char * ptr; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + char * FileName[100]; + int FileLen[100]; + int Files = 0; + int BodyLen, NewLen; + int i; + char *ptr2; + char Msg[512]; + int Len; + + ptr = MsgBytes; + + Len = sprintf(Msg, "Message has Attachments\r\r"); + QueueMsg(conn, Msg, Len); + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + FileName[Files++] = &ptr1[1]; + *(ptr2 - 1) = 0; + } + + ptr = ptr2; + ptr++; + } + + ptr += 2; // Over Blank Line and Separator + + NewLen = RemoveLF(ptr, BodyLen); + + QueueMsg(conn, ptr, NewLen); // Display Body + + ptr += BodyLen + 2; // to first file + + for (i = 0; i < Files; i++) + { + char Msg[512]; + int Len, n; + char * p = ptr; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c == 10) + *p = 13; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); + QueueMsg(conn, Msg, Len); + } + else + { + Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); + QueueMsg(conn, Msg, Len); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += NewLen; + + QueueMsg(conn, ptr, NewLen); + } + + ptr += FileLen[i]; + ptr +=2; // Over separator + } + goto sendEOM; + } + + // Remove B2 Headers (up to the File: Line) + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr) + { + MsgBytes = ptr; + Length = (int)strlen(ptr); + } + } + + // Remove lf chars + + Length = RemoveLF(MsgBytes, Length); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += Length; + + QueueMsg(conn, MsgBytes, Length); + +sendEOM: + + free(Save); + + nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); + + if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + } + } + } + } + else + { + nodeprintf(conn, "File for Message %d not found\r", msgno); + } +} + struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + m = GetUserMsg(m, Call, sysop); + + if (m == 0) + return NULL; + + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + m--; + + } while (m> 0); + + return NULL; + +} + + +char * ReadInfoFile(char * File) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1 = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + +#ifndef WIN32 + + // Replace LF with CR + + // Remove lf chars + + ptr1 = MsgBytes; + + while (*ptr1) + { + if (*ptr1 == '\n') + *(ptr1) = '\r'; + + ptr1++; + } +#endif + + return MsgBytes; +} + +char * FormatDateAndTime(time_t Datim, BOOL DateOnly) +{ + struct tm *tm; + static char Date[]="xx-xxx hh:mmZ"; + + tm = gmtime(&Datim); + + if (tm) + sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", + tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); + + if (DateOnly) + { + Date[6]=0; + return Date; + } + + return Date; +} + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); + + +BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * OldMsg; + char OldTitle[62]; + char NewTitle[62]; + char To[100]= ""; + int msgno; + + if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP + + switch (toupper(Cmd[1])) + { + case 'B': + + if (RefuseBulls) + { + nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); + return FALSE; + } + + if (user->flags & F_NOBULLS) + { + nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); + return FALSE; + } + + + case 'P': + case 'T': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); + + case 'R': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + Arg1=&OldMsg->from[0]; + + strcpy(To, Arg1); + + if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) + { + // SMTP message. Need to get the real sender from the message + + sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); + } + + if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) + return FALSE; + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 57) OldTitle[57] = 0; + + strcpy(NewTitle, "Re:"); + strcat(NewTitle, OldTitle); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + + return TRUE; + + case 'C': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 56) OldTitle[56] = 0; + + strcpy(NewTitle, "Fwd:"); + strcat(NewTitle, OldTitle); + + conn->CopyBuffer = ReadMessageFile(msgno); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + } + + + nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); + + return FALSE; +} + +char * CheckToAddress(CIRCUIT * conn, char * Addr) +{ + // Check one element of Multiple Address + + if (conn == NULL || !(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(Addr, "SYSOP") == 0) + { + return _strdup(Addr); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(Addr, BBSName) == 0) + return _strdup(SYSOPCall); + + + if (strchr(Addr, '@') == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + char * NewAddr = malloc(250); + if (conn) + nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); + sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); + return NewAddr; + } + } + else + { + WPRecP WP = LookupWP(Addr); + + if (WP) + { + char * NewAddr = malloc(250); + + if (conn) + nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); + sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); + return NewAddr; + } + } + } + } + + // Check SMTP and RMS Addresses + + if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) + { + Addr[3] = ':'; // Replace RMS/ with RMS: + + if (conn && !FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) + { + Addr[4] = ':'; // Replace smpt/ with smtp: + + if (ISP_Gateway_Enabled) + { + if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + } + else + { + if (conn) + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + + return _strdup(Addr); +} + + +char Winlink[] = "WINLINK.ORG"; + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) +{ + char * ptr; + char seps[] = " \t\r"; + WPRecP WP; + char * ToCopy = _strdup(To); + int Len; + + conn->ToCount = 0; + + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + // Having trailing ; will mess up parsing multiple addresses, so remove. + + while (To[strlen(To) - 1] == ';') + To[strlen(To) - 1] = 0; + + if (strchr(Context, ';') || strchr(To, ';')) + { + // Multiple Addresses - put address list back together + + char * p; + + To[strlen(To)] = ' '; + Context = To; + + while (p = strchr(Context, ';')) + { + // Multiple Addressees + + To = strtok_s(NULL, ";", &Context); + Len = (int)strlen(To); + conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + + To = strtok_s(NULL, seps, &Context); + + Len = (int)strlen(To); + conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + else + { + // Single Call + + // accept CALL!CALL for source routed message + + if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ + { + char * bang = strchr(To, '!'); + + memmove(bang + 1, bang, strlen(bang)); // Move !call down one + + *ATBBS = strlop(To, '!');; + } + + // Accept call@call (without spaces) - but check for smtp addresses + + if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) + { + ptr = strchr(To, '@'); + + if (ptr) + { + // If looks like a valid email address, treat as such + + int tolen; + *ATBBS = strlop(To, '@'); + + strlop(To, '-'); // Cant have SSID on BBS Name + + tolen = (int)strlen(To); + + if (tolen > 6 || !CheckifPacket(*ATBBS)) + { + // Probably Email address. Add smtp: or rms: + + if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route + sprintf(To, "rms:%s", ToCopy); + else if (ISP_Gateway_Enabled) + sprintf(To, "smtp:%s", ToCopy); + else if (isAMPRMsg(ToCopy)) + sprintf(To, "rms:%s", ToCopy); + + } + } + } + } + + free(ToCopy); + + // Look for Optional fields; + + ptr = strtok_s(NULL, seps, &Context); + + while (ptr) + { + if (strcmp(ptr, "@") == 0) + { + *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); + } + else if(strcmp(ptr, "<") == 0) + { + *From = strtok_s(NULL, seps, &Context); + } + else if (ptr[0] == '$') + *BID = &ptr[1]; + else + { + nodeprintf(conn, "*** Error: Invalid Format\r"); + return FALSE; + } + ptr = strtok_s(NULL, seps, &Context); + } + + // Only allow < from a BBS + + if (*From) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "*** < can only be used by a BBS\r"); + return FALSE; + } + } + + if (!*From) + *From = conn->UserPointer->Call; + + if (!(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(To, "SYSOP") == 0) + { + conn->LocalMsg = TRUE; + return TRUE; + } + + if (!*ATBBS && conn->ToCount == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(To); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + *ATBBS = Winlink; + nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); + } + else if (ToUser->HomeBBS[0]) + { + *ATBBS = ToUser->HomeBBS; + nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); + } + else + { + conn->LocalMsg = TRUE; + } + } + else + { + conn->LocalMsg = FALSE; + WP = LookupWP(To); + + if (WP) + { + *ATBBS = WP->first_homebbs; + nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); + } + } + } + } + return TRUE; +} + +BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) +{ + struct MsgInfo * Msg, * TestMsg; + char * via = NULL; + char * FromHA; + + // Create a temp msg header entry + + if (conn->ToCount) + { + } + else + { + if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType)) + { + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - REJECTED\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); + + return FALSE; + } + } + + Msg = malloc(sizeof (struct MsgInfo)); + + if (Msg == 0) + { + CriticalErrorHandler("malloc failed for new message header"); + return FALSE; + } + + memset(Msg, 0, sizeof (struct MsgInfo)); + + conn->TempMsg = Msg; + + Msg->type = MsgType; + + if (conn->UserPointer->flags & F_HOLDMAIL) + Msg->status = 'H'; + else + Msg->status = 'N'; + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (BID) + { + BIDRec * TempBID; + + // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message + // If we do, reject it. If not, accept it again. (do we need some loop protection ???) + + TempBID = LookupBID(BID); + + if (TempBID) + { + if (MsgType == 'B') + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + + TestMsg = GetMsgFromNumber(TempBID->u.msgno); + + // if the same TO we will assume the same message + + if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) + { + // We have this message. If we have already forwarded it, we should accept it again + + if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + } + } + + if (strlen(BID) > 12) BID[12] = 0; + strcpy(Msg->bid, BID); + + // Save BID in temp list in case we are offered it again before completion + + TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, BID); + TempBID->u.conn = conn; + + } + + if (conn->ToCount) + { + } + else + { + if (_memicmp(ToCall, "rms:", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, ':'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "rms/", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, '/'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + if ((conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + via=strlop(ToCall, ':'); + ToCall[0] = 0; + } + else + { + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + else + { + _strupr(ToCall); + if (ATBBS) + via=_strupr(ATBBS); + } + + strlop(ToCall, '-'); // Remove any (illegal) ssid + if (strlen(ToCall) > 6) ToCall[6] = 0; + + strcpy(Msg->to, ToCall); + + if (SendBBStoSYSOPCall) + if (_stricmp(ToCall, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (via) + { + if (strlen(via) > 40) via[40] = 0; + + strcpy(Msg->via, via); + } + + } // End of Multiple Dests + + // Look for HA in From (even if we shouldn't be getting it!) + + FromHA = strlop(From, '@'); + + + strlop(From, '-'); // Remove any (illegal) ssid + if (strlen(From) > 6) From[6] = 0; + strcpy(Msg->from, From); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + if (Title) // Only used by SR and SC + { + strcpy(Msg->title, Title); + conn->Flags |= GETTINGMESSAGE; + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + return TRUE; + } + + if (conn->BBSFlags & FLARQMODE) + return TRUE; + + if (!(conn->BBSFlags & FBBCompressed)) + conn->Flags |= GETTINGTITLE; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "Enter Title (only):\r"); + else + if (!(conn->BBSFlags & FBBForwarding)) + nodeprintf(conn, "OK\r"); + + return TRUE; +} + +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + + conn->Flags &= ~GETTINGTITLE; + + if (msglen == 1) + { + nodeprintf(conn, "*** Message Cancelled\r"); + SendPrompt(conn, user); + return; + } + + if (msglen > 60) msglen = 60; + + Buffer[msglen-1] = 0; + + strcpy(conn->TempMsg->title, Buffer); + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to create Message Buffer\r"); + return; + } + + conn->Flags |= GETTINGMESSAGE; + + if (!conn->BBSFlags & BBS) + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + +} + +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + char * ptr2 = NULL; + + if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) + { + int Index = 0; + + if (conn->TempMsg->type == 'P') + Index = PMSG; + else if (conn->TempMsg->type == 'B') + Index = BMSG; + else if (conn->TempMsg->type == 'T') + Index = TMSG; + + conn->Flags &= ~GETTINGMESSAGE; + + user->Total.MsgsReceived[Index]++; + user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; + + if (conn->ToCount) + { + // Multiple recipients + + struct MsgInfo * Msg = conn->TempMsg; + int i; + struct MsgInfo * SaveMsg = Msg; + char * SaveBody = conn->MailBuffer; + int SaveMsgLen = Msg->length; + BOOL SentToRMS = FALSE; + int ToLen = 0; + char * ToString = zalloc(conn->ToCount * 100); + + // If no BID provided, allocate one + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); + + for (i = 0; i < conn->ToCount; i++) + { + char * Addr = conn->To[i]; + char * Via; + + if (_memicmp (Addr, "SMTP:", 5) == 0) + { + // For Email + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + Msg->to[0] = 0; + strcpy(Msg->via, &Addr[5]); + + CreateMessageFromBuffer(conn); + continue; + } + + if (_memicmp (Addr, "RMS:", 4) == 0) + { + // Add to B2 Message for RMS + + Addr+=4; + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + goto PktMsg; + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + continue; + } + } + + ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, Addr, Via); + continue; + } + + _strupr(Addr); + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + + // Add to B2 Message for RMS + + continue; + } + } + + PktMsg: + + conn->LocalMsg = FALSE; + + // Normal BBS Message + + if (_stricmp(Addr, "SYSOP") == 0) + conn->LocalMsg = TRUE; + else + { + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + conn->LocalMsg = TRUE; + } + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + strcpy(Msg->to, Addr); + + if (Via) + { + Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe + strcpy(Msg->via, Via); + } + + CreateMessageFromBuffer(conn); + } + + if (ToLen) + { + char * B2Hddr = zalloc(ToLen + 1000); + int B2HddrLen; + char DateString[80]; + struct tm * tm; + time_t Date = time(NULL); + char Type[16] = "Private"; + + // Get Type + + if (conn->TempMsg->type == 'B') + strcpy(Type, "Bulletin"); + else if (conn->TempMsg->type == 'T') + strcpy(Type, "Traffic"); + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); + + Msg->B2Flags = B2Msg; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", + SaveMsg->bid, DateString, Type, + SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); + + memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); + memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); + + Msg->length += B2HddrLen; + + strcpy(Msg->to, "RMS"); + + CreateMessageFromBuffer(conn); + + free(B2Hddr); + } + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (!(conn->BBSFlags & BBS)) + SendPrompt(conn, conn->UserPointer); + else + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->BBSFlags & OUTWARDCONNECT) + BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward + else + BBSputs(conn, ">\r"); + } + + /* + // From a client - Create one copy with all RMS recipients, and another for each packet recipient + + // Merge all RMS To: lines + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (LocalMsg[i]) + continue; // For a local RMS user + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + ToLen += strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, "RMS"); + strcpy(Msg->via, "winlink.org"); + + // Must Change the BID + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + SetupNextFBBMessage(conn); + return; + + } My__except_Routine("Process Multiple Destinations"); + + BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; +*/ + + conn->ToCount = 0; + + return; + } + + + CreateMessageFromBuffer(conn); + return; + + } + + Buffer[msglen++] = 0x0a; + + if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); + + conn->TempMsg->length += msglen; +} + +VOID CreateMessageFromBuffer(CIRCUIT * conn) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * ptr1, * ptr2 = NULL; + char * ptr3, * ptr4; + int FWDCount = 0; + char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; + time_t Age; + int OurCount; + char * HoldReason = "User has Hold Messages flag set"; + struct UserInfo * user; + + +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; +#endif + + // If doing SC, Append Old Message + + if (conn->CopyBuffer) + { + if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) + { + conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); + + conn->TempMsg->length += (int)strlen(OldMess); + + memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); + + conn->TempMsg->length += (int)strlen(conn->CopyBuffer); + + free(conn->CopyBuffer); + conn->CopyBuffer = NULL; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); + + free(conn->TempMsg); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + // Create BID if non supplied + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + // if message body had R: lines, get date created from last (not very accurate, but best we can do) + + // Also check if we have had message before to detect loops + + ptr1 = conn->MailBuffer; + OurCount = 0; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr1 = strstr(ptr1, "\r\n\r\n"); + if (ptr1) + ptr1 += 4; + else + ptr1 = conn->MailBuffer; + } + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + // Is if ours? + + // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 + + ptr3 = strchr(ptr1, '@'); + ptr4 = strchr(ptr1, '.'); + + if (ptr3 && ptr4 && (ptr4 > ptr3)) + { + if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) + OurCount++; + } + + GetWPBBSInfo(ptr1); // Create WP /I record from R: Line + + // see if another + + ptr2 = ptr1; // save + ptr1 = strchr(ptr1, '\r'); + if (ptr1 == 0) + { + Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); + return; + } + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + // ptr2 points to last R: line (if any) + + if (ptr2) + { + struct tm rtime; + time_t result; + + memset(&rtime, 0, sizeof(struct tm)); + + if (ptr2[10] == '/') + { + // Dodgy 4 char year + + sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (ptr2[8] == '/') + { + sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + // result = _mkgmtime(&rtime); + + if ((result = mktime(&rtime)) != (time_t)-1 ) + { + result -= (time_t)_MYTIMEZONE; + + Msg->datecreated = result; + Age = (time(NULL) - result)/86400; + + if ( Age < -7) + { + Msg->status = 'H'; + HoldReason = "Suspect Date Sent"; + } + else if (Age > BidLifetime || Age > MaxAge) + { + Msg->status = 'H'; + HoldReason = "Message too old"; + + } + else + GetWPInfoFromRLine(Msg->from, ptr2, result); + } + else + { + // Can't decode R: Datestamp + + Msg->status = 'H'; + HoldReason = "Corrupt R: Line - can't determine age"; + } + + if (OurCount > 1) + { + // Message is looping + + Msg->status = 'H'; + HoldReason = "Message may be looping"; + + } + } + + if (strcmp(Msg->to, "WP") == 0) + { + // If Reject WP Bulls is set, Kill message here. + // It should only get here if B2 - otherwise it should be + // rejected earlier + + if (Msg->type == 'B' && FilterWPBulls) + Msg->status = 'K'; + + } + + conn->MailBuffer[Msg->length] = 0; + + if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) + { + Msg->status = 'H'; + HoldReason = "Bad word in title or body"; + } + + if (CheckHoldFilters(Msg->from, Msg->to, Msg->via, Msg->bid)) + { + Msg->status = 'H'; + HoldReason = "Matched Hold Filters"; + } + + if (CheckValidCall(Msg->from) == 0) + { + Msg->status = 'H'; + HoldReason = "Probable Invalid From Call"; + } + + // Process any WP Messages + + if (strcmp(Msg->to, "WP") == 0) + { + if (Msg->status == 'N') + { + ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); + + if (Msg->type == 'P') // Kill any processed private WP messages. + { + char VIA[80]; + + strcpy(VIA, Msg->via); + strlop(VIA, '.'); + + if (strcmp(VIA, BBSName) == 0) + Msg->status = 'K'; + } + } + } + + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (Msg->length > MaxTXSize) + { + Msg->status = 'H'; + HoldReason = "Message too long"; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); + } + + // Check for message to internal server + + if (Msg->via[0] == 0 + || _stricmp(Msg->via, BBSName) == 0 // our BBS a + || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address + { + if (CheckforMessagetoServer(Msg)) + { + // Flag as killed and send prompt + + FlagAsKilled(Msg, TRUE); + + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); + SendPrompt(conn, conn->UserPointer); + } + return; // no need to process further + } + } + + if (Msg->to[0]) + FWDCount = MatchMessagetoBBSList(Msg, conn); + else + { + // If addressed @winlink.org, and to a local user, Keep here. + + char * Call; + char * AT; + + // smtp or rms - don't warn no route + + FWDCount = 1; + + Call = _strupr(_strdup(Msg->via)); + AT = strlop(Call, '@'); + + if (AT && _stricmp(AT, "WINLINK.ORG") == 0) + { + struct UserInfo * user = LookupCall(Call); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); + strcpy(Msg->to, Call); + strcpy(Msg->via, AT); + if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + } + } + } + free(Call); + } + + // Warn SYSOP if P or T forwarded in, and has nowhere to go + + if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && + strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) + { + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + SendWarningToSYSOP(Msg); + } + else + { + // No via - is it for a local user? + + if (LookupCall(Msg->to) == 0) + SendWarningToSYSOP(Msg); + } + } + + if ((conn->BBSFlags & SYNCMODE) == 0) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); + + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + + if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg + nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); + } + else + { + if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') + // Not Local and no forward route + nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); + } + if (conn->ToCount == 0) + SendPrompt(conn, conn->UserPointer); + } + else + { + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->ToCount == 0) + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + } + } + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + if (Msg->status == 'H') + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + if (EnableUI) +#ifdef LINBPQ + SendMsgUI(Msg); +#else + __try + { + SendMsgUI(Msg); + } + My__except_Routine("SendMsgUI"); +#endif + + user = LookupCall(Msg->to); + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + return; +} + +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) +{ + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + char Mess[255]; + int len; + BOOL AutoImport = FALSE; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is + // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines + + if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) + { + UCHAR * ptr = conn->MailBuffer; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr = strstr(ptr, "\r\n\r\n"); + if (ptr) + ptr += 4; + else + ptr = conn->MailBuffer; + } + + if (memcmp(ptr, "R:", 2) == 0) + { + ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines + if (ptr) + ptr += 4; + } + + if (*(ptr) == 'S' && ptr[2] == ' ') + { + int HeaderLen = (int)(ptr - conn->MailBuffer); + Msg->length -= HeaderLen; + memmove(conn->MailBuffer, ptr, Msg->length); + Msg->status = 'K'; + AutoImport = TRUE; + } + } + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + if (AutoImport) + ImportMessages(NULL, MsgFile, TRUE); + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (WriteLen != Msg->length) + { + len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + QueueMsg(conn, Mess, len); + Debugprintf(Mess); + } + return; +} + + + + +VOID SendUnbuffered(int stream, char * msg, int len) +{ +#ifndef LINBPQ + if (stream < 0) + WritetoConsoleWindow(stream, msg, len); + else +#endif + SendMsg(stream, msg, len); +} + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); + +BOOL FindMessagestoForward (CIRCUIT * conn) +{ + struct UserInfo * user = conn->UserPointer; + +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + + // This is a hack so Winpack or WLE users can use forwarding + // protocols to get their messages without being defined as a BBS + + // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! + // !! Anything sent using this must be killed if sent or rejected. + + // I'm not sure about this. I think I only need the PacLinkCalls + // if from RMS Express, and it always sends an FW; line + // Ah, no. What about WinPack ?? + // If from RMS Express must have Temp_B2 or BBS set or SID will + // be rejected. So maybe just Temp_B2 && BBS = 0?? + // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? + + if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) + { + if (conn->PacLinkCalls == NULL) + { + // create a list with just the user call + + char * ptr1; + + conn->PacLinkCalls = zalloc(30); + + ptr1 = (char *)conn->PacLinkCalls; + ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) + strcpy(ptr1, user->Call); + + conn->PacLinkCalls[0] = ptr1; + } + } + + if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) + { + conn->LastForwardType = 'T'; + return TRUE; + } + + if (conn->LastForwardType == 'T') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) + { + conn->LastForwardType = 'P'; + return TRUE; + } + + if (conn->LastForwardType == 'P') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) + { + conn->LastForwardType = 'B'; + return TRUE; + } + + conn->LastForwardType = 0; + return FALSE; +#ifndef LINBPQ + } My__except_Routine("FindMessagestoForward"); +#endif + return FALSE; + +} + + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) +{ + // See if any messages are queued for this BBS + + int m; + struct MsgInfo * Msg; + struct UserInfo * user = conn->UserPointer; + struct FBBHeaderLine * FBBHeader; + BOOL Found = FALSE; + char RLine[100]; + int TotalSize = 0; + time_t NOW = time(NULL); + +// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", +// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); + + if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset + conn->NextMessagetoForward = 1; + + conn->FBBIndex = 0; + + for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If an NTS MPS, see if anything matches + + if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + int depth; + + if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + } + } + + // If forwarding to Paclink or RMS Express, look for any message matching the + // requested call list with status 'N' (maybe should also be 'P' ??) + + if (conn->PacLinkCalls) + { + int index = 1; + + char * Call = conn->PacLinkCalls[0]; + + while (Call) + { + if (Msg->type == Type && Msg->status == 'N') + { +// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); + if (_stricmp(Msg->to, Call) == 0) + if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) + goto Forwardit; + else + Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); + } + Call = conn->PacLinkCalls[index++]; + } +// continue; + } + + if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') + && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + // Message to be sent - do a consistancy check (State, etc) + + Forwardit: + + if (Msg->Defered) // = response received + { + Msg->Defered--; + Debugprintf("Message %d deferred", Msg->number); + continue; + } + + if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + Msg->status = 'H'; + continue; + } + + conn->NextMessagetoForward = m + 1; // So we don't offer again if defered + + Msg->Locked = 1; // So other MPS can't pick it up + + // if FBB forwarding add to list, eise save pointer + + if (conn->BBSFlags & FBBForwarding) + { + struct tm *tm; + time_t temp; + + FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; + + FBBHeader->FwdMsg = Msg; + FBBHeader->MsgType = Msg->type; + FBBHeader->Size = Msg->length; + TotalSize += Msg->length; + strcpy(FBBHeader->From, Msg->from); + strcpy(FBBHeader->To, Msg->to); + strcpy(FBBHeader->ATBBS, Msg->via); + strcpy(FBBHeader->BID, Msg->bid); + + // Set up R:Line, so se can add its length to the sise + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + // If using B2 forwarding we need the message size and Compressed size for FC proposal + + if (conn->BBSFlags & FBBB2Mode) + { + if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) + { + char * MailBuffer = malloc(100); + char Title[100]; + int Length; + + // Corrupt B2 Message + + Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); + Msg->status = 'H'; + SaveMessageDatabase(); + + conn->FBBIndex--; + TotalSize -= Msg->length; + memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + continue; + } + } + + if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) + return TRUE; // Got max number or too big + + Found = TRUE; // Remember we have some + } + else + { + conn->FwdMsg = Msg; + return TRUE; + } + } + } + + return Found; +} + +BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) +{ + // See if any messages are queued for this BBS + + // if Conn is not NULL, also check Msg Type + + int m; + struct MsgInfo * Msg; + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + if (conn) + { + char Type = Msg->type; + + if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + else + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + } + + return FALSE; +} + +int CountMessagestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n++; + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n++; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + } + } + } + + return n; +} + +int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + } + } + } + + return n; +} + +VOID SendWarningToSYSOP(struct MsgInfo * Msg) +{ + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go", Msg->number); + sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); + SendMessageToSYSOP(Title, MailBuffer, Length); +} + + + +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, "SYSTEM"); + if (SendSYStoSYSOPCall) + strcpy(Msg->to, SYSOPCall); + else + strcpy(Msg->to, "SYSOP"); + + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +VOID CheckBBSNumber(int i) +{ + // Make sure number is unique + + int Count = 0; + struct UserInfo * user; + + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + { + Count++; + + if (Count > 1) + { + // Second with same number - Renumber this one + + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + + Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); + + } + } + } +} + + +int FindFreeBBSNumber() +{ + // returns the lowest number not used by any bbs or message. + + struct MsgInfo * Msg; + struct UserInfo * user; + int i, m; + + for (i = 1; i<= NBBBS; i++) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + goto nexti; // In use + } + + // Not used by BBS - check messages + + for (m = 1; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if (check_fwd_bit(Msg->fbbs, i)) + goto nexti; // In use + + if (check_fwd_bit(Msg->forw, i)) + goto nexti; // In use + } + + // Not in Use + + return i; + +nexti:; + + } + + return 0; // All used +} + +BOOL SetupNewBBS(struct UserInfo * user) +{ + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + return FALSE; + + user->BBSNext = BBSChain; + BBSChain = user; + + SortBBSChain(); + + ReinitializeFWDStruct(user); + + return TRUE; +} + +VOID DeleteBBS(struct UserInfo * user) +{ + struct UserInfo * BBSRec, * PrevBBS = NULL; + +#ifndef LINBPQ + RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); +#endif + for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) + { + if (user == BBSRec) + { + if (PrevBBS == NULL) // First in chain; + { + BBSChain = BBSRec->BBSNext; + break; + } + PrevBBS->BBSNext = BBSRec->BBSNext; + break; + } + } +} + + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); + +VOID SetupForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + + char Key[100] = "BBSForwarding."; + char Temp[100]; + + HKEY hKey=0; + char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; + + int m; + struct MsgInfo * Msg; + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + if (UsingingRegConfig == 0) + { + // Config from file + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + strcat(Key, "*"); + + strcat(Key, user->Call); + + group = config_lookup (&cfg, Key); + + if (group == NULL) // No info + return; + else + { + ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); + ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); + ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); + ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); + ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); + ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); + + ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); + ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); + ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); + ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); + ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); + ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); + ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + if (ForwardingInfo->AllowCompressed) + ForwardingInfo->AllowBlocked = TRUE; + + ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); + ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); + ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); + ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); + ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); + ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + if (ForwardingInfo->ConTimeout == 0) + ForwardingInfo->ConTimeout = 120; + + GetStringValue(group, "BBSHA", Temp); + + if (Temp[0]) + ForwardingInfo->BBSHA = _strdup(Temp); + else + ForwardingInfo->BBSHA = _strdup(""); + } + } + else + { +#ifndef LINBPQ + + int retCode,Type,Vallen; + + strcat(RegKey, user->Call); + retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode != ERROR_SUCCESS) + return; + else + { + ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); + ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); + ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); + ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); + ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); + ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Enabled", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "RequestReverse", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"FWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"RevFWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); + + RegQueryValueEx(hKey,"MaxFBBBlock",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + Vallen=0; + retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); + + if (retCode != 0) + { + // No Key - Get from WP?? + + WPRec * ptr = LookupWP(user->Call); + + if (ptr) + { + if (ptr->first_homebbs) + { + ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); + } + } + } + + if (Vallen) + { + ForwardingInfo->BBSHA = malloc(Vallen); + RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); + } + + RegCloseKey(hKey); + } +#endif + } + + // Convert FWD Times and H Addresses + + if (ForwardingInfo->FWDTimes) + SetupFwdTimes(ForwardingInfo); + + if (ForwardingInfo->Haddresses) + SetupHAddreses(ForwardingInfo); + + if (ForwardingInfo->HaddressesP) + SetupHAddresesP(ForwardingInfo); + + if (ForwardingInfo->BBSHA) + { + if (ForwardingInfo->BBSHA[0]) + SetupHAElements(ForwardingInfo); + else + { + free(ForwardingInfo->BBSHA); + ForwardingInfo->BBSHA = NULL; + } + } + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If any forward bits are set, increment count on BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount++; + } + } + } +} + +VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) +{ + char * ptr1; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + char ** Value; + config_setting_t *setting; + char * Save; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + setting = config_setting_get_member (group, ValueName); + + if (setting) + { + ptr = config_setting_get_string (setting); + + Save = _strdup(ptr); // DOnt want to change config string + ptr = Save; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) // ignore null elements + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + free(Save); + } + + Value[Count] = NULL; + return Value; +} + + +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) +{ +#ifdef LINBPQ + return NULL; +#else + int retCode,Type,Vallen; + char * MultiString = NULL; + int ptr, len; + int Count = 0; + char ** Value; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + Value[0] = NULL; + + Vallen=0; + + + retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring + { + free(Value); + return FALSE; + } + + MultiString = malloc(Vallen); + + retCode = RegQueryValueEx(hKey, ValueName, 0, + (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); + + ptr=0; + + while (MultiString[ptr]) + { + len=strlen(&MultiString[ptr]); + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(&MultiString[ptr]); + ptr+= (len + 1); + } + + Value[Count] = NULL; + + free(MultiString); + + return Value; +#endif +} + +VOID FreeForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + int i; + + + ForwardingInfo = user->ForwardingInfo; + + FreeList(ForwardingInfo->TOCalls); + FreeList(ForwardingInfo->ATCalls); + FreeList(ForwardingInfo->Haddresses); + FreeList(ForwardingInfo->HaddressesP); + + i=0; + if(ForwardingInfo->HADDRS) + { + while(ForwardingInfo->HADDRS[i]) + { + FreeList(ForwardingInfo->HADDRS[i]); + i++; + } + free(ForwardingInfo->HADDRS); + free(ForwardingInfo->HADDROffet); + } + + i=0; + if(ForwardingInfo->HADDRSP) + { + while(ForwardingInfo->HADDRSP[i]) + { + FreeList(ForwardingInfo->HADDRSP[i]); + i++; + } + free(ForwardingInfo->HADDRSP); + } + + FreeList(ForwardingInfo->ConnectScript); + FreeList(ForwardingInfo->FWDTimes); + if (ForwardingInfo->FWDBands) + { + i=0; + while(ForwardingInfo->FWDBands[i]) + { + free(ForwardingInfo->FWDBands[i]); + i++; + } + free(ForwardingInfo->FWDBands); + } + if (ForwardingInfo->BBSHAElements) + { + i=0; + while(ForwardingInfo->BBSHAElements[i]) + { + free(ForwardingInfo->BBSHAElements[i]); + i++; + } + free(ForwardingInfo->BBSHAElements); + } + free(ForwardingInfo->BBSHA); + +} + +VOID FreeList(char ** Hddr) +{ + VOID ** Save; + + if (Hddr) + { + Save = (void **)Hddr; + while(Hddr[0]) + { + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + + +VOID ReinitializeFWDStruct(struct UserInfo * user) +{ + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + SetupForwardingStruct(user); + +} + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Times = ForwardingInfo->FWDTimes; + int Start, End; + int Count = 0; + + ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); + + if (Times) + { + while(Times[0]) + { + ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); + ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); + + Start = atoi(Times[0]); + End = atoi(&Times[0][5]); + + ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + Count++; + Times++; + } + ForwardingInfo->FWDBands[Count] = NULL; + } +} +void StartForwarding(int BBSNumber, char ** TempScript) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + + if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) + if (ForwardingInfo) + if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + // See if TempScript requested + + if (user->ForwardingInfo->TempConnectScript) + FreeList(user->ForwardingInfo->TempConnectScript); + + user->ForwardingInfo->TempConnectScript = TempScript; + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + + return; +} + +size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) +{ + if (_File) + return fwrite(_Str, _Size, _Count, _File); + + // Appending to MailBuffer + + memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); + conn->InputLen += (int)_Count; + + return _Count; +} + + +BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) +{ + BOOL AddCRLF = FALSE; + BOOL AutoImport = FALSE; + FILE * Handle = NULL; + char * Context; + BOOL Email = FALSE; + time_t now = time(NULL); + char * param; + + FN = strtok_s(FN, " ,", &Context); + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + } + } + // If FN is an email address, write to a temp file, and send via rms or emali gateway + + if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) + { + Email = TRUE; + AddCRLF =TRUE; + conn->MailBuffer=malloc(100000); + conn->MailBufferSize=100000; + conn->InputLen = 0; + } + else + { + Handle = fopen(FN, "ab"); + + if (Handle == NULL) + { + int err = GetLastError(); + Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); + return FALSE; + } + } + + while (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + struct tm * tm; + time_t temp; + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + char * MsgPtr; + char Line[256]; + int len; + struct UserInfo * user = conn->UserPointer; + int Index = 0; + + Msg = conn->FwdMsg; + + if (Email) + if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) + break; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwritex(conn, Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwritex(conn, Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + fwritex(conn, Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwritex(conn, "\r\n", 1, 2, Handle); + + fwritex(conn, MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwritex(conn, "/EX\r\n", 1, 5, Handle); + else + fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); + + if (AddCRLF) + fwritex(conn, "\r\n", 1, 2, Handle); + + free(MsgBytes); + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + Msg->datechanged = now; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + if (Email) + { + struct MsgInfo * Msg; + BIDRec * BIDRec; + + if (conn->InputLen == 0) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + return TRUE; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datecreated = Msg->datechanged = Msg->datereceived = now; + + strcpy(Msg->from, BBSName); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + if (AutoImport) + sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); + else + sprintf(Msg->title, "Batched messages from BBS %s", BBSName); + + Msg->length = conn->InputLen; + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (_memicmp(FN, "SMTP:", 5) == 0) + { + strcpy(Msg->via, &FN[5]); + SMTPMsgCreated=TRUE; + } + else + { + strcpy(Msg->to, "RMS"); + if (_memicmp(FN, "RMS:", 4) == 0) + strcpy(Msg->via, &FN[4]); + else + strcpy(Msg->via, FN); + } + + MatchMessagetoBBSList(Msg, conn); + + SaveMessageDatabase(); + SaveBIDDatabase(); + } + else + fclose(Handle); + + SaveMessageDatabase(); + return TRUE; +} + +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) +{ + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(Msg->number); + char * MsgPtr; + char Line[256]; + int len; + int MsgLen = Msg->length; + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwrite(Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwrite(Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + MsgLen = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + + // If a B2 Message, remove B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + fwrite(Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwrite("\r\n", 1, 2, Handle); + + fwrite(MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwrite("/EX\r\n", 1, 5, Handle); + else + fwrite("\r\n/EX\r\n", 1, 7, Handle); + + free(MsgBytes); + + return TRUE; + +} + +BOOL ConnecttoBBS (struct UserInfo * user) +{ + int n, p; + CIRCUIT * conn; + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + + for (n = NumberofStreams-1; n >= 0 ; n--) + { + conn = &Connections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->BPQStream = p; + + // Can't set Active until Connected or Stuck Session detertor can clear session. + // But must set Active before Connected() runs or will appear is Incoming Connect. + // Connected() is semaphored, so get semaphore before ConnectUsingAppl + // Probably better to semaphore lost session code instead + + + strcpy(conn->Callsign, user->Call); + conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); + conn->UserPointer = user; + + Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); + + ForwardingInfo->MoreLines = TRUE; + + GetSemaphore(&ConSemaphore, 1); + conn->Active = TRUE; + ConnectUsingAppl(conn->BPQStream, BBSApplMask); + FreeSemaphore(&ConSemaphore); + +#ifdef LINBPQ + { + BPQVECSTRUC * SESS; + SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; + + if (SESS->HOSTSESSION == NULL) + { + Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); + return FALSE; + } + + SESS->HOSTSESSION->Secure_Session = 1; + } +#endif + + strcpy(conn->Callsign, user->Call); + + // Connected Event will trigger connect to remote system + + RefreshMainWindow(); + + return TRUE; + } + } + + Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); + + return FALSE; + +} + +struct DelayParam +{ + struct UserInfo * User; + CIRCUIT * conn; + int Delay; +}; + +struct DelayParam DParam; // Not 100% safe, but near enough + +VOID ConnectDelayThread(struct DelayParam * DParam) +{ + struct UserInfo * User = DParam->User; + int Delay = DParam->Delay; + + User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects + + Sleep(Delay); + + User->ForwardingInfo->Forwarding = TRUE; + ConnecttoBBS(User); + + return; +} + +VOID ConnectPauseThread(struct DelayParam * DParam) +{ + CIRCUIT * conn = DParam->conn; + int Delay = DParam->Delay; + char Msg[] = "Pause Ok\r "; + + Sleep(Delay); + + ProcessBBSConnectScript(conn, Msg, 9); + + return; +} + + +/* +BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); + + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + BOOL Ret; + GetSemaphore(&ScriptSEM); + Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); + FreeSemaphore(&ScriptSEM); + + return Ret; +} +*/ + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + char callsign[10]; + int port, sesstype, paclen, maxframe, l4window; + char * ptr, * ptr2; + + WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); + + Buffer[len]=0; + _strupr(Buffer); + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + if (ForwardingInfo->ScriptIndex == -1) + { + // First Entry - if first line is TIMES, check and skip forward if necessary + + int n = 0; + int Start, End; + time_t now = time(NULL), StartSecs, EndSecs; + char * Line; + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; + Line = Scripts[n]; + + if (_memicmp(Line, "TIMES", 5) == 0) + { + NextBand: + Start = atoi(&Line[6]); + End = atoi(&Line[11]); + + StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + if ((StartSecs <= now) && (EndSecs >= now)) + goto InBand; // In band + + // Look for next TIME + NextLine: + Line = Scripts[++n]; + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) != 0) + goto NextLine; + else + goto NextBand; +InBand: + ForwardingInfo->ScriptIndex = n; + } + + } + else + { + // Dont check first time through + + if (strcmp(Buffer, "*** CONNECTED ") != 0) + { + if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + { + ForwardingInfo->MoreLines = FALSE; + } + if (!ForwardingInfo->MoreLines) + goto CheckForSID; + } + } + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + int Delay = 1000; + + // Look for an alternative connect block (Starting with ELSE) + + ElseLoop: + + // Skip any comments + + while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // TIMES terminates a script + + if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "ELSE", 4) != 0) + { + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + goto ElseLoop; + } + + if (_memicmp(&Cmd[5], "DELAY", 5) == 0) + Delay = atoi(&Cmd[10]) * 1000; + else + Delay = 1000; + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + + DParam.Delay = Delay; + DParam.User = conn->UserPointer; + + _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); + + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + // NETROM to KA node returns + + //c 1 milsw + //WIRAC:N9PMO-2} Connected to MILSW + //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A + //You have reached N9ZXS's KA-Node MILSW + //ENTER COMMAND: B,C,J,N, or Help ? + + //C KB9PRF-7 + //###LINK MADE + //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A + + // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not + // an indication of a connect. + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + char * Cmd; + + if (conn->SkipConn) + { + conn->SkipConn = FALSE; + return TRUE; + } + + LoopBack: + + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished + { + if (_memicmp(Cmd, "MSGTYPE", 7) == 0) + { + char * ptr; + + // Select Types to send. Only send types in param. Only reverse if R in param + + _strupr(Cmd); + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + + strcpy(conn->MSGTYPES, &Cmd[8]); + + if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; + + ptr = strchr(&Cmd[8], 'B'); + + if (ptr) + { + conn->SendB = TRUE; + conn->MaxBLen = atoi(++ptr); + if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; + } + + ptr = strchr(&Cmd[8], 'T'); + + if (ptr) + { + conn->SendT = TRUE; + conn->MaxTLen = atoi(++ptr); + if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; + } + ptr = strchr(&Cmd[8], 'P'); + + if (ptr) + { + conn->SendP = TRUE; + conn->MaxPLen = atoi(++ptr); + if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; + } + + // If nothing to do, terminate script + + if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) + goto LoopBack; + + Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) + { + // Used to limit connects on a port to 1 + + int Port; + char Option[80]; + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); + + if (CountConnectionsOnPort(Port)) + { + Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + goto LoopBack; + } + + if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) + { + // Generate a Password to enable RADIO commands on a remote node + char AuthCommand[80]; + + _strupr(Cmd); + strcpy(AuthCommand, Cmd); + + CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); + + nodeprintf(conn, "%s\r", AuthCommand); + return TRUE; + } + + if (_memicmp(Cmd, "SKIPCON", 7) == 0) + { + // Remote Node sends Connected in CTEXT - we need to swallow it + + conn->SkipConn = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) + { + // Send ;FW: command + + conn->SendWL2KFW = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) + { + // Remote Node sends > at end of CTEXT - we need to swallow it + + conn->SkipPrompt++; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) + { + conn->BBSFlags |= TEXTFORWARDING; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "RADIOONLY", 9) == 0) + { + conn->BBSFlags |= WINLINKRO; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "NEEDLF", 6) == 0) + { + conn->BBSFlags |= NEEDLF; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "MCASTRX", 6) == 0) + { + conn->BBSFlags |= MCASTRX; + conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs + + // send MCAST to Node + + nodeprintfEx(conn, "MCAST\r"); + return TRUE; + } + + if (_memicmp(Cmd, "FLARQ", 5) == 0) + { + conn->BBSFlags |= FLARQMAIL; + + CheckForEnd: + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + ForwardingInfo->MoreLines = FALSE; + + goto LoopBack; + } + if (_memicmp(Cmd, "PAUSE", 5) == 0) + { + // Pause script + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + DParam.Delay = atoi(&Cmd[6]) * 1000; + DParam.conn = conn; + + _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); + + return TRUE; + } + + if (_memicmp(Cmd, "FILE", 4) == 0) + { + if (Cmd[4] == 0) + { + // Missing Filename + + Logprintf(LOG_BBS, conn, '!', "Export file name missing"); + } + else + ForwardMessagestoFile(conn, &Cmd[5]); + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "SMTP", 4) == 0) + { + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + + SendAMPRSMTP(conn); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + + if (_memicmp(Cmd, "IMPORT", 6) == 0) + { + char * File, * Context; + int Num; + char * Temp = _strdup(Cmd); + + File = strtok_s(&Temp[6], " ", &Context); + + if (File && File[0]) + { + Num = ImportMessages(NULL, File, TRUE); + + Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); + + if (Context && _stricmp(Context, "delete") == 0) + DeleteFile(File); + } + free(Temp); + + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + goto LoopBack; + } + + // Anything else is sent to Node + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nodeprintfEx(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nodeprintfEx(conn, "%s\r", Cmd); + + return TRUE; + } + + // End of script. + + ForwardingInfo->MoreLines = FALSE; + + if (conn->BBSFlags & MCASTRX) + { + // No session with Multicast, so no SID + + conn->BBSFlags &= ~RunningConnectScript; + return TRUE; + } + + if (conn->BBSFlags & FLARQMAIL) + { + // FLARQ doesnt send a prompt - Just send message(es) + + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + // Update Paclen + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + SendARQMail(conn); + return TRUE; + } + + + return TRUE; + } + + ptr = strchr(Buffer, '}'); + + if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext + { + // Could be respsonse to Node Command + + ptr+=2; + + ptr2 = strchr(&ptr[0], ' '); + + if (ptr2) + { + if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command + { + if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) + { + // stray match or misconfigured + + return TRUE; + } + + ForwardingInfo->ScriptIndex++; + + if (Scripts[ForwardingInfo->ScriptIndex]) + if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) + nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); + + return TRUE; + } + } + } + + // Not Success or Fail. If last line is still outstanding, wait fot Response + // else look for SID or Prompt + + if (conn->SkipPrompt && Buffer[len-2] == '>') + { + conn->SkipPrompt--; + return TRUE; + } + + if (ForwardingInfo->MoreLines) + return TRUE; + + // No more steps, Look for SID or Prompt + +CheckForSID: + + if (strstr(Buffer, "POSYNCHELLO")) // URONODE + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + ProcessLine(conn, 0, Buffer, len); + return FALSE; + } + + if (strstr(Buffer, "SORRY, NO")) // URONODE + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (memcmp(Buffer, ";PQ: ", 5) == 0) + { + // Secure CMS challenge + + int Len; + struct UserInfo * User = conn->UserPointer; + char * Pass = User->CMSPass; + int Response ; + char RespString[12]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + + strlop(ConnectingCall, ' '); + + if (Pass[0] == 0) + { + Pass = User->pass; // Old Way + if (Pass[0] == 0) + { + strlop(ConnectingCall, '-'); + User = LookupCall(ConnectingCall); + if (User) + Pass = User->CMSPass; + } + } + + // + + Response = GetCMSHash(&Buffer[5], Pass); + + sprintf(RespString, "%010d", Response); + + Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); + + // Save challengs in case needed for FW lines + + strcpy(conn->PQChallenge, &Buffer[5]); + + return FALSE; + } + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // Update PACLEN + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + conn->FBBChecksum = 0; + } + + return TRUE; + } + + if (memcmp(Buffer, "[PAKET ", 7) == 0) + { + conn->BBSFlags |= BBS; + conn->BBSFlags |= MBLFORWARDING; + } + + if (Buffer[len-2] == '>') + { + if (conn->SkipPrompt) + { + conn->SkipPrompt--; + return TRUE; + } + + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) + { + // PK232. Don't send a SID, and switch to Text Mode + + conn->BBSFlags |= (BBS | TEXTFORWARDING); + conn->Flags |= SENDTITLE; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + return TRUE; + } + + if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) + { + // Build a ;FW: line with all calls with PollRMS Set + + // According to Lee if you use secure login the first + // must be the BBS call + // Actually I don't think we need the first, + // as that is implied + + // If a secure password is available send the new + // call|response format. + + // I think this should use the session callsign, which + // normally will be the BBS ApplCall, and not the BBS Name, + // but coudl be changed by *** LINKED + + int i, s; + char FWLine[10000] = ";FW: "; + struct UserInfo * user; + char RMSCall[20]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + strlop(ConnectingCall, ' '); + + strcat (FWLine, ConnectingCall); + + for (i = 0; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; + + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(RMSCall, "%s-%d", user->Call, s); + else + sprintf(RMSCall, "%s", user->Call); + + // We added connectingcall at front + + if (strcmp(RMSCall, ConnectingCall) != 0) + { + strcat(FWLine, " "); + strcat(FWLine, RMSCall); + + if (user->CMSPass[0]) + { + int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); + char RespString[12]; + + sprintf(RespString, "%010d", Response); + strcat(FWLine, "|"); + strcat(FWLine, &RespString[2]); + } + } + } + } + } + } + + strcat(FWLine, "\r"); + + nodeprintf(conn, FWLine); + } + + // Only declare B1 and B2 if other end did, and we are configued for it + + nodeprintfEx(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + (conn->BBSFlags & FBBCompressed) ? "B" : "", + (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", + (conn->BBSFlags & FBBB2Mode) ? "2" : "", + (conn->BBSFlags & FBBForwarding) ? "F" : "", + (conn->BBSFlags & WINLINKRO) ? "" : "J"); + + if (conn->SecureMsg[0]) + { + struct UserInfo * user; + BBSputs(conn, conn->SecureMsg); + conn->SecureMsg[0] = 0; + + // Also send a Location Comment Line + + //; GM8BPQ-10 DE G8BPQ (IO92KX) + //; WL2K DE GM8BPQ () (PAT) + + user = LookupCall(BBSName); + + if (LOC && LOC[0]) + nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); + } + + if (conn->BPQBBS && conn->MSGTYPES[0]) + + // Send a ; MSGTYPES to control what he sends us + + nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); + + if (conn->BBSFlags & FBBForwarding) + { + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + if (conn->DoReverse) + FBBputs(conn, "FF\r"); + else + { + FBBputs(conn, "FQ\r"); + conn->CloseAfterFlush = 20; // 2 Secs + } + } + + return TRUE; + } + + return TRUE; + } + + return TRUE; +} + +VOID Parse_SID(CIRCUIT * conn, char * SID, int len) +{ + ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions + + // scan backwards for first '-' + + if (strstr(SID, "BPQCHATSERVER")) + { + Disconnect(conn->BPQStream); + return; + } + + if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) + { + conn->RMSExpress = TRUE; + conn->Paclink = FALSE; + conn->PAT = FALSE; + + // Set new RMS Users as RMS User + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + + if (stristr(SID, "PAT")) + { + // Set new PAT Users as RMS User + + conn->RMSExpress = FALSE; + conn->Paclink = FALSE; + conn->PAT = TRUE; + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + if (strstr(SID, "Paclink")) + { + conn->RMSExpress = FALSE; + conn->Paclink = TRUE; + } + + if (strstr(SID, "WL2K-")) + { + conn->WL2K = TRUE; + conn->BBSFlags |= WINLINKRO; + } + + if (strstr(SID, "MFJ-")) + { + conn->BBSFlags |= MFJMODE; + } + + if (_memicmp(SID, "OpenBCM", 7) == 0) + { + // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency + + + conn->OpenBCM = TRUE; + } + + + // See if BPQ for selective forwarding + + if (strstr(SID, "BPQ")) + conn->BPQBBS = TRUE; + + while (len > 0) + { + switch (SID[len--]) + { + case '-': + + len=0; + break; + + case '$': + + conn->BBSFlags |= BBS | MBLFORWARDING; + conn->Paging = FALSE; + + break; + + case 'F': // FBB Blocked Forwarding + + // We now support blocked uncompressed. Not necessarily compatible with FBB + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + if (conn->UserPointer->ForwardingInfo->AllowBlocked) + { + conn->BBSFlags |= FBBForwarding | BBS; + conn->BBSFlags &= ~MBLFORWARDING; + + conn->Paging = FALSE; + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + // Allocate a Header Block + + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + } + break; + + case 'J': + + // Suspected to be associated with Winlink Radio Only + + conn->BBSFlags &= ~WINLINKRO; + break; + + case 'B': + + if (conn->UserPointer->ForwardingInfo->AllowCompressed) + { + conn->BBSFlags |= FBBCompressed; + conn->DontSaveRestartData = FALSE; // Allow restarts + + // Look for 1 or 2 or 12 as next 2 chars + + if (SID[len+2] == '1') + { + if (conn->UserPointer->ForwardingInfo->AllowB1 || + conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 + conn->BBSFlags |= FBBB1Mode; + + if (SID[len+3] == '2') + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + break; + } + + if (SID[len+2] == '2') + { + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + if (conn->UserPointer->ForwardingInfo->AllowB1) + conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) + + } + break; + } + + break; + } + } + + // Only allow blocked non-binary to other BPQ Nodes + + if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) + { + // Switch back to MBL + + conn->BBSFlags |= MBLFORWARDING; + conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked + } + + return; +} + +VOID BBSSlowTimer() +{ + ConnectionInfo * conn; + int n; + + // Called every 10 seconds + + MCastTimer(); + + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + // Check for stuck BBS sessions (BBS session but no Node Session) + + int state; + + GetSemaphore(&ConSemaphore, 1); + SessionStateNoAck(conn->BPQStream, &state); + FreeSemaphore(&ConSemaphore); + + if (state == 0) // No Node Session + { + // is it safe just to clear Active ?? + + conn->InputMode = 0; // So Disconnect wont save partial transfer + conn->BBSFlags = 0; + Disconnected (conn->BPQStream); + continue; + } + + if (conn->BBSFlags & MCASTRX) + MCastConTimer(conn); + + + // Check SIDTImers - used to detect failure to compete SID Handshake + + if (conn->SIDResponseTimer) + { + conn->SIDResponseTimer--; + if (conn->SIDResponseTimer == 0) + { + // Disconnect Session + + Disconnect(conn->BPQStream); + } + } + } + } + + // Flush logs + + for (n = 0; n < 4; n++) + { + if (LogHandle[n]) + { + time_t LT = time(NULL); + if ((LT - LastLogTime[n]) > 30) + { + LastLogTime[n] = LT; + fclose(LogHandle[n]); + LogHandle[n] = NULL; + } + } + } +} + + +VOID FWDTimerProc() +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + ForwardingInfo->FwdTimer+=10; + + if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) + { + ForwardingInfo->FwdTimer=0; + + if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) + { + // Check Timebands + + struct FWDBAND ** Bands = ForwardingInfo->FWDBands; + int Count = 0; + time_t now = time(NULL); + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; // Secs in day + + while(Bands[Count]) + { + if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) + goto FWD; // In band + + Count++; + } + continue; // Out of bands + } + FWD: + if (ForwardingInfo->Enabled) + { + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + { + //Temp Debug Code + +// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", +// ForwardingInfo->ReverseFlag, +// SeeifMessagestoForward(user->BBSNumber, NULL), +// CountMessagestoForward(user)); + + if (SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) + + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + + // remove any old TempScript + + if (user->ForwardingInfo->TempConnectScript) + { + FreeList(user->ForwardingInfo->TempConnectScript); + user->ForwardingInfo->TempConnectScript = NULL; + } + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + } + } + } +} + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + +#ifdef WIN32 + ptr=_malloc_dbg(len, type, file, line); +#else + ptr = malloc(len); +#endif + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + + +struct MsgInfo * FindMessageByNumber(int msgno) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + if (Msg->number && Msg->number < msgno) // sometimes get zero msg number + return NULL; // Not found + + m--; + + } while (m > 0); + + return NULL; +} + +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, j = 0, val1, val2; + unsigned char hostname[100]=""; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // String is now encoded as hex pairs, but still need to decode old format + + for (i=0; i < len; i++) + { + if (Encrypt[i] < '0' || Encrypt[i] > 'F') + goto OldFormat; + } + + // Only '0' to 'F' + + for (i=0; i < len; i++) + { + val1 = Encrypt[i++]; + val1 -= '0'; + if (val1 > 9) + val1 -= 7; + + val2 = Encrypt[i]; + val2 -= '0'; + if (val2 > 9) + val2 -= 7; + + Pass[j] = (val1 << 4) | val2; + Pass[j] ^= hash[j]; + j++; + } + + return; + +OldFormat: + + for (i=0; i < len; i++) + { + Pass[i] = Encrypt[i] ^ hash[i]; + } + + return; +} + +int EncryptPass(char * Pass, char * Encrypt) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, val; + unsigned char hostname[100]; + unsigned char extendedpass[100]; + unsigned int passlen; + unsigned char * ptr; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // if password is less than 16 chars, extend with zeros + + passlen=(int)strlen(Pass); + + strcpy(extendedpass, Pass); + + if (passlen < 16) + { + for (i=passlen+1; i <= 16; i++) + { + extendedpass[i] = 0; + } + + passlen = 16; + } + + ptr = Encrypt; + Encrypt[0] = 0; + + for (i=0; i < passlen; i++) + { + val = extendedpass[i] ^ hash[i]; + ptr += sprintf(ptr, "%02X", val); + } + + return passlen * 2; +} + + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveInt64Value(config_setting_t * group, char * name, long long value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT64); + if(setting) + config_setting_set_int64(setting, value); +} + +VOID SaveFloatValue(config_setting_t * group, char * name, double value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); + if (setting) + config_setting_set_float(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) +{ + config_setting_t *setting; + struct Override ** Calls; + char Multi[10000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + + +VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) +{ + config_setting_t *setting; + char ** Calls; + char Multi[100000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '|'; + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + +int configSaved = 0; + +VOID SaveConfig(char * ConfigName) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + config_setting_t *root, *group, *bbs; + int i; + char Size[80]; + struct BBSForwardingInfo DummyForwardingInfo; + char Line[1024]; + + if (configSaved == 0) + { + // only create backup once per run + + CopyConfigFile(ConfigName); + configSaved = 1; + } + + memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); + + // Get rid of old config before saving + + config_destroy(&cfg); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Streams", MaxStreams); + SaveIntValue(group, "BBSApplNum", BBSApplNum); + SaveStringValue(group, "BBSName", BBSName); + SaveStringValue(group, "SYSOPCall", SYSOPCall); + SaveStringValue(group, "H-Route", HRoute); + SaveStringValue(group, "AMPRDomain", AMPRDomain); + SaveIntValue(group, "EnableUI", EnableUI); + SaveIntValue(group, "RefuseBulls", RefuseBulls); + SaveIntValue(group, "OnlyKnown", OnlyKnown); + SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); + SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); + SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); + SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); + SaveIntValue(group, "AllowAnon", AllowAnon); + SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); + SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); + SaveIntValue(group, "UserCantKillT", UserCantKillT); + + SaveIntValue(group, "ForwardToMe", ForwardToMe); + SaveIntValue(group, "SMTPPort", SMTPInPort); + SaveIntValue(group, "POP3Port", POP3InPort); + SaveIntValue(group, "NNTPPort", NNTPInPort); + SaveIntValue(group, "RemoteEmail", RemoteEmail); + SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); + + SaveIntValue(group, "MailForInterval", MailForInterval); + SaveStringValue(group, "MailForText", MailForText); + + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); + + SaveIntValue(group, "MulticastRX", MulticastRX); + + SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); + SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); + SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); + SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); + + SaveStringValue(group, "MyDomain", MyDomain); + SaveStringValue(group, "ISPSMTPName", ISPSMTPName); + SaveStringValue(group, "ISPEHLOName", ISPEHLOName); + SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); + SaveStringValue(group, "ISPAccountName", ISPAccountName); + SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + + + // Save Window Sizes + +#ifndef LINBPQ + + if (ConsoleRect.right) + { + sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, + ConsoleRect.top, ConsoleRect.bottom); + + SaveStringValue(group, "ConsoleSize", Size); + } + + sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); + SaveStringValue(group, "MonitorSize", Size); + + sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); + SaveStringValue(group, "WindowSize", Size); + + SaveIntValue(group, "Bells", Bells); + SaveIntValue(group, "FlashOnBell", FlashOnBell); + SaveIntValue(group, "StripLF", StripLF); + SaveIntValue(group, "WarnWrap", WarnWrap); + SaveIntValue(group, "WrapInput", WrapInput); + SaveIntValue(group, "FlashOnConnect", FlashOnConnect); + SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); + +#endif + + SaveIntValue(group, "Log_BBS", LogBBS); + SaveIntValue(group, "Log_TCP", LogTCP); + + sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + SaveStringValue(group, "Version", Size); + + // Save Welcome Messages and prompts + + SaveStringValue(group, "WelcomeMsg", WelcomeMsg); + SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); + SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); + + SaveStringValue(group, "Prompt", Prompt); + SaveStringValue(group, "NewUserPrompt", NewPrompt); + SaveStringValue(group, "ExpertPrompt", ExpertPrompt); + SaveStringValue(group, "SignoffMsg", SignoffMsg); + + SaveMultiStringValue(group, "RejFrom", RejFrom); + SaveMultiStringValue(group, "RejTo", RejTo); + SaveMultiStringValue(group, "RejAt", RejAt); + SaveMultiStringValue(group, "RejBID", RejBID); + + SaveMultiStringValue(group, "HoldFrom", HoldFrom); + SaveMultiStringValue(group, "HoldTo", HoldTo); + SaveMultiStringValue(group, "HoldAt", HoldAt); + SaveMultiStringValue(group, "HoldBID", HoldBID); + + SaveIntValue(group, "SendWP", SendWP); + SaveIntValue(group, "SendWPType", SendWPType); + SaveIntValue(group, "FilterWPBulls", FilterWPBulls); + SaveIntValue(group, "NoWPGuesses", NoWPGuesses); + + SaveStringValue(group, "SendWPTO", SendWPTO); + SaveStringValue(group, "SendWPVIA", SendWPVIA); + + SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); + + // Save Forwarding Config + + // Interval and Max Sizes and Aliases are not user specific + + SaveIntValue(group, "MaxTXSize", MaxTXSize); + SaveIntValue(group, "MaxRXSize", MaxRXSize); + SaveIntValue(group, "ReaddressLocal", ReaddressLocal); + SaveIntValue(group, "ReaddressReceived", ReaddressReceived); + SaveIntValue(group, "WarnNoRoute", WarnNoRoute); + SaveIntValue(group, "Localtime", Localtime); + SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); + + SaveMultiStringValue(group, "FWDAliases", AliasText); + + bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + ForwardingInfo = user->ForwardingInfo; + + if (ForwardingInfo == NULL) + continue; + + if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) + continue; // Ignore empty records; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + char Key[20] = "*"; + strcat (Key, user->Call); + group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + + SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); + SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); + SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); + SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); + SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); + SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); + + SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); + SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); + SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); + SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); + SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); + SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); + SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); + + SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); + SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); + SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); + SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); + SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); + SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); + + SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); + } + + + // Save Housekeeping config + + group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); + + SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); + SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); + SaveIntValue(group, "MaxMsgno", MaxMsgno); + SaveIntValue(group, "BidLifetime", BidLifetime); + SaveIntValue(group, "MaxAge", MaxAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "MaintInterval", MaintInterval); + SaveIntValue(group, "UserLifetime", UserLifetime); + SaveIntValue(group, "MaintTime", MaintTime); + SaveFloatValue(group, "PR", PR); + SaveFloatValue(group, "PUR", PUR); + SaveFloatValue(group, "PF", PF); + SaveFloatValue(group, "PNF", PNF); + SaveIntValue(group, "BF", BF); + SaveIntValue(group, "BNF", BNF); + SaveIntValue(group, "NTSD", NTSD); + SaveIntValue(group, "NTSF", NTSF); + SaveIntValue(group, "NTSU", NTSU); +// SaveIntValue(group, "AP", AP); +// SaveIntValue(group, "AB", AB); + SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); + SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); + SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); + SaveIntValue(group, "OverrideUnsent", OverrideUnsent); + SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); + SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); + + SaveOverride(group, "LTFROM", LTFROM); + SaveOverride(group, "LTTO", LTTO); + SaveOverride(group, "LTAT", LTAT); + + // Save UI config + + for (i=1; i<=32; i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + if (group) + { + SaveIntValue(group, "Enabled", UIEnabled[i]); + SaveIntValue(group, "SendMF", UIMF[i]); + SaveIntValue(group, "SendHDDR", UIHDDR[i]); + SaveIntValue(group, "SendNull", UINull[i]); + + if (UIDigi[i]) + SaveStringValue(group, "Digis", UIDigi[i]); + } + } + + // Save User Config + + bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + char stats[256], stats2[256]; + struct MsgStats * Stats; + char Key[20] = "*"; + + user = UserRecPtr[i]; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + strcat (Key, user->Call); +// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + { + strcpy(Key, user->Call); +// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + } + /* + SaveStringValue(group, "Name", user->Name); + SaveStringValue(group, "Address", user->Address); + SaveStringValue(group, "HomeBBS", user->HomeBBS); + SaveStringValue(group, "QRA", user->QRA); + SaveStringValue(group, "pass", user->pass); + SaveStringValue(group, "ZIP", user->ZIP); + SaveStringValue(group, "CMSPass", user->CMSPass); + + SaveIntValue(group, "lastmsg", user->lastmsg); + SaveIntValue(group, "flags", user->flags); + SaveIntValue(group, "PageLen", user->PageLen); + SaveIntValue(group, "BBSNumber", user->BBSNumber); + SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); + SaveIntValue(group, "WebSeqNo", user->WebSeqNo); + + SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); +*/ + Stats = &user->Total; + +// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Totsl", stats); + + Stats = &user->Last; + + sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", +// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Last", stats2); + + sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", + user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, + user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, + user->TimeLastConnected, stats, stats2); + + if (strlen(Line) < 10) + continue; + + SaveStringValue(bbs, Key, Line); + } + +/* + wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); + + for (i = 0; i <= NumberofWPrecs; i++) + { + char WPString[1024]; + long long val1, val2; + + WP = WPRecPtr[i]; + val1 = WP->last_modif; + val2 = WP->last_seen; + + sprintf(Key, "R%d", i); + + sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", + &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], + &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], + val1, val2); + + SaveStringValue(wp, Key, WPString); + } + + // Save Message Headers + + msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); + + memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); + + MsgHddrPtr[0]->type = 'X'; + MsgHddrPtr[0]->status = '2'; + MsgHddrPtr[0]->number = 0; + MsgHddrPtr[0]->length = LatestMsg; + + + for (i = 0; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d", Msg->number); + + n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + + SaveStringValue(msgs, Key, Line); + } + + // Save Bids + + msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofBIDs; i++) + { + sprintf(Key, "R%s", BIDRecPtr[i]->BID); + sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); + SaveStringValue(msgs, Key, Line); + } + +#ifdef LINBPQ + + if(! config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) + { + print("Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); + +#else +*/ + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + +//#endif + + config_destroy(&cfg); + +/* + +#ifndef LINBPQ + + // Save a copy with current Date/Time Stamp for debugging + + { + char Backup[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, + tm->tm_mday, tm->tm_hour, tm->tm_min); + + CopyFile(ConfigName, Backup, FALSE); // Copy to .bak + } +#endif +*/ +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +long long GetInt64Value(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int64 (setting); + + return 0; +} + +double GetFloatValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + return config_setting_get_float (setting); + } + return 0; +} + +int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return Default; +} + + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +BOOL GetConfig(char * ConfigName) +{ + int i; + char Size[80]; + config_setting_t *setting; + const char * ptr; + + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + char Msg[256]; + sprintf(Msg, "Config File Line %d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); +#ifdef WIN32 + MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); +#else + printf("%s", Msg); +#endif + config_destroy(&cfg); + return(EXIT_FAILURE); + } + +#ifdef FREEBSD + config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); +#else + config_set_auto_convert (&cfg, 1); +#endif + + group = config_lookup (&cfg, "main"); + + if (group == NULL) + return EXIT_FAILURE; + + SMTPInPort = GetIntValue(group, "SMTPPort"); + POP3InPort = GetIntValue(group, "POP3Port"); + NNTPInPort = GetIntValue(group, "NNTPPort"); + RemoteEmail = GetIntValue(group, "RemoteEmail"); + MaxStreams = GetIntValue(group, "Streams"); + BBSApplNum = GetIntValue(group, "BBSApplNum"); + EnableUI = GetIntValue(group, "EnableUI"); + MailForInterval = GetIntValue(group, "MailForInterval"); + RefuseBulls = GetIntValue(group, "RefuseBulls"); + OnlyKnown = GetIntValue(group, "OnlyKnown"); + SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); + SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); + DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); + DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); + ForwardToMe = GetIntValue(group, "ForwardToMe"); + AllowAnon = GetIntValue(group, "AllowAnon"); + UserCantKillT = GetIntValue(group, "UserCantKillT"); + + DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); + DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); + MaxTXSize = GetIntValue(group, "MaxTXSize"); + MaxRXSize = GetIntValue(group, "MaxRXSize"); + ReaddressLocal = GetIntValue(group, "ReaddressLocal"); + ReaddressReceived = GetIntValue(group, "ReaddressReceived"); + WarnNoRoute = GetIntValue(group, "WarnNoRoute"); + SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); + Localtime = GetIntValue(group, "Localtime"); + AliasText = GetMultiStringValue(group, "FWDAliases"); + GetStringValue(group, "BBSName", BBSName); + GetStringValue(group, "MailForText", MailForText); + GetStringValue(group, "SYSOPCall", SYSOPCall); + GetStringValue(group, "H-Route", HRoute); + GetStringValue(group, "AMPRDomain", AMPRDomain); + SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); + ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); + ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); + GetStringValue(group, "MyDomain", MyDomain); + GetStringValue(group, "ISPSMTPName", ISPSMTPName); + GetStringValue(group, "ISPPOP3Name", ISPPOP3Name); + ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); + ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); + GetStringValue(group, "ISPAccountName", ISPAccountName); + GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + GetStringValue(group, "ISPAccountName", ISPAccountName); + + sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default + GetStringValue(group, "SignoffMsg", SignoffMsg); + + DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); + + SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); + LogBBS = GetIntValue(group, "Log_BBS"); + LogTCP = GetIntValue(group, "Log_TCP"); + + MulticastRX = GetIntValue(group, "MulticastRX"); + +#ifndef LINBPQ + + GetStringValue(group, "MonitorSize", Size); + sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); + + GetStringValue(group, "WindowSize", Size); + sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); + + Bells = GetIntValue(group, "Bells"); + + FlashOnBell = GetIntValue(group, "FlashOnBell"); + + StripLF = GetIntValue(group, "StripLF"); + CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); + WarnWrap = GetIntValue(group, "WarnWrap"); + WrapInput = GetIntValue(group, "WrapInput"); + FlashOnConnect = GetIntValue(group, "FlashOnConnect"); + + GetStringValue(group, "ConsoleSize", Size); + sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, + &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); + +#endif + + // Get Welcome Messages + + setting = config_setting_get_member (group, "WelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + WelcomeMsg = _strdup(ptr); + } + else + WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "NewUserWelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + NewWelcomeMsg = _strdup(ptr); + } + else + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "ExpertWelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + ExpertWelcomeMsg = _strdup(ptr); + } + else + ExpertWelcomeMsg = _strdup(""); + + // Get Prompts + + setting = config_setting_get_member (group, "Prompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + Prompt = _strdup(ptr); + } + else + { + Prompt = malloc(20); + sprintf(Prompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "NewUserPrompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + NewPrompt = _strdup(ptr); + } + else + { + NewPrompt = malloc(20); + sprintf(NewPrompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "ExpertPrompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + ExpertPrompt = _strdup(ptr); + } + else + { + ExpertPrompt = malloc(20); + sprintf(ExpertPrompt, "de %s>\r\n", BBSName); + } + + TidyPrompts(); + + RejFrom = GetMultiStringValue(group, "RejFrom"); + RejTo = GetMultiStringValue(group, "RejTo"); + RejAt = GetMultiStringValue(group, "RejAt"); + RejBID = GetMultiStringValue(group, "RejBID"); + + HoldFrom = GetMultiStringValue(group, "HoldFrom"); + HoldTo = GetMultiStringValue(group, "HoldTo"); + HoldAt = GetMultiStringValue(group, "HoldAt"); + HoldBID = GetMultiStringValue(group, "HoldBID"); + + // Send WP Params + + SendWP = GetIntValue(group, "SendWP"); + SendWPType = GetIntValue(group, "SendWPType"); + + GetStringValue(group, "SendWPTO", SendWPTO); + GetStringValue(group, "SendWPVIA", SendWPVIA); + + SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); + + FilterWPBulls = GetIntValue(group, "FilterWPBulls"); + NoWPGuesses = GetIntValue(group, "NoWPGuesses"); + + if (SendWPAddrs[0] == NULL && SendWPTO[0]) + { + // convert old format TO and VIA to entry in SendWPAddrs + + SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry + + if (SendWPVIA[0]) + { + char WP[256]; + + sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); + SendWPAddrs[0] = _strdup(WP); + } + else + SendWPAddrs[0] = _strdup(SendWPTO); + + + SendWPAddrs[1] = 0; + + SendWPTO[0] = 0; + SendWPVIA[0] = 0; + } + + GetStringValue(group, "Version", Size); + sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); + + for (i=1; i<=32; i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_lookup (&cfg, Key); + + if (group) + { + UIEnabled[i] = GetIntValue(group, "Enabled"); + UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); + UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); + UINull[i] = GetIntValue(group, "SendNull"); + Size[0] = 0; + GetStringValue(group, "Digis", Size); + if (Size[0]) + UIDigi[i] = _strdup(Size); + } + } + + group = config_lookup (&cfg, "Housekeeping"); + + if (group) + { + LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); + LastTrafficTime = GetIntValue(group, "LastTrafficTime"); + MaxMsgno = GetIntValue(group, "MaxMsgno"); + LogAge = GetIntValue(group, "LogLifetime"); + BidLifetime = GetIntValue(group, "BidLifetime"); + MaxAge = GetIntValue(group, "MaxAge"); + if (MaxAge == 0) + MaxAge = 30; + UserLifetime = GetIntValue(group, "UserLifetime"); + MaintInterval = GetIntValue(group, "MaintInterval"); + + if (MaintInterval == 0) + MaintInterval = 24; + + MaintTime = GetIntValue(group, "MaintTime"); + + PR = GetFloatValue(group, "PR"); + PUR = GetFloatValue(group, "PUR"); + PF = GetFloatValue(group, "PF"); + PNF = GetFloatValue(group, "PNF"); + + BF = GetIntValue(group, "BF"); + BNF = GetIntValue(group, "BNF"); + NTSD = GetIntValue(group, "NTSD"); + NTSU = GetIntValue(group, "NTSU"); + NTSF = GetIntValue(group, "NTSF"); +// AP = GetIntValue(group, "AP"); +// AB = GetIntValue(group, "AB"); + DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); + SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); + SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); + + LTFROM = GetOverrides(group, "LTFROM"); + LTTO = GetOverrides(group, "LTTO"); + LTAT = GetOverrides(group, "LTAT"); + } + + return EXIT_SUCCESS; +} + + +int Connected(int Stream) +{ + int n, Mask; + CIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + char Title[100]; + int Freq = 0; + int Mode = 0; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * Sess1 = NULL, * Sess2; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + if (conn->BBSFlags & RunningConnectScript) + { + // BBS Outgoing Connect + + conn->paclen = 236; + + // Run first line of connect script + + ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions + ProcessBBSConnectScript(conn, ConnectedMsg, 15); + return 0; + } + } + + // Incoming Connect + + // Try to find port, freq, mode, etc + +#ifdef LINBPQ + SESS = &BPQHOSTVECTOR[0]; +#else + SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + SESS +=(Stream - 1); + + if (SESS) + Sess1 = SESS->HOSTSESSION; + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // See if L2 session - if so, get info from WL2K report line + + // if Session has report info, use it + + if (Sess2->Mode) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + else if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + Freq = PORT->WL2KInfo.Freq; + Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + } + } + } + + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + strlop(callsign, ' '); // Remove trailing spaces + + if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) + conn->RadioOnlyMode = 'T'; + else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) + conn->RadioOnlyMode = 'R'; + else + conn->RadioOnlyMode = 0; + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = LookupCall(callsign); + + if (user == NULL) + { + int Length=0; + + if (OnlyKnown) + { + // Unknown users not allowed + + n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + Disconnect(Stream); + return 0; + } + + user = AllocateUserRecord(callsign); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (SendNewUserMessage) + { + char * MailBuffer = malloc(100); + Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %d\r\n", callsign, port, Freq, Mode); + + sprintf(Title, "New User %s", callsign); + + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + if (user == NULL) return 0; // Cant happen?? + + if (!DontHoldNewUsers) + user->flags |= F_HOLDMAIL; + + if (DefaultNoWINLINK) + user->flags |= F_NOWINLINK; + + // Always set WLE User - can't see it doing any harm + + user->flags |= F_Temp_B2_BBS; + + conn->NewUser = TRUE; + } + + user->TimeLastConnected = time(NULL); + user->Total.ConnectsIn++; + + conn->UserPointer = user; + + conn->lastmsg = user->lastmsg; + + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (paclen == 0) + { + paclen = 236; + + if (conn->SessType & Sess_PACTOR) + paclen = 100; + } + + conn->paclen = paclen; + + // Set SYSOP flag if user is defined as SYSOP and Host Session + + if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + if (conn->Secure_Session && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + Mask = 1 << (GetApplNum(Stream) - 1); + + if (user->flags & F_Excluded) + { + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + Disconnect(Stream); + return 0; + } + + if (port) + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", + user->Call, port, Freq, WL2KModes[Mode]); + else + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt + + if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) + conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks + else + conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID + + { + BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; + BOOL WL2KRO = FALSE; + + struct BBSForwardingInfo * ForwardingInfo; + + if (conn->RadioOnlyMode == 'R') + WL2KRO = 1; + + conn->PageLen = user->PageLen; + conn->Paging = (user->PageLen > 0); + + if (user->flags & F_Temp_B2_BBS) + { + // An RMS Express user that needs a temporary BBS struct + + if (user->ForwardingInfo == NULL) + { + // we now save the Forwarding info if BBS flag is cleared, + // so there may already be a ForwardingInfo + + user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + } + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; + + ForwardingInfo = user->ForwardingInfo; + + ForwardingInfo->AllowCompressed = TRUE; + B1 = ForwardingInfo->AllowB1 = FALSE; + B2 = ForwardingInfo->AllowB2 = TRUE; + BLOCKED = ForwardingInfo->AllowBlocked = TRUE; + } + + if (conn->NewUser) + { + BLOCKED = TRUE; + BIN = TRUE; + B2 = TRUE; + } + + if (user->ForwardingInfo) + { + BLOCKED = user->ForwardingInfo->AllowBlocked; + if (BLOCKED) + { + BIN = user->ForwardingInfo->AllowCompressed; + B1 = user->ForwardingInfo->AllowB1; + B2 = user->ForwardingInfo->AllowB2; + } + } + + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + if (conn->RadioOnlyMode) + nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); + + nodeprintf(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", + BLOCKED ? "FW": "", WL2KRO ? "" : "J"); + +// if (user->flags & F_Temp_B2_BBS) +// nodeprintf(conn,";PQ: 66427529\r"); + + // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + } + + if ((user->Name[0] == 0) & AllowAnon) + strcpy(user->Name, user->Call); + + if (user->Name[0] == 0) + { + conn->Flags |= GETTINGUSER; + BBSputs(conn, NewUserPrompt); + } + else + SendWelcomeMsg(Stream, conn, user); + + RefreshMainWindow(); + + return 0; + } + } + + return 0; +} + +int Disconnected (int Stream) +{ + struct UserInfo * user = NULL; + CIRCUIT * conn; + int n; + char Msg[255]; + int len; + char DiscMsg[] = "DISCONNECTED "; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active == FALSE) + return 0; + + // if still running connect script, reenter it to see if + // there is an else + + if (conn->BBSFlags & RunningConnectScript) + { + // We need to see if we got as far as connnected, + // as if we have we need to reset the connect script + // over the ELSE + + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + // First see if any script left + + if (Scripts[ForwardingInfo->ScriptIndex]) + { + if (ForwardingInfo->MoreLines == FALSE) + { + // Have reached end of script, so need to set back over ELSE + + ForwardingInfo->ScriptIndex--; + ForwardingInfo->MoreLines = TRUE; + } + + // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + + + ProcessBBSConnectScript(conn, DiscMsg, 15); + return 0; + } + } + + // if sysop was chatting to user clear link +#ifndef LINBPQ + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, "User has disconnected\n", 23); + BBSConsole.Console->SysopChatStream = 0; + } +#endif + ClearQueue(conn); + + if (conn->PacLinkCalls) + free(conn->PacLinkCalls); + + if (conn->InputBuffer) + { + free(conn->InputBuffer); + conn->InputBuffer = NULL; + conn->InputBufferLen = 0; + } + + if (conn->InputMode == 'B') + { + // Save partly received message for a restart + + if (conn->BBSFlags & FBBB1Mode) + if (conn->Paclink == 0) // Paclink doesn't do restarts + if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. + if (conn->DontSaveRestartData == FALSE) + SaveFBBBinary(conn); + } + + conn->Active = FALSE; + + if (conn->FwdMsg) + conn->FwdMsg->Locked = 0; // Unlock + + RefreshMainWindow(); + + RemoveTempBIDS(conn); + + len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + if (conn->FBBHeaders) + { + struct FBBHeaderLine * FBBHeader; + int n; + + for (n = 0; n < 5; n++) + { + FBBHeader = &conn->FBBHeaders[n]; + + if (FBBHeader->FwdMsg) + FBBHeader->FwdMsg->Locked = 0; // Unlock + + } + + free(conn->FBBHeaders); + conn->FBBHeaders = NULL; + } + + if (conn->UserPointer) + { + struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; + + if (FWDInfo) + { + FWDInfo->Forwarding = FALSE; + +// if (FWDInfo->UserCall[0]) // Will be set if RMS +// { +// FindNextRMSUser(FWDInfo); +// } +// else + FWDInfo->FwdTimer = 0; + } + } + + conn->BBSFlags = 0; // Clear ARQ Mode + + return 0; + } + } + return 0; +} + +int DoReceivedData(int Stream) +{ + int count, InputLen; + size_t MsgLen; + int n; + CIRCUIT * conn; + struct UserInfo * user; + char * ptr, * ptr2; + char * Buffer; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. + + do + { + // May have several messages per packet, or message split over packets + + OuterLoop: + if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode + { + conn->InputBufferLen += 1000; + conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); + } + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0 && conn->InputMode != 'Y') + return 0; + + conn->InputLen += InputLen; + + if (conn->InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + + if (conn->InputMode == 'Y') // YAPP + { + if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process + goto OuterLoop; + + return 0; + } + + if (conn->InputMode == 'B') + { + // if in OpenBCM mode, remove FF transparency + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = conn->InputBuffer; + unsigned char * ptr2; + int Len; + unsigned char c; + + // We can come through here again for the + // same data as we wait for a full packet + // So only check last InputLen bytes + + ptr1 += (conn->InputLen - InputLen); + ptr2 = ptr1; + Len = InputLen; + + while (Len--) + { + c = *(ptr1++); + + if (conn->InTelnetExcape) // Last char was ff + { + conn->InTelnetExcape = FALSE; + continue; + } + + *(ptr2++) = c; + + if (c == 0xff) // + conn->InTelnetExcape = TRUE; + } + + conn->InputLen = (int)(ptr2 - conn->InputBuffer); + } + + UnpackFBBBinary(conn); + goto OuterLoop; + } + else + { + + loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + return 0; + } + + user = conn->UserPointer; + + if (conn->BBSFlags & (MCASTRX | SYNCMODE)) + { + // MCAST and SYNCMODE deliver full packets + + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + + conn->InputLen=0; + continue; + } + + // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, + // but this relies on both arriving in same packet. + // Need to check for LF and start of packet and ignore it + // But what if client is only using LF?? + // (WLE sends SID with CRLF, other packets with CR only) + + // We don't get here on the data part of a binary transfer, so + // don't need to worry about messing up binary data. + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); + + if (ptr) + conn->usingCR = 1; + + if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR + ptr = ptr2; // Use LF + + if (ptr) // CR or LF in buffer + { + conn->lastLineEnd = *(ptr); + + *(ptr) = '\r'; // In case was LF + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single msg in buffer + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + } + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (ptr2-ptr); + + Buffer = malloc(MsgLen + 100); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); + else + ProcessLine(conn, user, Buffer, (int)MsgLen); + } + free(Buffer); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= (int)MsgLen; + + goto loop; + + } + } + else + { + // Could be a YAPP Header + + + if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init + { + UCHAR YAPPRR[2]; + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + + conn->InputMode = 'Y'; + QueueMsg(conn, YAPPRR, 2); + + conn->InputLen = 0; + return 0; + } + } + } + + } while (count > 0); + + return 0; + } + } + + // Socket not found + + return 0; + +} +int DoBBSMonitorData(int Stream) +{ +// UCHAR Buffer[1000]; + UCHAR buff[500]; + + int len = 0,count=0; + int stamp; + + do + { + stamp=GetRaw(Stream, buff,&len,&count); + + if (len == 0) return 0; + + SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); + } + + while (count > 0); + + + return 0; + +} + +VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = (int)(ptr1 - conn->MailBuffer); + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + size_t Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) +{ + Buffer[len] = 0; +// Debugprintf(Buffer); + + if (len == 1 && Buffer[0] == 13) + return; + + if (conn->Flags & SENDTITLE) + { + // Waiting for Subject: prompt + + struct MsgInfo * Msg = conn->FwdMsg; + + nodeprintf(conn, "%s\r", Msg->title); + + conn->Flags &= ~SENDTITLE; + conn->Flags |= SENDBODY; + return; + + } + + if (conn->Flags & SENDBODY) + { + // Waiting for Enter Message Prompt + + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + char * MsgPtr; + int MsgLen; + int Index = 0; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &conn->FwdMsg->datereceived, 4); + tm = gmtime(&temp); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + BBSputs(conn, "\r"); + + MsgLen = RemoveLF(MsgPtr, MsgLen); + + QueueMsg(conn, MsgPtr, MsgLen); + + if (user->ForwardingInfo->SendCTRLZ) + nodeprintf(conn, "\r\x1a"); + else + nodeprintf(conn, "\r/ex\r"); + + free(MsgBytes); + + conn->FBBMsgsSent = TRUE; + + + if (conn->FwdMsg->type == 'P') + Index = PMSG; + else if (conn->FwdMsg->type == 'B') + Index = BMSG; + else if (conn->FwdMsg->type == 'T') + Index = TMSG; + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + conn->Flags &= ~SENDBODY; + conn->Flags |= WAITPROMPT; + + return; + } + + if (conn->Flags & WAITPROMPT) + { + if (Buffer[len-2] != '>') + return; + + conn->Flags &= ~WAITPROMPT; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + SaveMessageDatabase(); + + conn->UserPointer->ForwardingInfo->MsgCount--; + + // See if any more to forward + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + conn->Flags |= SENDTITLE; + Msg = conn->FwdMsg; + + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + Disconnect(conn->BPQStream); + } + return; + } +} + + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +extern UCHAR * infile; + +BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); + + +VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + char * Cmd, * Arg1; + char * Context; + char seps[] = " \t\r"; + int CmdLen; + + if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + BBSputs(conn, "OK\r"); + return; + } + + if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) + { + char Reply[32]; + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + sprintf(Reply, "POSYNCLOGON %s\r", BBSName); + BBSputs(conn, Reply); + return; + } + + if (conn->BBSFlags & SYNCMODE) + { + ProcessSyncModeMessage(conn, user, Buffer, len); + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) + + // *** Protocol Error + // Already Connected + // Invalid Command + + if (_memicmp(Buffer, "Already Connected", 17) == 0 || + _memicmp(Buffer, "Invalid Command", 15) == 0 || + _memicmp(Buffer, "*** Protocol Error", 18) == 0) + { + conn->BBSFlags |= DISCONNECTING; + Disconnect(conn->BPQStream); + return; + } + + if (conn->BBSFlags & FBBForwarding) + { + ProcessFBBLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & FLARQMODE) + { + ProcessFLARQLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MCASTRX) + { + ProcessMCASTLine(conn, user, Buffer, len); + return; + } + + + if (conn->BBSFlags & TEXTFORWARDING) + { + ProcessTextFwdLine(conn, user, Buffer, len); + return; + } + + // if chatting to sysop pass message to BBS console + + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, Buffer,len); + return; + } + + if (conn->Flags & GETTINGMESSAGE) + { + ProcessMsgLine(conn, user, Buffer, len); + return; + } + if (conn->Flags & GETTINGTITLE) + { + ProcessMsgTitle(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MBLFORWARDING) + { + ProcessMBLLine(conn, user, Buffer, len); + return; + } + + if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name + { + if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') + { + struct BBSForwardingInfo * ForwardingInfo; + + conn->Flags &= ~GETTINGUSER; + + // New User is a BBS - create a temp struct for it + + if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name + { + // Not defined as BBS - allocate and initialise forwarding structure + + user->flags |= F_Temp_B2_BBS; + + // An RMS Express user that needs a temporary BBS struct + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + ForwardingInfo->AllowCompressed = TRUE; + ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; + } + SaveUserDatabase(); + } + else + { + if (conn->Flags & GETTINGUSER) + { + conn->Flags &= ~GETTINGUSER; + if (len > 18) + len = 18; + + memcpy(user->Name, Buffer, len-1); + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + UpdateWPWithUserInfo(user); + return; + } + } + } + + // Process Command + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + { + // Waiting for paging prompt + + if (len > 1) + { + if (_memicmp(Buffer, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + } + + conn->LinesSent = 0; + return; + } + + if (user->Temp->ListSuspended) + { + // Paging limit hit when listing. User may abort, continue, or read one or more messages + + ProcessSuspendedListCommand(conn, user, Buffer, len); + return; + } + if (len == 1) + { + SendPrompt(conn, user); + return; + } + + Buffer[len] = 0; + + if (strstr(Buffer, "ARQ:FILE:")) + { + // Message from FLARQ + + conn->BBSFlags |= FLARQMODE; + strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files + + // Create a Temp Messge Stucture + + CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); + + Buffer[len++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + return; + } + if (Buffer[0] == ';') // WL2K Comment + { + if (memcmp(Buffer, ";FW:", 4) == 0) + { + // Paclink User Select (poll for list) + + char * ptr1,* ptr2, * ptr3; + int index=0; + + // Convert string to Multistring + + Buffer[len-1] = 0; + + conn->PacLinkCalls = zalloc(len*3); + + ptr1 = &Buffer[5]; + ptr2 = (char *)conn->PacLinkCalls; + ptr2 += (len * 2); + strcpy(ptr2, ptr1); + + while (ptr2) + { + ptr3 = strlop(ptr2, ' '); + + if (strlen(ptr2)) + conn->PacLinkCalls[index++] = ptr2; + + ptr2 = ptr3; + } + + return; + } + + if (memcmp(Buffer, ";FR:", 4) == 0) + { + // New Message from TriMode - Just igonre till I know what to do with it + + return; + } + + // Ignore other ';' message + + return; + } + + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // If a BBS, set BBS Flag + + if (user->flags & ( F_BBS | F_Temp_B2_BBS)) + { + if (user->ForwardingInfo) + { + if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) + { + BBSputs(conn, "Already Connected\r"); + Flush(conn); + Sleep(500); + Disconnect(conn->BPQStream); + return; + } + } + + if (user->ForwardingInfo) + { + user->ForwardingInfo->Forwarding = TRUE; + user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately + } + } + + if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) + { + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + conn->FBBChecksum = 0; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + } + else + FBBputs(conn, ">\r"); + + } + + return; + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + // Check List first. If any other, save last listed to user record. + + if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) + { + DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); + SendPrompt(conn, user); + return; + } + + if (conn->lastmsg > user->lastmsg) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) + { + DoShowRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "AUTH") == 0) + { + DoAuthCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + Disconnect(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "Node", 4) == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + ReturntoNode(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "IDLETIME", 4) == 0) + { + DoSetIdleTime(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) + { + DoSetMsgNo(conn, user, Arg1, Context); + return; + } + + if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) + { + DoDeliveredCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "K", 1) == 0) + { + DoKillCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) + { + ListFiles(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "READFILE", 4) == 0) + { + ReadBBSFile(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) + { + if (conn->sysop == 0) + nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); + else + { + ReRouteMessages(); + nodeprintf(conn, "Ok\r"); + } + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "YAPP", 4) == 0) + { + YAPPSendFile(conn, user, Arg1); + return; + } + + if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) + { + DoUnholdCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_stricmp(Cmd, "IMPORT") == 0) + { + DoImportCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "EXPORT") == 0) + { + DoExportCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "I", 1) == 0) + { + char * Save; + char * MsgBytes; + + if (Arg1) + { + // User WP lookup + + DoWPLookup(conn, user, Cmd[1], Arg1); + SendPrompt(conn, user); + return; + } + + + MsgBytes = Save = ReadInfoFile("info.txt"); + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + BBSputs(conn, "SYSOP has not created an INFO file\r"); + + + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "Name", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 17) + Arg1[17] = 0; + + strcpy(user->Name, Arg1); + UpdateWPWithUserInfo(user); + + } + + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "OP", 2) == 0) + { + int Lines; + + // Paging Control. Param is number of lines per page + + if (Arg1) + { + Lines = atoi(Arg1); + + if (Lines) // Sanity Check + { + if (Lines < 10) + { + nodeprintf(conn,"Page Length %d is too short\r", Lines); + SendPrompt(conn, user); + return; + } + } + + user->PageLen = Lines; + conn->PageLen = Lines; + conn->Paging = (Lines > 0); + SaveUserDatabase(); + } + + nodeprintf(conn,"Page Length is %d\r", user->PageLen); + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "QTH", CmdLen) == 0) + { + if (Arg1) + { + // QTH may contain spaces, so put back together, and just split at cr + + Arg1[strlen(Arg1)] = ' '; + strtok_s(Arg1, "\r", &Context); + + if (strlen(Arg1) > 60) + Arg1[60] = 0; + + strcpy(user->Address, Arg1); + UpdateWPWithUserInfo(user); + + } + + nodeprintf(conn,"QTH is %s\r", user->Address); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "ZIP", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 8) + Arg1[8] = 0; + + strcpy(user->ZIP, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + } + + nodeprintf(conn,"ZIP is %s\r", user->ZIP); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "CMSPASS", 7) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 15) + Arg1[15] = 0; + + strcpy(user->CMSPass, Arg1); + nodeprintf(conn,"CMS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "PASS", CmdLen) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 12) + Arg1[12] = 0; + + strcpy(user->pass, Arg1); + nodeprintf(conn,"BBS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + + if (_memicmp(Cmd, "R", 1) == 0) + { + DoReadCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "S", 1) == 0) + { + if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) + SendPrompt(conn, user); + return; + } + + if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("help.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + { + BBSputs(conn, "A - Abort Output\r"); + BBSputs(conn, "B - Logoff\r"); + BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); + BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); + BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); + BBSputs(conn, "INFO - Display information about this BBS\r"); + BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); + BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); + BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); + BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); + + BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); + BBSputs(conn, "L - List Message(s) - \r"); + BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); + BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); + BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); + BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); + BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); + BBSputs(conn, " LC = List TO fields of all active bulletins\r"); + BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); + BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); + + BBSputs(conn, "N Name - Set Name\r"); + BBSputs(conn, "NODE - Return to Node\r"); + BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); + BBSputs(conn, "PASS Password - Set BBS Password\r"); + BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); + BBSputs(conn, "Q QTH - Set QTH\r"); + BBSputs(conn, "R - Read Message(s) - R num \r"); + BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); + BBSputs(conn, "READ Name - Read File\r"); + + BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); + BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); + BBSputs(conn, "X - Toggle Expert Mode\r"); + BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); + if (conn->sysop) + { + BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); + BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); + BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); + BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); + BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); + BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); + BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); + BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); + BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); + } + } + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "Ver", CmdLen) == 0) + { + nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 40) Arg1[40] = 0; + + strcpy(user->HomeBBS, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + + if (!strchr(Arg1, '.')) + BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); + } + + nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) + { + DoEditUserCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "POLLRMS") == 0) + { + DoPollRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) + { + DoHousekeepingCmd(conn, user, Arg1, Context); + return; + } + + + if (_stricmp(Cmd, "FWD") == 0) + { + DoFwdCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "X", 1) == 0) + { + user->flags ^= F_Expert; + + if (user->flags & F_Expert) + BBSputs(conn, "Expert Mode\r"); + else + BBSputs(conn, "Expert Mode off\r"); + + SaveUserDatabase(); + SendPrompt(conn, user); + return; + } + + if (conn->Flags == 0) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + } + + // Send if possible + + Flush(conn); +} + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) +{ + // seems to be printf to a socket + + char buff[600]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + BBSputs(conn, buff); +} + +// Code to delete obsolete files from Mail folder + +#ifdef WIN32 + +int DeleteRedundantMessages() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + int Msgno; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, MailDir); + strcat(szDir, "\\*.mes"); + + + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return 0; + } + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + Msgno = atoi(&ffd.cFileName[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); + Debugprintf("Tidy Mail - Delete %s\n", File); + +// if (DeletetoRecycleBin) + DeletetoRecycle(File); +// else +// DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return 0; +} + +#else + +#include + +int MsgFilter(const struct dirent * dir) +{ + return (strstr(dir->d_name, ".mes") != 0); +} + +int DeleteRedundantMessages() +{ + struct dirent **namelist; + int n; + struct stat STAT; + int Msgno = 0, res; + char File[100]; + + n = scandir("Mail", &namelist, MsgFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + if (stat(namelist[n]->d_name, &STAT) == 0); + { + Msgno = atoi(&namelist[n]->d_name[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "Mail/%s", namelist[n]->d_name); + printf("Deleting %s\n", File); + unlink(File); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +VOID TidyWelcomeMsg(char ** pPrompt) +{ + // Make sure Welcome Message doesn't ends with > + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + while (i >= 0 && Prompt[i] == '>') + Prompt[i--] = 0; + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompt(char ** pPrompt) +{ + // Make sure prompt ends > CR LF + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + if (Prompt[i] != '>') + strcat(Prompt, ">"); + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompts() +{ + TidyPrompt(&Prompt); + TidyPrompt(&NewPrompt); + TidyPrompt(&ExpertPrompt); +} + +BOOL SendARQMail(CIRCUIT * conn) +{ + conn->NextMessagetoForward = FirstMessageIndextoForward; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + char MsgHddr[512]; + int HddrLen; + char TimeString[64]; + char * WholeMessage; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + Msg = conn->FwdMsg; + WholeMessage = malloc(Msg->length + 512); + + FormatTime(TimeString, (time_t)Msg->datecreated); + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); + + HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", + TimeString, Msg->to, Msg->from, Msg->title); + + MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", + BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); + + WholeMessage[MsgLen] = 0; + QueueMsg(conn,WholeMessage, MsgLen); + + free(WholeMessage); + free(MsgBytes); + + // FLARQ doesn't ACK the message, so set flag to look for all acked + + conn->BBSFlags |= ARQMAILACK; + conn->ARQClearCount = 10; // To make sure clear isn't reported too soon + + return TRUE; + } + + // Nothing to send - close + + Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); + + conn->CloseAfterFlush = 20; + return FALSE; +} + +char *stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup (ch1); + chN2 = _strdup (ch2); + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + chNdx = chN2; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + + chNdx = strstr (chN1, chN2); + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + free (chN1); + free (chN2); + return chRet; +} + +#ifdef WIN32 + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\*.*"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + nodeprintf(conn, "No Files\r"); + return; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); +} + +#else + +#include + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + struct dirent **namelist; + int n, i; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + return; +} +#endif + +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + char * MsgBytes; + + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Missing Filename\r"); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(MsgBytes); + + nodeprintf(conn, "\r\r[End of File %s]\r", filename); + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); +} + +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + struct TempUserInfo * Temp = user->Temp; + + Buffer[len] = 0; + + // Command entered during listing pause. May be A R or C (or ) + + if (Buffer[0] == 'A' || Buffer[0] == 'a') + { + // Abort + + Temp->ListActive = Temp->ListSuspended = FALSE; + SendPrompt(conn, user); + return; + } + + if (_memicmp(Buffer, "R ", 2) == 0) + { + // Read Message(es) + + int msgno; + char * ptr; + char * Context; + + ptr = strtok_s(&Buffer[2], " ", &Context); + + while (ptr) + { + msgno = atoi(ptr); + ReadMessage(conn, user, msgno); + + ptr = strtok_s(NULL, " ", &Context); + } + + nodeprintf(conn, "bort, , = Continue..>"); + return; + } + + if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) + { + // Resume Listing from where we left off + + DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "bort, , = Continue..>"); + +} +/* +CreateMessageWithAttachments() +{ + int i; + char * ptr, * ptr2, * ptr3, * ptr4; + char Boundary[1000]; + BOOL Multipart = FALSE; + BOOL ALT = FALSE; + int Partlen; + char * Save; + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + char FileName[100][250] = {""}; + int FileLen[100]; + char * FileBody[100]; + char * MallocSave[100]; + UCHAR * NewMsg; + + int Files = 0; + + ptr = Msg; + + if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 2000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sockptr->socket, 0); + return FALSE; + } + } + + + NewMsg = sockptr->MailBuffer + 1000; + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); + + for (i = 1; i < Files; i++) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + + for (i = 0; i < Files; i++) + { + memcpy(NewMsg, FileBody[i], FileLen[i]); + NewMsg += FileLen[i]; + free(MallocSave[i]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); + *Body = sockptr->MailBuffer + 1000; + + return TRUE; // B2 Message +} + +*/ +VOID CreateUserReport() +{ + struct UserInfo * User; + int i; + char Line[200]; + int len; + char File[MAX_PATH]; + FILE * hFile; + + sprintf(File, "%s/UserList.csv", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + { + Debugprintf("Failed to create UserList.csv"); + return; + } + + for (i=1; i <= NumberofUsers; i++) + { + User = UserRecPtr[i]; + + len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", + User->Call, + User->lastmsg, + FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + User->flags, + User->Name, + User->Address, + User->RMSSSIDBits, + User->HomeBBS, + User->QRA, + User->ZIP +// struct MsgStats Total; +// struct MsgStats Last; + ); + fwrite(Line, 1, len, hFile); + } + + fclose(hFile); +} + +BOOL ProcessYAPPMessage(CIRCUIT * conn) +{ + int Len = conn->InputLen; + UCHAR * Msg = conn->InputBuffer; + int pktLen = Msg[1]; + char Reply[2] = {ACK}; + int NameLen, SizeLen, OptLen; + char * ptr; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char Mess[255]; + int len; + char * FN = &Msg[2]; + + switch (Msg[0]) + { + case ENQ: // YAPP Send_Init + + // Shouldn't occur in session. Reset state + + Mess[0] = ACK; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + conn->InputLen = 0; + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + return TRUE; + + case SOH: + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // YAPPC has date/time in dos format + + if (Len < Msg[1] + 1) + return 0; + + NameLen = (int)strlen(FN); + strcpy(conn->ARQFilename, FN); + ptr = &Msg[3 + NameLen]; + SizeLen = (int)strlen(ptr); + FileSize = atoi(ptr); + + // Check file name for unsafe characters (.. / \) + + if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Mess[0] = NAK; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + OptLen = pktLen - (NameLen + SizeLen + 2); + + conn->YAPPDate = 0; + + if (OptLen >= 8) // We have a Date/Time for YAPPC + { + ptr = ptr + SizeLen + 1; + conn->YAPPDate = strtol(ptr, NULL, 16); + } + + // Check Size + + if (FileSize > MaxRXSize) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + // Make sure file does not exist + + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + fclose(hFile); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + + conn->MailBufferSize = FileSize; + conn->MailBuffer=malloc(FileSize); + conn->YAPPLen = 0; + + if (conn->YAPPDate) // If present use YAPPC + Reply[1] = ACK; //Receive_TPK + else + Reply[1] = 2; //Rcv_File + + QueueMsg(conn, Reply, 2); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len, LOG_BBS); + + conn->InputLen = 0; + return FALSE; + + case STX: + + // Data Packet + + // Check we have it all + + if (conn->YAPPDate) // If present use YAPPC so have checksum + { + if (pktLen > (Len - 3)) // -3 for header and checksum + return 0; // Wait for rest + } + else + { + if (pktLen > (Len - 2)) // -2 for header + return 0; // Wait for rest + } + + // Save data and remove from buffer + + // if YAPPC check checksum + + if (conn->YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = &Msg[2]; + + i = pktLen; + + while(i--) + Sum += *(uptr++); + + if (Sum != *uptr) + { + // Checksum Error + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + } + + if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) + { + // Too Big ?? + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + + + memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); + conn->YAPPLen += pktLen; + + if (conn->YAPPDate) + ++pktLen; // Add Checksum + + conn->InputLen -= (pktLen + 2); + memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); + + return TRUE; + + case ETX: + + // End Data + + + + if (conn->YAPPLen == conn->MailBufferSize) + { + // All received + + int ret; + DWORD Written = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + +#ifdef WIN32 + hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); + + if (conn->YAPPDate) + { + FILETIME FileTime; + struct tm TM; + struct timeval times[2]; + time_t TT; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + TT = mktime(&TM); + times[0].tv_sec = times[1].tv_sec = + times[0].tv_usec = times[1].tv_usec = 0; + + DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); + ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); + ret = GetLastError(); + + } + CloseHandle(hFile); + } +#else + + hFile = fopen(MsgFile, "wb"); + if (hFile) + { + Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); + fclose(hFile); + + if (conn->YAPPDate) + { + struct tm TM; + struct timeval times[2]; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + times[0].tv_sec = times[1].tv_sec = mktime(&TM); + times[0].tv_usec = times[1].tv_usec = 0; + } + } +#endif + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (Written != conn->YAPPLen) + { + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + } + } + + Reply[1] = 3; //Ack_EOF + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + + return TRUE; + + case EOT: + + // End Session + + Reply[1] = 4; // Ack_EOT + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + conn->InputMode = 0; + + len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + + return TRUE; + + case CAN: + + // Abort + + Mess[0] = ACK; + Mess[1] = 5; // CAN Ack + QueueMsg(conn, Mess, 2); + Flush(conn); + + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + + // There may be a reason after the CAN + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + + len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + return FALSE; + + case ACK: + + switch (Msg[1]) + { + case 1: // Rcv_Rdy + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + len = (int)strlen(conn->ARQFilename) + 3; + + strcpy(&Mess[2], conn->ARQFilename); + len += sprintf(&Mess[len], "%d", conn->MailBufferSize); + len++; // include null + Mess[0] = SOH; + Mess[1] = len - 2; + + QueueMsg(conn, Mess, len); + Flush(conn); + conn->InputLen = 0; + + return FALSE; + + case 2: + + // Start sending message + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + case 3: + + // ACK EOF - Send EOT + + + Mess[0] = EOT; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputLen = 0; + return FALSE; + + case 4: + + // ACK EOT + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + conn->InputLen = 0; + return FALSE; + + default: + conn->InputLen = 0; + return FALSE; + + + + } + + case NAK: + + // Either Reject or Restart + + // RE Resume NAK len R NULL (File size in ASCII) NULL + + if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) + { + int posn = atoi(&Msg[4]); + + conn->YAPPLen += posn; + conn->MailBufferSize -= posn; + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + } + + // There may be a reason after the ack + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + return FALSE; + } + + nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + + return FALSE; + +} + +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Filename missing\r"); + SendPrompt(conn, user); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + SendPrompt(conn, user); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + char Mess[255]; + strcpy(conn->ARQFilename, filename); + conn->MailBuffer = malloc(FileSize); + conn->MailBufferSize = FileSize; + conn->YAPPLen = 0; + fread(conn->MailBuffer, 1, FileSize, hFile); + fclose(hFile); + + Mess[0] = ENQ; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputMode = 'Y'; + + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); + SendPrompt(conn, user); +} + +void YAPPSendData(ConnectionInfo * conn) +{ + char Mess[258]; + + conn->BBSFlags |= YAPPTX; + + while (TXCount(conn->BPQStream) < 15) + { + int Left = conn->MailBufferSize; + + if (Left == 0) + { + // Finished - send End Data + + Mess[0] = ETX; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->BBSFlags &= ~YAPPTX; + break; + } + + if (Left > conn->paclen - 2) // 2 byte header + Left = conn->paclen -2; + + memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(conn, Mess, Left + 2); + Flush(conn); + + conn->YAPPLen += Left; + conn->MailBufferSize -= Left; + } +} + +char * AddUser(char * Call, char * password, BOOL BBSFlag) +{ + struct UserInfo * USER; + + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + return("User already exists\r\n"); + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (strlen(password) > 12) + password[12] = 0; + + strcpy(USER->pass, password); + + if (BBSFlag) + { + if(SetupNewBBS(USER)) + USER->flags |= F_BBS; + else + printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); + } + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + return("User added\r\n"); +} + +// Server Support Code + +// For the moment only internal REQDIR and REQFIL. + +// May add WPSERV and user implemented servers +/* +F6FBB BBS > + SP REQDIR @ F6ABJ.FRA.EU + Title of message : + YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + F6FBB BBS > + SP REQFIL @ F6ABJ.FRA.EU + Title of message : + DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + Note Text not used. + +*/ + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); + +BOOL ProcessReqDir(struct MsgInfo * Msg) +{ + char * Buffer; + int Len = 0; + char * ptr; + + // Parse title - gives directory and return address + + // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char Pattern[64]; + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + +#else + + #include + + struct dirent **namelist; + int n, i; + struct stat STAT; + int res; + char FN[256]; + +#endif + + strcpy(Pattern, Msg->title); + + ptr = strchr(Pattern, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(Pattern, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize); + +#ifdef WIN32 + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\"); + strcat(szDir, Pattern); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + Len = sprintf(Buffer, "No Files\r"); + } + else + { + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + } + +#else + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + +#endif + + // Build Message + + SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + + +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + Buffer[len] = 0; + + if (strcmp(Buffer, "OK\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Propose any waiting files + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "TR AddMessage_", 14) == 0) + { + BIDRec * BID; + char *ptr1, *ptr2, *context; + + // TR AddMessage_1145_G8BPQ 727 1202 440 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + ptr1 = strtok_s(&Buffer[14], " ", &context); // MID + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + BID = LookupBID(ptr1); + + if (BID) + { + BBSputs(conn, "Rejected - Duplicate BID\r"); + return; + } + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "TR RequestSync_", 15) == 0) + { + char *ptr1, *ptr2, *context; + + // TR RequestSync_G8BPQ_14 224 417 0 True + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + ptr1 = strtok_s(&Buffer[15], " ", &context); // MID + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "TR Delivered_", 13) == 0) + { + char *ptr1, *ptr2, *context; + + // TR RequestSync_G8BPQ_14 224 417 0 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + ptr1 = strtok_s(&Buffer[13], " ", &context); // MID + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "TR Remove_", 10) == 0) + { + char *ptr1, *ptr2, *context; + + // TR RequestSync_G8BPQ_14 224 417 0 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + ptr1 = strtok_s(&Buffer[10], " ", &context); // MID + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + BBSputs(conn, "OK\r"); + return; + } + + if (strcmp(Buffer, "BYE\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "BBS\r", 4) == 0) + { + // Out of Sync + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags &= ~SYNCMODE; + return; + } + + // Data + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + if (conn->TempMsg->length >= conn->SyncCompressedLen) + { + // Complete - decompress it + + conn->BBSFlags |= FBBCompressed; + Decode(conn, 1); + + BBSputs(conn, "OK\r"); + return; + } + return; +} + + + +BOOL ProcessReqFile(struct MsgInfo * Msg) +{ + char FN[128]; + char * Buffer; + int Len = 0; + char * ptr; + struct stat STAT; + char MsgFile[MAX_PATH]; + FILE * hFile; + int FileSize; + char * MsgBytes; + + // Parse title - gives file and return address + + // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + + strcpy(FN, Msg->title); + + ptr = strchr(FN, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(FN, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null + + // Build Message + + if (FN == NULL) + { + Len = sprintf(Buffer, "Missing Filename\r"); + } + else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Len = sprintf(Buffer,"Invalid filename %s\r", FN); + } + else + { + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + if (FileSize > MaxTXSize) + FileSize = MaxTXSize; // Truncate to max size + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + Len = sprintf(Buffer, "%s", MsgBytes); + free(MsgBytes); + } + } + else + Len = sprintf(Buffer, "File %s not found\r", FN); + } + + SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +BOOL CheckforMessagetoServer(struct MsgInfo * Msg) +{ + if (_stricmp(Msg->to, "REQDIR") == 0) + return ProcessReqDir(Msg); + + if (_stricmp(Msg->to, "REQFIL") == 0) + return ProcessReqFile(Msg); + + return FALSE; +} + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + char * Via; + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, BBSName); + Via = strlop(To, '@'); + + if (Via) + strcpy(Msg->via, Via); + + strcpy(Msg->to, To); + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +int ReformatSyncMessage(CIRCUIT * conn) +{ + // Message has been decompressed - reformat to look lime a WLE message + + char * MsgBit; + char *ptr1, *ptr2; + int linelen; + char FullFrom[80]; + char FullTo[80]; + char BID[80]; + time_t Date; + char Mon[80]; + char Subject[80]; + int i = 0; + char * Boundary; + char * Input; + char * via = NULL; + char * NewMsg = conn->MailBuffer; + char * SaveMsg = NewMsg; + char DateString[80]; + struct tm * tm; + char Type[16] = "Private"; + char * part[100]; + int partLen[100]; + + // Message has an XML header which I don't think we need, then the message + /* + Date: Mon, 25 Oct 2021 10:22:00 -0000 + From: GM8BPQ + Subject: Test + To: 2E1BGT + Message-ID: ALYJQJRXVQAO + X-Source: GM8BPQ + X-Relay: G8BPQ + MIME-Version: 1.0 + MIME-Version: 1.0 + Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" + + --boundaryBSoxlw== + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Hello Hello + + --boundaryBSoxlw==-- + */ + + // I think the best way is to reformat as if from Winlink Express, then pass + //through the normal B2 code. + +// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); + + if (conn->SyncMsgLen == 0) + return 0; + + MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; + conn->TempMsg->length -= conn->SyncXMLLen; + + ptr1 = MsgBit; + +Loop: + + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, &ptr1[6], linelen - 6); + FullFrom[linelen - 6] = 0; + } + + if (_memicmp(ptr1, "To:", 3) == 0) + { + memcpy(FullTo, &ptr1[4], linelen - 4); + FullTo[linelen - 4] = 0; + } + + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + memcpy(Subject, &ptr1[9], linelen - 9); + Subject[linelen - 9] = 0; + } + + else if (_memicmp(ptr1, "Message-ID", 10) == 0) + { + memcpy(BID, &ptr1[12], linelen - 12); + BID[linelen - 12] = 0; + } + + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Mon, 25 Oct 2021 10:22:00 -0000 + + sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", + &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + for (i = 0; i < 12; i++) + { + if (strcmp(Mon, month[i]) == 0) + break; + } + + rtime.tm_mon = i; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Unpack Body - seems to be multipart even if only one + + Input = MsgBit; + + Boundary = initMultipartUnpack(&Input); + + i = 0; + + if (Boundary) + { + // input should be start of part + + // Find End of part - ie -- Boundary + CRLF or -- + + char * ptr, * saveptr; + char * Msgptr; + size_t BLen = strlen(Boundary); + size_t Partlen; + + saveptr = Msgptr = ptr = Input; + + while(ptr) // Just in case we run off end + { + if (*ptr == '-' && *(ptr+1) == '-') + { + if (memcmp(&ptr[2], Boundary, BLen) == 0) + { + // Found Boundary + + Partlen = ptr - Msgptr; + + ptr += (BLen + 2); // End of Boundary + + if (*ptr == '-') // Terminating Boundary + Input = NULL; + else + Input = ptr + 2; + + // Part Starts with header (content-type, etc), but skip for now + + part[i] = strstr(Msgptr, "\r\n\r\n"); // Over separator + if (part[i]) + { + part[i] += 4; + partLen[i] = Partlen - (part[i] - Msgptr) - 2; + } + Msgptr = ptr = Input; + i++; + } + } + else + ptr++; + } + } + + // Build the message + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + NewMsg += sprintf(NewMsg, + "MID: %s\r\n" + "Date: %s\r\n" + "Type: %s\r\n" + "From: %s\r\n", + BID, DateString, Type, FullFrom); + +// if (ToCalls) +// { +// int i; + +// for (i = 0; i < Calls; i++) +// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); + +// } +// else + { + NewMsg += sprintf(NewMsg, "To: %s\r\n", + FullTo); + } +// if (WebMail->CC && WebMail->CC[0]) +// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); + + NewMsg += sprintf(NewMsg, + "Subject: %s\r\n" + "Mbo: %s\r\n", + Subject, BBSName); + + // Write the Body: line and any File Lines + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); + +/* + i == 1; + + while (part[i]) + { + Msg->B2Flags |= Attachments; + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", + partn[i], partName[i]); + } +*/ + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header + + // Now add parts + + memmove(NewMsg, part[0], partLen[0]); + NewMsg += partLen[0]; + + conn->TempMsg->length = NewMsg - SaveMsg; + conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); + conn->TempMsg->datecreated = Date; + strcpy(conn->TempMsg->bid, BID); + + if (strlen(Subject) > 60) + Subject[60] = 0; + + strcpy(conn->TempMsg->title, Subject); + + return TRUE; +} + + + + + + diff --git a/BPQChat.rc b/BPQChat.rc new file mode 100644 index 0000000..ec9c161 --- /dev/null +++ b/BPQChat.rc @@ -0,0 +1,416 @@ +// Microsoft Visual C++ generated resource script. +// +#include "chatrc.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#define CHAT +#define IDC_STATIC -1 +#include "..\CommonSource\Versions.h" + + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROPPAGE_LARGE DIALOG 0, 0, 235, 156 +STYLE DS_SETFONT | WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Property Page" +FONT 8, "MS Sans Serif" +BEGIN +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PROPPAGE_LARGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (United Kingdom) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +BPQMAILCHAT DIALOG 120, 50, 294, 165 +STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "G8BPQ Chat Server 1.0.0.10 Beta July 2009" +CLASS "BPQMailChat" +FONT 8, "FixedSys" +BEGIN + LTEXT " User Callsign Stream Conf Queue RTT",101,3,4,184,10 + LTEXT "UTC",IDC_STATIC,197,5,15,10 + LTEXT "Local",IDC_STATIC,241,5,21,10 + LTEXT "",IDC_UTC,215,5,25,10 + LTEXT "",IDC_LOCAL,269,5,25,10 + LISTBOX 100,2,16,190,130,WS_VSCROLL + LTEXT "Chat Nodes",IDC_STATIC,197,25,40,10 + LTEXT "",IDC_NODES,243,25,20,10 + LTEXT "Chat Users",IDC_STATIC,197,35,40,10 + LTEXT "0",IDC_CHATSEM,274,126,20,10 + LTEXT "Chat Links",IDC_STATIC,197,45,40,10 + LTEXT "",IDC_LINKS,243,45,20,10 + LTEXT "Chat SEM Clashes",IDC_STATIC,197,126,68,10 + LTEXT "",IDC_USERS,243,35,20,10 + LTEXT "Con SEM Clashes",IDC_STATIC,197,135,68,10 + LTEXT "0",IDC_CONSEM,274,135,20,10 +END + +CONSOLEWINDOW DIALOG 17, 25, 400, 301 +STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Chat Console" +MENU CONSOLEMENU +CLASS "CONSOLEWINDOW" +FONT 8, "FixedSys" +BEGIN + EDITTEXT 118,24,228,348,15,ES_AUTOHSCROLL | ES_NOHIDESEL +END + +BPQMONWINDOW DIALOG 17, 25, 400, 300 +STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Chat Monitor" +MENU MENU_2 +CLASS "BPQMONWINDOW" +FONT 8, "FixedSys" +BEGIN + LISTBOX 121,6,25,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL +END + +BPQDEBUGWINDOW DIALOG 17, 25, 400, 300 +STYLE DS_SETFONT | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Chat Debug Window" +MENU MENU_3 +CLASS "BPQDEBUGWINDOW" +FONT 8, "FixedSys" +BEGIN + LISTBOX 122,5,156,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL +END + +CHAT_CONFIG DIALOGEX 0, 0, 371, 296 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Configuration" +FONT 8, "System", 0, 0, 0x0 +BEGIN + LTEXT "Chat Appl Number",IDC_STATIC,91,85,61,8 + EDITTEXT ID_CHATAPPL,159,83,29,14 + LTEXT "Nodes to link to",IDC_STATIC,159,106,53,8 + EDITTEXT ID_CHATNODES,29,119,313,60,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL + DEFPUSHBUTTON "Save",SAVENODES,160,183,50,14,BS_CENTER | BS_VCENTER + LTEXT "The Application Number defines which BPQ32 Application gives access to the Chat Server. Note this is the APPLNumber (1-32) not an Application Mask, as uses in many BPQ32 programs.",IDC_STATIC,10,10,353,18 + LTEXT "The Nodes to link to box defines which other Chat Nodes should be connected to, or from which connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. Note these must be directly connectable - ie in your NODES table.",IDC_STATIC,10,30,355,25 + LTEXT "The Callsign of the Chat Node is not defined here - It is obtained from the BPQ32 APPLCALL parameter corresponding to the Chat Appl Number.",IDC_STATIC,10,60,360,25 + LTEXT "Streams",IDC_STATIC,199,85,34,8 + EDITTEXT ID_STREAMS,236,83,29,14 + LTEXT "Chat Welcome Message",IDC_STATIC,142,202,82,8 + EDITTEXT IDM_CHATUSERMSG,15,214,340,54,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + DEFPUSHBUTTON "Save Welcome Message",SAVEWELCOME,140,274,91,14,BS_CENTER | BS_VCENTER +END + +IDD_USERADDED_BOX DIALOG 176, 132, 129, 68 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Ok",0,47,48,36,17,BS_CENTER | BS_VCENTER + LTEXT "Label0",5050,5,10,117,32 +END + +WELCOMEMSG DIALOG 26, 5, 381, 266 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Normal User Welcome Message",IDC_STATIC,5,7,130,8 + EDITTEXT IDM_USERMSG,5,20,340,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "Chat Welcome Message",IDC_STATIC,5,67,130,8 + EDITTEXT IDM_CHATUSERMSG,5,80,340,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "New User Welcome Message",IDC_STATIC,5,127,130,8 + EDITTEXT IDM_NEWUSERMSG,5,140,340,45,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "Expert User Welcome Message",IDC_STATIC,5,187,130,8 + EDITTEXT IDM_EXPERTUSERMSG,5,200,340,25,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_MSGSAVE,166,250,50,14,BS_CENTER | BS_VCENTER + LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages",IDC_STATIC,5,228,369,8 + LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user",IDC_STATIC,5,238,365,10 +END + +IDD_CHATCOLCONFIG DIALOG 0, 0, 224, 120 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Chat Colour Configuration" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Save",IDOK,50,95,50,14 + PUSHBUTTON "Close",IDCANCEL,120,95,50,14 + COMBOBOX IDC_CHATCALLS,10,5,100,60,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CHATCOLOURS,115,5,100,60,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP +END + +IDD_UPDATECHATMAP DIALOG 0, 0, 274, 146 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Update Chat Map" +FONT 8, "System" +BEGIN + LTEXT "Click Help for full information about the Chat Network Map",IDC_STATIC,5,10,195,15 + DEFPUSHBUTTON "? Help",IDC_MAPHELP,220,10,33,11,BS_CENTER | BS_VCENTER + LTEXT "Position",IDC_STATIC,5,30,35,10 + EDITTEXT IDC_MAPPOSITION,5,45,145,15,ES_AUTOHSCROLL + LTEXT "Popup Box Text. ",IDC_STATIC,5,65,70,9 + EDITTEXT IDC_POPUPTEXT,5,80,200,35,ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL + LTEXT "Popup Mode",IDC_STATIC,215,80,49,10 + CONTROL "Hover",IDC_HOVER,"Button",BS_AUTORADIOBUTTON,219,92,45,10 + CONTROL "Click",IDC_CLICK,"Button",BS_AUTORADIOBUTTON,219,104,45,10 + DEFPUSHBUTTON "Send to Map System",IDSENDTOMAP,5,125,90,14 + DEFPUSHBUTTON "Save",IDOK,100,125,35,14 + PUSHBUTTON "Cancel",IDCANCEL,140,125,40,14 +END + +IDD_HRHELP DIALOG 0, 0, 415, 182 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hierarchical Forwarding Help" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,182,158,50,14 + EDITTEXT IDC_HRTEXT,4,4,405,150,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + "BPQMAILCHAT", DIALOG + BEGIN + BOTTOMMARGIN, 155 + END + + CHAT_CONFIG, DIALOG + BEGIN + END + + "IDD_USERADDED_BOX", DIALOG + BEGIN + BOTTOMMARGIN, 65 + END + + IDD_HRHELP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 408 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_BPQMailChat MENU +BEGIN + POPUP "Actions" + BEGIN + POPUP "Logging Options" + BEGIN + MENUITEM "Log Chat Traffic", IDM_LOGCHAT + END + POPUP "Disconnect User" + BEGIN + MENUITEM ".", IDM_DISCONNECT + END + MENUITEM "Update Chat Map Info", ID_ACTIONS_UPDATECHATMAPINFO + MENUITEM "Edit Chat Console Colours", IDM_EDITCHATCOLOURS + END + MENUITEM "&Configuration", IDM_CONFIG + POPUP "Windows" + BEGIN + MENUITEM "Chat Console (F3)", IDM_CHATCONSOLE + MENUITEM "Monitor (F4)", IDM_MONITOR + MENUITEM "Debug Chat (F5)", IDM_DEBUG + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + MENUITEM "Online Documentation", ID_HELP_ONLINEHELP + END +END + +CONSOLEMENU MENU +BEGIN + POPUP "Options" + BEGIN + MENUITEM "Enable Bells", BPQBELLS + MENUITEM "Flash insstead of Beep on Bell", BPQFLASHONBELL + MENUITEM "Strip Linefeeds", BPQStripLF + MENUITEM "Wrap Input ", IDM_WRAPTEXT + MENUITEM "Beep if Input too long", IDM_WARNINPUT + MENUITEM "Flash on Chat User Connect", IDM_Flash + MENUITEM "Close Window on exit", IDM_CLOSEWINDOW + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Output Window", BPQCOPYOUT + MENUITEM "Clear Output Window", BPQCLEAROUT + MENUITEM "Edit Chat Console Colours", IDM_EDITCHATCOLOURS + END +END + +MENU_2 MENU +BEGIN + POPUP "Monitor" + BEGIN + MENUITEM "Monitor CHAT", MONCHAT + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYOUT + MENUITEM "Clear Monitor Window", BPQCLEAROUT + END +END + +MENU_3 MENU +BEGIN + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYOUT + MENUITEM "Clear Monitor Window", BPQCLEAROUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_TELNETSERVER ACCELERATORS +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "chatrc.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#define CHAT\r\n" + "#define IDC_STATIC -1\r\n" + "#include ""..\\CommonSource\\Versions.h""\r\n" + "\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""..\\StdVer.inc""\r\n" + "\0" +END + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""..\\StdVer.inc""\r\n" + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +CHAT_CONFIG AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_APP_TITLE "BPQMailChat" + IDC_BPQMailChat "BPQMailChat" +END + +#endif // English (United Kingdom) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "..\StdVer.inc" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/BPQChat.vcproj b/BPQChat.vcproj new file mode 100644 index 0000000..0baae76 --- /dev/null +++ b/BPQChat.vcproj @@ -0,0 +1,386 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user b/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40182c4 --- /dev/null +++ b/BPQChat.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQChat.vcproj.SKIGACER.johnw.user b/BPQChat.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..b5b0536 --- /dev/null +++ b/BPQChat.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQINP3.c b/BPQINP3.c new file mode 100644 index 0000000..2d5374b --- /dev/null +++ b/BPQINP3.c @@ -0,0 +1,1375 @@ +/* +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 +*/ + +// +// INP3 Suport Code for BPQ32 Switch +// + +// All code runs from the BPQ32 Received or Timer Routines under Semaphore. + + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "CHeaders.h" + +#include "time.h" +#include "stdio.h" +#include +//#include "vmm.h" + +static VOID SendNetFrame(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Frame) +{ + // INP3 should only ever send over an active link, so just queue the message + + if (Route->NEIGHBOUR_LINK) + C_Q_ADD(&Route->NEIGHBOUR_LINK->TX_Q, Frame); + else + ReleaseBuffer(Frame); +} + + +typedef struct _RTTMSG +{ + UCHAR ID[7]; + UCHAR TXTIME[11]; + UCHAR SMOOTHEDRTT[11]; + UCHAR LASTRTT[11]; + UCHAR POINTER[11]; + UCHAR ALIAS[7]; + UCHAR VERSION[12]; + UCHAR SWVERSION[9]; + UCHAR FLAGS[10]; + UCHAR PADDING[147]; + +} RTTMSG; + +extern int COUNTNODES(); + +VOID __cdecl Debugprintf(const char * format, ...); + +VOID SendINP3RIF(struct ROUTE * Route, UCHAR * Call, UCHAR * Alias, int Hops, int RTT); +VOID SendOurRIF(struct ROUTE * Route); +VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int hops, int rtt); +VOID UpdateRoute(struct DEST_LIST * Dest, struct DEST_ROUTE_ENTRY * ROUTEPTR, int hops, int rtt); +VOID KillRoute(struct DEST_ROUTE_ENTRY * ROUTEPTR); +VOID AddHere(struct DEST_ROUTE_ENTRY * ROUTEPTR,struct ROUTE * Route , int hops, int rtt); +VOID SendRIPToNeighbour(struct ROUTE * Route); +VOID DecayNETROMRoutes(struct ROUTE * Route); +VOID DeleteINP3Routes(struct ROUTE * Route); +BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries); + +//#define NOINP3 + +struct _RTTMSG RTTMsg = {""}; + +//struct ROUTE DummyRoute = {"","",""}; + +int RIPTimerCount = 0; // 1 sec to 10 sec counter +int PosTimerCount = 0; // 1 sec to 5 Mins counter + +// Timer Runs every 10 Secs + +extern int MAXRTT; // 90 secs +extern int MaxHops; + +extern int RTTInterval; // 4 Minutes +int RTTRetries = 2; +int RTTTimeout = 6; // 1 Min (Horizon is 1 min) + +VOID InitialiseRTT() +{ + UCHAR temp[sizeof(RTTMsg.FLAGS) + 4]; + + memset(&RTTMsg, ' ', sizeof(struct _RTTMSG)); + memcpy(RTTMsg.ID, "L3RTT: ", 7); + memcpy(RTTMsg.VERSION, "LEVEL3_V2.1 ", 12); + memcpy(RTTMsg.SWVERSION, "BPQ32001 ", 9); + _snprintf(temp, sizeof(temp), "$M%d $N ", MAXRTT); // trailing spaces extend to ensure padding if the length of characters for MAXRTT changes. + memcpy(RTTMsg.FLAGS, temp, 10); // But still limit the actual characters copied. + memcpy(RTTMsg.ALIAS, &MYALIASTEXT, 6); + RTTMsg.ALIAS[6] = ' '; +} + +VOID TellINP3LinkGone(struct ROUTE * Route) +{ + struct DEST_LIST * Dest = DESTS; + char call[11]=""; + + ConvFromAX25(Route->NEIGHBOUR_CALL, call); + Debugprintf("BPQ32 L2 Link to Neighbour %s lost", call); + + if (Route->NEIGHBOUR_LINK) + Debugprintf("BPQ32 Neighbour_Link not cleared"); + + + if (Route->INP3Node == 0) + DecayNETROMRoutes(Route); + else + DeleteINP3Routes(Route); +} + +VOID DeleteINP3Routes(struct ROUTE * Route) +{ + int i; + struct DEST_LIST * Dest = DESTS; + + // Delete any NETROM Dest entries via this Route + + Route->SRTT = 0; + Route->RTT = 0; + Route->BCTimer = 0; + Route->Status = 0; + Route->Timeout = 0; + + Dest--; + + // Delete any Dest entries via this Route + + for (i=0; i < MAXDESTS; i++) + { + Dest++; + + if (Dest->DEST_CALL[0] == 0) + continue; // Spare Entry + + if (Dest->NRROUTE[0].ROUT_OBSCOUNT >= 128) // Not if locked + continue; + + if (Dest->ROUTE[0].ROUT_NEIGHBOUR == Route) + { + // We are deleting the best route, so need to tell other nodes + // If this is the only one, we need to keep the entry with at 60000 rtt so + // we can send it. Remove when all gone + + // How do we indicate is is dead - Maybe the 60000 is enough! + if (Dest->ROUTE[1].ROUT_NEIGHBOUR == 0) + { + // Only entry + Dest->ROUTE[0].SRTT = 60000; + Dest->ROUTE[0].Hops = 255; + + continue; + } + + Dest->ROUTE[1].LastRTT = Dest->ROUTE[0].SRTT; // So next scan will check if rtt has increaced + // enough to need a RIF + + memcpy(&Dest->ROUTE[0], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY)); + memcpy(&Dest->ROUTE[1], &Dest->ROUTE[2], sizeof(struct DEST_ROUTE_ENTRY)); + memset(&Dest->ROUTE[2], 0, sizeof(struct DEST_ROUTE_ENTRY)); + + continue; + } + + // If we aren't removing the best, we don't need to tell anyone. + + if (Dest->ROUTE[1].ROUT_NEIGHBOUR == Route) + { + memcpy(&Dest->ROUTE[1], &Dest->ROUTE[2], sizeof(struct DEST_ROUTE_ENTRY)); + memset(&Dest->ROUTE[2], 0, sizeof(struct DEST_ROUTE_ENTRY)); + + continue; + } + + if (Dest->ROUTE[2].ROUT_NEIGHBOUR == Route) + { + memset(&Dest->ROUTE[2], 0, sizeof(struct DEST_ROUTE_ENTRY)); + continue; + } + } +} + +VOID DecayNETROMRoutes(struct ROUTE * Route) +{ + int i; + struct DEST_LIST * Dest = DESTS; + + Dest--; + + // Decay any NETROM Dest entries via this Route. If OBS reaches zero, remove + + // OBSINIT is probably too many retries. Try decrementing by 2. + + for (i=0; i < MAXDESTS; i++) + { + Dest++; + + if (Dest->DEST_CALL[0] == 0) + continue; // Spare Entry + + if (Dest->NRROUTE[0].ROUT_NEIGHBOUR == Route) + { + if (Dest->NRROUTE[0].ROUT_OBSCOUNT && Dest->NRROUTE[0].ROUT_OBSCOUNT < 128) // Not if locked + { + Dest->NRROUTE[0].ROUT_OBSCOUNT--; + if (Dest->NRROUTE[0].ROUT_OBSCOUNT) + Dest->NRROUTE[0].ROUT_OBSCOUNT--; + + } + if (Dest->NRROUTE[0].ROUT_OBSCOUNT == 0) + { + // Route expired + + if (Dest->NRROUTE[1].ROUT_NEIGHBOUR == 0) // No more Netrom Routes + { + if (Dest->ROUTE[0].ROUT_NEIGHBOUR == 0) // Any INP3 ROutes? + { + // No More Routes - ZAP Dest + + REMOVENODE(Dest); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + continue; + } + else + { + // Still have an INP3 Route - just zap this entry + + memset(&Dest->NRROUTE[0], 0, sizeof(struct NR_DEST_ROUTE_ENTRY)); + continue; + + } + } + + memcpy(&Dest->NRROUTE[0], &Dest->NRROUTE[1], sizeof(struct NR_DEST_ROUTE_ENTRY)); + memcpy(&Dest->NRROUTE[1], &Dest->NRROUTE[2], sizeof(struct NR_DEST_ROUTE_ENTRY)); + memset(&Dest->NRROUTE[2], 0, sizeof(struct NR_DEST_ROUTE_ENTRY)); + + continue; + } + } + + if (Dest->NRROUTE[1].ROUT_NEIGHBOUR == Route) + { + Dest->NRROUTE[1].ROUT_OBSCOUNT--; + + if (Dest->NRROUTE[1].ROUT_OBSCOUNT == 0) + { + memcpy(&Dest->NRROUTE[1], &Dest->NRROUTE[2], sizeof(struct NR_DEST_ROUTE_ENTRY)); + memset(&Dest->NRROUTE[2], 0, sizeof(struct NR_DEST_ROUTE_ENTRY)); + + continue; + } + } + + if (Dest->NRROUTE[2].ROUT_NEIGHBOUR == Route) + { + Dest->NRROUTE[2].ROUT_OBSCOUNT--; + + if (Dest->NRROUTE[2].ROUT_OBSCOUNT == 0) + { + memset(&Dest->NRROUTE[2], 0, sizeof(struct NR_DEST_ROUTE_ENTRY)); + continue; + } + } + } +} + + +VOID TellINP3LinkSetupFailed(struct ROUTE * Route) +{ + // Attempt to activate Neighbour failed + +// char call[11]=""; + +// ConvFromAX25(Route->NEIGHBOUR_CALL, call); +// Debugprintf("BPQ32 L2 Link to Neighbour %s setup failed", call); + + + if (Route->INP3Node == 0) + DecayNETROMRoutes(Route); + else + DeleteINP3Routes(Route); +} + +VOID ProcessRTTReply(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff) +{ + int RTT; + unsigned int OrigTime; + + if ((Route->Status & GotRTTResponse) == 0) + { + // Link is just starting + + Route->Status |= GotRTTResponse; + + if (Route->Status & GotRTTRequest) + { + Route->Status |= SentOurRIF; + SendOurRIF(Route); + SendRIPToNeighbour(Route); + } + } + + Route->Timeout = 0; // Got Response + + sscanf(&Buff->L4DATA[6], "%d", &OrigTime); + RTT = GetTickCount() - OrigTime; + + if (RTT > 60000) + return; // Ignore if more than 60 secs + + Route->RTT = RTT; + + if (Route->SRTT == 0) + Route->SRTT = RTT; + else + Route->SRTT = ((Route->SRTT * 80)/100) + ((RTT * 20)/100); +} + +VOID ProcessINP3RIF(struct ROUTE * Route, UCHAR * ptr1, int msglen, int Port) +{ + unsigned char axcall[7]; + int hops; + unsigned short rtt; + int len; + int opcode; + char alias[6]; + UINT Stamp, HH, MM; + + +#ifdef NOINP3 + + return; + +#endif + + if (Route->INP3Node == 0) + return; // We don't want to use INP3 + + // Update Timestamp on Route + + time((time_t *)&Stamp); + + Stamp = Stamp % 86400; // Secs into day + HH = Stamp / 3600; + + Stamp -= HH * 3600; + MM = Stamp / 60; + + Route->NEIGHBOUR_TIME = 256 * HH + MM; + + while (msglen > 0) + { + memset(alias, ' ', 6); + memcpy(axcall, ptr1, 7); + + if (axcall[0] < 0x60 || (axcall[0] & 1)) // Not valid ax25 callsign + return; // Corrupt RIF + + ptr1+=7; + + hops = *ptr1++; + rtt = (*ptr1++ << 8); + rtt += *ptr1++; + + msglen -= 10; + + while (*ptr1 && msglen > 0) + { + len = *ptr1; + opcode = *(ptr1+1); + + if (len < 2 || len > msglen) + return; // Duff RIF + + if (opcode == 0) + { + if (len > 1 && len < 9) + memcpy(alias, ptr1+2, len-2); + else + { + Debugprintf("Corrupt INP3 Message"); + return; + } + } + ptr1+=len; + msglen -=len; + } + + ptr1++; + msglen--; // EOP + + UpdateNode(Route, axcall, alias, hops, rtt); + } + + return; +} + +VOID KillRoute(struct DEST_ROUTE_ENTRY * ROUTEPTR) +{ +} + + +VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int hops, int rtt) +{ + struct DEST_LIST * Dest; + struct DEST_ROUTE_ENTRY * ROUTEPTR; + int i; + char call[11]=""; + + if (hops > MaxHops && hops < 255) + { +// ConvFromAX25(axcall, call); +// Debugprintf("Node %s Hops %d RTT %d Ignored - Hop Count too high", call, hops, rtt); + return; + } + + if (rtt > MAXRTT && rtt < 60000) + { +// ConvFromAX25(axcall, call); +// Debugprintf("Node %s Hops %d RTT %d Ignored - rtt too high", call, hops, rtt); + return; + } + + if (FindDestination(axcall, &Dest)) + goto Found; + + if (Dest == NULL) + return; // Tsble Full + + if (rtt >= 60000) + return; // No Point addind a new dead route + + memset(Dest, 0, sizeof(struct DEST_LIST)); + + memcpy(Dest->DEST_CALL, axcall, 7); + memcpy(Dest->DEST_ALIAS, alias, 6); + +// Set up First Route + + Dest->ROUTE[0].Hops = hops; + Dest->ROUTE[0].SRTT = rtt; + Dest->ROUTE[0].LastRTT = 0; + + Dest->INP3FLAGS = NewNode; + + Dest->ROUTE[0].ROUT_NEIGHBOUR = Route; + + NUMBEROFNODES++; + + ConvFromAX25(Dest->DEST_CALL, call); + Debugprintf("Adding Node %s Hops %d RTT %d", call, hops, rtt); + + return; + +Found: + + if (Dest->DEST_STATE & 0x80) // Application Entry + return; + + // Update ALIAS + + if (alias[0] > ' ') + memcpy(Dest->DEST_ALIAS, alias, 6); + + // See if we are known to it, it not add + + ROUTEPTR = &Dest->ROUTE[0]; + + if (rtt >= 60000) + { + i=rtt+1; + } + + if (ROUTEPTR->ROUT_NEIGHBOUR == Route) + { + UpdateRoute(Dest, ROUTEPTR, hops, rtt); + return; + } + + ROUTEPTR = &Dest->ROUTE[1]; + + if (ROUTEPTR->ROUT_NEIGHBOUR == Route) + { + UpdateRoute(Dest, ROUTEPTR, hops, rtt); + return; + } + + ROUTEPTR = &Dest->ROUTE[2]; + + if (ROUTEPTR->ROUT_NEIGHBOUR == Route) + { + UpdateRoute(Dest, ROUTEPTR, hops, rtt); + return; + } + + // Not in list. If any spare, add. + // If full, see if this is better + + if (rtt >= 60000) + return; // No Point addind a new dead route + + ROUTEPTR = &Dest->ROUTE[0]; + + for (i = 1; i < 4; i++) + { + if (ROUTEPTR->ROUT_NEIGHBOUR == NULL) + { + // Add here + + Dest->ROUTE[0].Hops = hops; + Dest->ROUTE[0].SRTT = rtt; + Dest->ROUTE[0].ROUT_NEIGHBOUR = Route; + + return; + } + ROUTEPTR++; + } + + // Full, see if this is better + + // Note that wont replace any netrom routes with INP3 ones unless we add pseudo rtt values to netrom entries + + if (Dest->ROUTE[0].SRTT > rtt) + { + // We are better. Move others down and add on front + + memcpy(&Dest->ROUTE[2], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY)); + memcpy(&Dest->ROUTE[1], &Dest->ROUTE[0], sizeof(struct DEST_ROUTE_ENTRY)); + + AddHere(&Dest->ROUTE[0], Route, hops, rtt); + return; + } + + if (Dest->ROUTE[1].SRTT > rtt) + { + // We are better. Move 2nd down and add + + memcpy(&Dest->ROUTE[2], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY)); + + AddHere(&Dest->ROUTE[1], Route, hops, rtt); + return; + } + + if (Dest->ROUTE[2].SRTT > rtt) + { + // We are better. Add here + + AddHere(&Dest->ROUTE[2], Route, hops, rtt); + return; + } + + // Worse than any - ignoee + +} + +VOID AddHere(struct DEST_ROUTE_ENTRY * ROUTEPTR,struct ROUTE * Route , int hops, int rtt) +{ + ROUTEPTR->Hops = hops; + ROUTEPTR->SRTT = rtt; + ROUTEPTR->LastRTT = 0; + ROUTEPTR->RTT = 0; + ROUTEPTR->ROUT_NEIGHBOUR = Route; + + return; +} + + +/* LEA EDI,DEST_CALL[EBX] + MOV ECX,7 + REP MOVSB + + MOV ECX,6 ; ADD ALIAS + MOV ESI,OFFSET32 TEMPFIELD + REP MOVSB + + POP ESI +; +; GET _NEIGHBOURS FOR THIS DESTINATION +; + CALL CONVTOAX25 + JNZ SHORT BADROUTE +; + CALL GETVALUE + MOV _SAVEPORT,AL ; SET PORT FOR _FINDNEIGHBOUR + + CALL GETVALUE + MOV _ROUTEQUAL,AL +; + MOV ESI,OFFSET32 AX25CALL + + PUSH EBX ; SAVE DEST + CALL _FINDNEIGHBOUR + MOV EAX,EBX ; ROUTE TO AX + POP EBX + + JZ SHORT NOTBADROUTE + + JMP SHORT BADROUTE + +NOTBADROUTE: +; +; UPDATE ROUTE LIST FOR THIS DEST +; + MOV ROUT1_NEIGHBOUR[EBX],EAX + MOV AL,_ROUTEQUAL + MOV ROUT1_QUALITY[EBX],AL + MOV ROUT1_OBSCOUNT[EBX],255 ; LOCKED +; + POP EDI + POP EBX + + INC _NUMBEROFNODES + + JMP SENDOK +*/ + + +VOID SortRoutes(struct DEST_LIST * Dest) +{ + struct DEST_ROUTE_ENTRY Temp; + + // May now be out of order + + if (Dest->ROUTE[1].ROUT_NEIGHBOUR == 0) + return; // Only One, so cant be out of order + + if (Dest->ROUTE[2].ROUT_NEIGHBOUR == 0) + { + // Only 2 + + if (Dest->ROUTE[0].SRTT <= Dest->ROUTE[1].SRTT) + return; + + // Swap one and two + + memcpy(&Temp, &Dest->ROUTE[0], sizeof(struct DEST_ROUTE_ENTRY)); + memcpy(&Dest->ROUTE[0], &Dest->ROUTE[1], sizeof(struct DEST_ROUTE_ENTRY)); + memcpy(&Dest->ROUTE[1], &Temp, sizeof(struct DEST_ROUTE_ENTRY)); + + return; + } + + // Have 3 Entries +} + + + +VOID UpdateRoute(struct DEST_LIST * Dest, struct DEST_ROUTE_ENTRY * ROUTEPTR, int hops, int rtt) +{ + if (ROUTEPTR->Hops == 0) + { + // This is not a INP3 Route - Convert it + + ROUTEPTR->Hops = hops; + ROUTEPTR->SRTT = rtt; + + SortRoutes(Dest); + return; + } + + if (rtt == 60000) + { + ROUTEPTR->SRTT = rtt; + ROUTEPTR->Hops = hops; + + SortRoutes(Dest); + return; + + } + + ROUTEPTR->SRTT = rtt; + ROUTEPTR->Hops = hops; + + SortRoutes(Dest); + return; +} + +VOID ProcessRTTMsg(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff, int Len, int Port) +{ + // See if a reply to our message, or a new request + + if (memcmp(Buff->L3SRCE, MYCALL,7) == 0) + { + ProcessRTTReply(Route, Buff); + ReleaseBuffer(Buff); + } + else + { + int OtherRTT; + int Dummy; + + if (Route->INP3Node == 0) + { + ReleaseBuffer(Buff); + return; // We don't want to use INP3 + } + + // Extract other end's SRTT + + sscanf(&Buff->L4DATA[6], "%d %d", &Dummy, &OtherRTT); + Route->NeighbourSRTT = OtherRTT * 10; // We store in mS + + // Echo Back to sender + + SendNetFrame(Route, Buff); + + if ((Route->Status & GotRTTRequest) == 0) + { + // Link is just starting + + Route->Status |= GotRTTRequest; + + if (Route->Status & GotRTTResponse) + { + Route->Status |= SentOurRIF; + SendOurRIF(Route); + SendRIPToNeighbour(Route); + + } + else + { + // We have not yet seen a response (and maybe haven't sent one + + Route->BCTimer = 0; // So send one + } + } + } +} + +VOID SendRTTMsg(struct ROUTE * Route) +{ + struct _L3MESSAGEBUFFER * Msg; + char Stamp[50]; + + Msg = GetBuff(); + if (Msg == 0) + return; + + Msg->Port = Route->NEIGHBOUR_PORT; + Msg->L3PID = NRPID; + + memcpy(Msg->L3DEST, L3RTT, 7); + memcpy(Msg->L3SRCE, MYCALL, 7); + Msg->L3TTL = 2; + Msg->L4ID = 0; + Msg->L4INDEX = 0; + Msg->L4RXNO = 0; + Msg->L4TXNO = 0; + Msg->L4FLAGS = L4INFO; + + + sprintf(Stamp, "%10d %10d %10d %10d ", GetTickCount(), Route->SRTT/10, Route->RTT/10, 0); + memcpy(RTTMsg.TXTIME, Stamp, 44); + + memcpy(Msg->L4DATA, &RTTMsg, 236); + + Msg->LENGTH = 256 + 1 + 7; + + Route->Timeout = RTTTimeout; + + SendNetFrame(Route, Msg); +} + +VOID SendKeepAlive(struct ROUTE * Route) +{ + struct _L3MESSAGEBUFFER * Msg = GetBuff(); + + if (Msg == 0) + return; + + Msg->L3PID = NRPID; + + memcpy(Msg->L3DEST, L3KEEP, 7); + memcpy(Msg->L3SRCE, MYCALL, 7); + Msg->L3TTL = 1; + Msg->L4ID = 0; + Msg->L4INDEX = 0; + Msg->L4RXNO = 0; + Msg->L4TXNO = 0; + Msg->L4FLAGS = L4INFO; + +// Msg->L3MSG.L4DATA[0] = 'K'; + + Msg->LENGTH = 20 + MSGHDDRLEN + 1; + + SendNetFrame(Route, Msg); +} + +int BuildRIF(UCHAR * RIF, UCHAR * Call, UCHAR * Alias, int Hops, int RTT) +{ + int AliasLen; + int RIFLen; + UCHAR AliasCopy[10] = ""; + UCHAR * ptr; + + + if (RTT > 60000) RTT = 60000; // Dont send more than 60000 + + memcpy(&RIF[0], Call, 7); + RIF[7] = Hops; + RIF[8] = RTT >> 8; + RIF[9] = RTT & 0xff; + + if (Alias) + { + // Need to null-terminate Alias + + memcpy(AliasCopy, Alias, 6); + ptr = strchr(AliasCopy, ' '); + + if (ptr) + *ptr = 0; + + AliasLen = (int)strlen(AliasCopy); + + RIF[10] = AliasLen+2; + RIF[11] = 0; + memcpy(&RIF[12], Alias, AliasLen); + RIF[12+AliasLen] = 0; + + RIFLen = 13 + AliasLen; + return RIFLen; + } + RIF[10] = 0; + + return (11); +} + + +VOID SendOurRIF(struct ROUTE * Route) +{ + struct _L3MESSAGEBUFFER * Msg; + int RIFLen; + int totLen = 1; + int App; + APPLCALLS * APPL; + + Msg = GetBuff(); + if (Msg == 0) + return; + + Msg->L3SRCE[0] = 0xff; + + // send a RIF for our Node and all our APPLCalls + + RIFLen = BuildRIF(&Msg->L3SRCE[totLen], MYCALL, MYALIASTEXT, 1, 0); + totLen += RIFLen; + + for (App = 0; App < NumberofAppls; App++) + { + APPL=&APPLCALLTABLE[App]; + + if (APPL->APPLQUAL > 0) + { + RIFLen = BuildRIF(&Msg->L3SRCE[totLen], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, 0); + totLen += RIFLen; + } + } + + Msg->L3PID = NRPID; + Msg->LENGTH = totLen + 1 + MSGHDDRLEN; + + SendNetFrame(Route, Msg); +} + +int SendRIPTimer() +{ + int count, nodes; + struct ROUTE * Route = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int INP3Delay; + + for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + if (Route->NoKeepAlive) // Keepalive Disabled + { + Route++; + continue; + } + + if (Route->NEIGHBOUR_LINK == 0 || Route->NEIGHBOUR_LINK->LINKPORT == 0) + { + if (Route->NEIGHBOUR_QUAL == 0) + { + Route++; + continue; // Qual zero is a locked out route + } + + // Dont Activate if link has no nodes unless INP3 + + if (Route->INP3Node == 0) + { + nodes = COUNTNODES(Route); + + if (nodes == 0) + { + Route++; + continue; + } + } + + // Delay more if Locked - they could be retrying for a long time + + if ((Route->NEIGHBOUR_FLAG & 1)) // LOCKED ROUTE + INP3Delay = 1200; + else + INP3Delay = 600; + + if (Route->LastConnectAttempt && + (REALTIMETICKS - Route->LastConnectAttempt) < INP3Delay) + { + Route++; + continue; // No room for link + } + + // Try to activate link + + L2SETUPCROSSLINKEX(Route, 2); // Only try SABM twice + + Route->LastConnectAttempt = REALTIMETICKS; + + if (Route->NEIGHBOUR_LINK == 0) + { + Route++; + continue; // No room for link + } + } + + if (Route->NEIGHBOUR_LINK->L2STATE != 5) // Not up yet + { + Route++; + continue; + } + + if (Route->NEIGHBOUR_LINK->KILLTIMER > ((L4LIMIT - 60) * 3)) // IDLETIME - 1 Minute + { + SendKeepAlive(Route); + Route->NEIGHBOUR_LINK->KILLTIMER = 0; // Keep Open + } + +#ifdef NOINP3 + + Route++; + continue; + +#endif + if (Route->INP3Node) + { + if (Route->Timeout) + { + // Waiting for response + + Route->Timeout--; + + if (Route->Timeout) + { + Route++; + continue; // Wait + } + // No response Try again + + Route->Retries--; + + if (Route->Retries) + { + // More Left + + SendRTTMsg(Route); + } + else + { + // No Response - Kill all Nodes via this link + + if (Route->Status) + { + char Call [11] = ""; + + ConvFromAX25(Route->NEIGHBOUR_CALL, Call); + Debugprintf("BPQ32 INP Neighbour %s Lost", Call); + + Route->Status = 0; // Down + } + + Route->BCTimer=5; // Wait a while before retrying + } + } + + if (Route->BCTimer) + { + Route->BCTimer--; + } + else + { + Route->BCTimer = RTTInterval; + Route->Retries = RTTRetries; + SendRTTMsg(Route); + } + } + } + + Route++; + } + + return (0); +} + +// Create an Empty RIF + +struct _L3MESSAGEBUFFER * CreateRIFHeader(struct ROUTE * Route) +{ + struct _L3MESSAGEBUFFER * Msg = GetBuff(); + UCHAR AliasCopy[10] = ""; + + Msg->LENGTH = 1; + Msg->L3SRCE[0] = 0xff; + + Msg->L3PID = NRPID; + + return Msg; + +} + +VOID SendRIF(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Msg) +{ + Msg->LENGTH += MSGHDDRLEN + 1; // PID + + SendNetFrame(Route, Msg); +} + +VOID SendRIPToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct DEST_ROUTE_ENTRY * Entry) +{ + struct ROUTE * Routes = NEIGHBOURS; + struct _L3MESSAGEBUFFER * Msg; + int count, MaxRoutes = MAXNEIGHBOURS; + + for (count=0; countINP3Node) && + (Routes->Status) && + (Routes != Entry->ROUT_NEIGHBOUR)) // Dont send to originator of route + { + Msg = Routes->Msg; + + if (Msg == NULL) + Msg = Routes->Msg = CreateRIFHeader(Routes); + + Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], + axcall, alias, Entry->Hops + 1, Entry->SRTT + Entry->ROUT_NEIGHBOUR->SRTT/10); + + if (Msg->LENGTH > 250 - 15) +// if (Msg->LENGTH > Routes->NBOUR_PACLEN - 11) + { + SendRIF(Routes, Msg); + Routes->Msg = NULL; + } + } + Routes+=1; + } +} + +VOID SendRIPToNeighbour(struct ROUTE * Route) +{ + int i; + struct DEST_LIST * Dest = DESTS; + struct DEST_ROUTE_ENTRY * Entry; + struct _L3MESSAGEBUFFER * Msg; + + Dest--; + + // Send all entries not via this Neighbour - used when link starts + + for (i=0; i < MAXDESTS; i++) + { + Dest++; + + Entry = &Dest->ROUTE[0]; + + if (Entry->ROUT_NEIGHBOUR && Entry->Hops && Route != Entry->ROUT_NEIGHBOUR) + { + // Best Route not via this neighbour - send + + Msg = Route->Msg; + + if (Msg == NULL) + Msg = Route->Msg = CreateRIFHeader(Route); + + Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], + Dest->DEST_CALL, Dest->DEST_ALIAS, + Entry->Hops + 1, Entry->SRTT + Entry->ROUT_NEIGHBOUR->SRTT/10); + + if (Msg->LENGTH > 250 - 15) + { + SendRIF(Route, Msg); + Route->Msg = NULL; + } + } + } + if (Route->Msg) + { + SendRIF(Route, Route->Msg); + Route->Msg = NULL; + } +} + +VOID FlushRIFs() +{ + struct ROUTE * Routes = NEIGHBOURS; + int count, MaxRoutes = MAXNEIGHBOURS; + + for (count=0; countMsg) + { + SendRIF(Routes, Routes->Msg); + Routes->Msg = NULL; + } + Routes+=1; + } +} + +VOID SendNegativeInfo() +{ + int i, Preload; + struct DEST_LIST * Dest = DESTS; + struct DEST_ROUTE_ENTRY * Entry; + + Dest--; + + // Send RIF for any Dests that have got worse + + // ?? Should we send to one Neighbour at a time, or do all in parallel ?? + + // The spec says send Negative info as soon as possible so I'll try building them in parallel + // That will mean building several packets in parallel + + + for (i=0; i < MAXDESTS; i++) + { + Dest++; + + Entry = &Dest->ROUTE[0]; + + if (Entry->SRTT > Entry->LastRTT) + { + if (Entry->LastRTT) // if zero haven't yet reported +ve info + { + if (Entry->LastRTT == 1) // if 1, probably new, so send alias + SendRIPToOtherNeighbours(Dest->DEST_CALL, Dest->DEST_ALIAS, Entry); + else + SendRIPToOtherNeighbours(Dest->DEST_CALL, 0, Entry); + + Preload = Entry->SRTT /10; + if (Entry->SRTT < 60000) + Entry->LastRTT = Entry->SRTT + Preload; //10% Negative Preload + } + } + + if (Entry->SRTT >= 60000) + { + // It is dead, and we have reported it if necessary, so remove if no NETROM Routes + + if (Dest->NRROUTE[0].ROUT_NEIGHBOUR == 0) // No more Netrom Routes + { + char call[11]=""; + ConvFromAX25(Dest->DEST_CALL, call); + Debugprintf("Deleting Node %s", call); + REMOVENODE(Dest); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + } + else + { + // Have a NETROM route, so zap the INP3 one + + memset(Entry, 0, sizeof(struct DEST_ROUTE_ENTRY)); + } + } + } +} + +VOID SendPositiveInfo() +{ + int i; + struct DEST_LIST * Dest = DESTS; + struct DEST_ROUTE_ENTRY * Entry; + + Dest--; + + // Send RIF for any Dests that have got significantly better or are newly discovered + + for (i=0; i < MAXDESTS; i++) + { + Dest++; + + Entry = &Dest->ROUTE[0]; + + if (( (Entry->SRTT) && (Entry->LastRTT == 0) )|| // if zero haven't yet reported +ve info + ((((Entry->SRTT * 125) /100) < Entry->LastRTT) && // Better by 25% + ((Entry->LastRTT - Entry->SRTT) > 10))) // and 100ms + { + SendRIPToOtherNeighbours(Dest->DEST_CALL, 0, Entry); + Dest->ROUTE[0].LastRTT = (Dest->ROUTE[0].SRTT * 11) / 10; //10% Negative Preload + } + } +} + +VOID SendNewInfo() +{ + int i; + unsigned int NewRTT; + struct DEST_LIST * Dest = DESTS; + struct DEST_ROUTE_ENTRY * Entry; + + Dest--; + + // Send RIF for any Dests that have just been added + + for (i=0; i < MAXDESTS; i++) + { + Dest++; + + if (Dest->INP3FLAGS & NewNode) + { + Dest->INP3FLAGS &= ~NewNode; + + Entry = &Dest->ROUTE[0]; + + SendRIPToOtherNeighbours(Dest->DEST_CALL, Dest->DEST_ALIAS, Entry); + + NewRTT = (Entry->SRTT * 11) / 10; + Entry->LastRTT = NewRTT; //10% Negative Preload + } + } +} + + +VOID INP3TIMER() +{ + if (RTTMsg.ID[0] == 0) + InitialiseRTT(); + + // Called once per second + +#ifdef NOINP3 + + if (RIPTimerCount == 0) + { + RIPTimerCount = 10; + SendRIPTimer(); + } + else + RIPTimerCount--; + + return; + +#endif + + SendNegativeInfo(); // Urgent + + if (RIPTimerCount == 0) + { + RIPTimerCount = 10; + SendNewInfo(); // Not quite so urgent + SendRIPTimer(); + } + else + RIPTimerCount--; + + if (PosTimerCount == 0) + { + PosTimerCount = 300; // 5 mins + SendPositiveInfo(); + } + else + PosTimerCount--; + + FlushRIFs(); + +} + + +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen) +{ + char call[10]; + int calllen; + int hops; + unsigned short rtt; + unsigned int len; + unsigned int opcode; + char alias[10] = ""; + UCHAR IP[6]; + int i; + + ptr2+=sprintf(ptr2, " INP3 RIF:\r Alias Call Hops RTT\r"); + + while (msglen > 0) + { + calllen = ConvFromAX25(ptr1, call); + call[calllen] = 0; + + // Validate the call + + for (i = 0; i < calllen; i++) + { + if (!isupper(call[i]) && !isdigit(call[i]) && call[i] != '-') + { + ptr2+=sprintf(ptr2, " Corrupt RIF\r"); + return ptr2; + } + } + + ptr1+=7; + + hops = *ptr1++; + rtt = (*ptr1++ << 8); + rtt += *ptr1++; + + IP[0] = 0; + strcpy(alias, " "); + + msglen -= 10; + + while (*ptr1 && msglen > 0) + { + len = *ptr1; + opcode = *(ptr1+1); + + if (len < 2 || len > msglen) + { + ptr2+=sprintf(ptr2, " Corrupt RIF\r"); + return ptr2; + } + if (opcode == 0 && len < 9) + { + memcpy(&alias[6 - (len - 2)], ptr1+2, len-2); // Right Justiify + } + else + if (opcode == 1 && len < 8) + { + memcpy(IP, ptr1+2, len-2); + } + + ptr1+=len; + msglen -=len; + } + + if (IP[0]) + ptr2+=sprintf(ptr2, " %s:%s %d %4.2d %d.%d.%d.%d\r", alias, call, hops, rtt, IP[0], IP[1], IP[2], IP[3]); + else + ptr2+=sprintf(ptr2, " %s:%s %d %4.2d\r", alias, call, hops, rtt); + + ptr1++; + msglen--; // EOP + } + + return ptr2; +} + diff --git a/BPQMail.aps b/BPQMail.aps new file mode 100644 index 0000000000000000000000000000000000000000..2e2b5feee10127aa668fd7c5087333925df9f17a GIT binary patch literal 68844 zcmd_T3AkNFl`mc=xtD~15DlXs?=pyt;hy16j5*W2_n33fIoxw@Zi2C|Aqf!8WCjQ* znjk77A~UG5MG)s_W4CP!YCD0?0f)BR3CC{j!FFh;c0la>f4{Y=cGcczpMZR?|KI!G z+j_& zpS`--s8*(HE2^t2)fKDT&6(+Pb#D}e|MNG!J%c=m`m|)%tA9|+JLkH z*=)AUFVbx=mjFv<75S@zqGl#c1OGP6xM>19VP^1FF;!C+C~5Q=G+w3*Ku%slkHY&P z{;K#}fImJH`1_&!GNvCi4!!`tZ@2J=<{NVcev?-TCAbih7m;t>;-QB~#})I%-2@+I zcA1p`~$zn*J$f;07-17^^$H7ta#Hb9E?W*xM1B{ZzhJXWQdZ~DxK%pzd-?Yjo)W`L*m z2ZH~V!FwbAw;g;gMIEuOc7SF*;G6JPM%`{kZG~wrge2?b&t_*dzlV|Ip~!C^o@ z2dVW@{4an%Ji^z(d9CCg(tI5>M}y|E40*Um(?2bvxIYBAjJaFNI?pUNe`~IR9`@~f zg-aI6b+|bt%6AH7y)@F4u{6JeWQReYj|aRR?qQVPr&m3*#j|G9cs*=R59lYMtBsH= zNv&o272wSGcPaV)3VewBV+!tJGaTg6h9n)d+Fp4K3!nc=zamcgC%$vtLW^d)Ae4mlIP;m28a1Wc+L7v(& zpIm;cg=c@j^G4)2ia+-EGVDzQWp1LkS7BRegDQBNrC4~<`hLbd9qsg{%$b>Z>M3w& zpzWLea`hHLOR*h&VIy>$-lKPC_DCB)3RY}&=1_Avq~1fp@UV}#ayT2r(sY+%|K&I% z<0PMheew3Jo0pe;N^HXh*o!T&80oSfn7LnMKLFY~A+rfK%J) zKinw5F=8j`nq$`nseQ_z7I6op+$5>y9TL`S3HUkN8^+PMI2!8{*6T957kEzNKj!L8 zPV!CVF70U%#p^%H&VekJOHbDndPM%H-m~ao55`@{b>%k$B`o=0D0nRJZqt{s-3ZC2s0DWgL#{Uo(A08LO|%=hA0 zxi%s%rFQu`+qG0`&e`!r<`}ak^9)-*)FQRttMm=zw+T6^cM_JIy1PQ^U@iWoKSbY5 z%iW9HrO*{UDyCfnX0ds_f=b)>C^I%-I*QbOXirsFNBh*sS zb!~&bZtt?PUvD=rg11TOD5KZ4*6}95H;dN8(r!g7+k*bb-nR+q;`&|)OS(RDOhVu5 zF^W6ivn|?no+f>C$H~rK5( zS}l5#)YLYVg3tlUQ$tiTC1FKFfDtX_DVvC zzXY7O0InKWxwitMqkz)ujKy>7ZX-4Eg0Dono#06eyxBZQa=ZevvII^Ntxw;Um@j*i zQm`k`vsS6E!W*SqD%Qq!A?ph*gm%|syjL4nMILL=N9ZTem#^X9(DG_qr$xCObkrwH zGcLAKYj_hZk6P|Hmy1P$H7FB%HnqZb$kAqtlzB>c(V|hK1DLmQcEDb`4b<*Y|7`)% zKHD7J!!9fBLZkjn8~n~PhnOGD_x_BtAJ>D=X%U|^8y4sFA@Dj4ynY_=I?-fgtoT>- z={?|;?c29+H+069!?vqjJJ#2AzPziMFi&S6lV!asDC;CRPD|^c?)ms~?n29bn)#1F zHdoFC=6Z8p4`QxG1kFY4!8~*mLyJ z_Ve`2o6Pf2f4`G*g}#vMhb@t5hsfdVRR#Pw5;|j@GJ;VYGWPR7UMK)p%adJ8i{b$E6L5lL#r0%#U z8>^`6u#Ix$<>;j|e2tARhx|?GU;^+Wh$e9C~KU3FkbjgS-0uys?b-RfL|> zW{zp|Mh(thomrGoUMaIu@ddUc=kqW!YuoX0*!_*T#`-G{m=}sI{X3Lzk7k2^)vj-c zhcOiKZJ>nok9_aujE_3hwxEgYi?yvkV6H}Ot~6f>_-3ptCi)CM@S-U6%6xrCqTQ<8Ar#1W5u zhaQjDVaS`&S)r}+VlcW&;_ zIjOcSYHJ(*NBwTaZ_bdI1IG~^2OV!;mUNq``pPW|PX-%2~ zfnQ`^C-~hlzr_(N-4Q}}qor%X!)QAK`(LFyQRrT8>HNx2oLFhOVz*&O+OZ%tJOUV?AvD z^r_l$a-qN1>hKmzj|c@?%_g)J)>c(4g|2`abK8>7>?RAA>0War`qx6hSU0<6uA;us zv}l)pi&`em%QgP`9MpaI?8Z3!Ypl~f4gS$mtlfsAFzJY&mr?v#J@AVyelP{Ea-Ilz z7Maro&zY~TU~7Fj-!g2Mp@j2L&RKJ^^-UR>uA{H1zg&lo_=|lu{!s4Up`~*^e?W>{ z9OoBcCHF-50rXqQH|aH#Bl(P&51JQ%?lq$4J@Jf$&*x%%7zd=ce1&K~E}8QC4C2R! znCpA;`*w`qg=jxq*AE}%^C;G;4l%Fo$>(QS&-CR>pIs=|>!h4NfuDScxgm`oe5a<# zWEcYIxT8^=_w#kR-ZhV4tx@r-nT+CndT4TuXnaJ;1(w_V(tI$O>Hww++ zX*5DOuQdxkCkAr+C|SC^vyeBPK3&eMt(-@u$zbKY8a!E+BhqNBoNr0U$=T`)@kjqB zUAyyIZvkJouFpD2rb$R_jFsoJR_3G9d$D(iWxL(lh#FG*@!Z9sb%oBy zaahs;j2}~umHc-Aa(~!-7PG}e%$+H6AB^(fZa!~L2yAKDj$zK$hYVTHNyK$FLcWJ1 z8`eAO>S7{{xX@4F*s&ooqEWXWgm<$TwQyI8jGE^<@jE_k^JL!(eR$_}>iDY?--JoN z+b>WtNtihkbbNyADdBL-SL4v(nRK@Ru44~929yNI>%px5o`$FhmOEL z&Kb^VmAj)q!8|{6w4twiO*BK=j$W>Q0AnEZDA=A|uVFOo*Z8hTZeY9 zQ7U4>+2VHih+JQ$C#7~zy($-dWSi2+5O;KGoH5Q@`x>QU&8k#d=iGzF6++!@=Hp~K zi(Lw=MmouF7kD$S#u1crPOepPhT&$ZTKQZrUy9Uj)l}DSR8O2E zQAeDg`q-WJPx_=zdQxy+vmP4_dh>UFsMDCvu#^Y+Um4}0`CowsJ-`8Ske4be%Ej{q3=B_vPj`?7(|4)^;84CUYxrA4$c% zRd9bB!)Y6S447v`^28(hu8fbNUo7cKqxmn%+ywvjQ1j7P9uD_%!F@P}bA5=ht;1}e zW0XN-3Y^30Y9#f-9g2SKn&YRNt-3h)x#oO8X?eB9u^q5=Oo0#QD+7o;=&C7e{z{2F zFgLcYIL7rG#zL7>xFf>(i7Em28fZJ@jJ|tCL0eU~O!3w4Fs9A^$o}NknB1B-`;+b} z(H%V- zt@CeyzuP!1&kY_br)UeLOY4WwNhM?fNWw z+cf4^+yyuR&c4lEf*jaJxC2V{>0@FV*{4-gd1z@_`_!OXsCD>7sLlezJyhJwp)K)6 zLQ5;IIr!Mr8o=15Y1ui~b|Z$y5ZwGb-P(Jr7;hJpt0*A(TBTcG`gl- z`9{A$<6&FeAq7UVEsJ|TIfkH=h-*L5phEo09tT3xa<)Fek5{+De>AJ=9cf=_a& z`DALF`83+7>n}d<6Clwf{_2P&o`AfPeQ43aVO;F9l3f@VbNlbOWA`xg*~~3XWa;;HIcSc&e`XF?0Darar?5`D8W0F21$+qgODs{^~$CXWL z)}>r49MqoD_URrnTA>}HX%|;i`|7wQSFY%BxcHyeqsuEElkJD5c4k~%IIA6Rd+DY2 zIjS$@Y`WX!qZXQeDR;lPT{GGr;`*l_#=fAjc#bR_7w8+u%bK*o&Ogy8cAPVP4{Dna z&MZj(nrDuOZWmxDH~Yne@&YXnuX?ek%?uBd&-=txt)=^{y=NDRV)qDdp>;`^0}iY zAJx}-$jG^}s}EQISEByMGp_zM`g&&6AG=$CtE=5;NsK@180S~)_w)})*{?F^pp~3! z-g;;lIamEXh_o(F!g&n+8o%P9yHlJF-*5Tc3qDSVPKIBC&*_m2T;1)>=SP;$+fw+P zo>__&H&>q>Xi9q(_3Y#r$Y^=qnUa1r();#MMtL^le;O-dzo7NnfV$I>&6tO5`ed}uT%U}RgznJap0#Hw)b~d8d>*M^;7i7*7KeSKilF@yw z!%w5lK9zoOC;HEks8gQH+GF#L$_4AgF;Mp$x^nq3hVv3_Q3I0FrJI62)*+ql=W$~? zqc1$ww-NhoFG5*a3mSvsD8^BfBkNhvz%DC?Yg_$hMamfO>;-*k`lR}}Hdcac^cnW0 zlQ%)&=y*)2S__u$coQ?o}0 zx?O=fS`#wCZ{_o#c|T;?(UZ>?V|?0ZNf$>eaB+rOw$BT{ohkg-9`8n50&5Vy;c$Lmh8` zj-MXL*MVhaJSlt*czUMw9rP=%=^hOPt!DTr{^^2$W>5Svf*wkQ59!9S}f zeoXMk^~9G2|37--rvyLH6F)2X<9p)Q2>yhg_!kNO#Gd$_fS@37}#6K!{MupPz z|DoW|>WTlE;0Jr+extL?k?)Du{-JA|yFuk2_OHH-_KzWe)AQH< zG29cc{bQsjUi*ihBi{`w|7iZ&KX~Irdj8r!7zs$nYyUW>Ctmx9p0nN!D*tHy+CR<% zIGw-tkMn!twSQdD6R-VaSx>z7k77@}_79$|PM1&nN4Y0n`v-l1^z_<4sy*>vlJ+^? z6aP)YPxQq9K=9nRm7f1k1z+ol|E=Jk+XH{P?H|i~;>3HoQD|+I!f6Vs8 zYyV)NGo8Qok5xVK+CMJpiP!$|yqo_Osa zYkK0fe_Y%Xul-|f5B!C;f2`|?e}?ogdS+?$b0N-s(?8>F4(WIuKQ8Tw*Zy%?3Z8!A zd6|PSpFR-0`CMa`!$;JJ4-(j?e~D=wfZf}S?|uO=%E7fS-LK&0*^XNO%QPdIqriI+ zA}E?BjgoY0EZvu4bS^@x{5a1&$}Gv8j5WJ&h|kLiqMLPgAoVWHBH|Cz+<`PlA#$lKsdx)p5luu{tUri5#V<8>^!swZB@f|vBf>sav89{4LWn*O$)_=QrxSNFuLFZHsXcpWQV-V?85 z#qOSX9V_YjM@rLOCV*Z%XG zo_Os)*Z0J0|9Ndsy!M|Pdg8VJysjr++t(dE@!G#`?1|U@_4=N8?OzN$r}d93Guppy z?upm_bxTjY_ODw5{8h1WurA}^RXuF}W!SaVdwlkh)=7*n1<9iyI}EsAhxfSg4m6(k z;Y>-NFuuLa?!h=1s~i1hlRc}Jw)4it(-$BD=+;MhX4Bt!tJ(8kQe;`YX9wKq1@&0G~l>a6F*s_IyUCJs0pXzlOV$Y)~mvO1>6_P-6Qc6nLDxm zpgtSd%k_UJAr7yp>7DRi2bPNKm0Y=favWBIE?$FNd3WoRxuyS*GIq3x>F;axFFX*Yr|y;LnzoVzc7V?UgD>X)n7Ttn|(6X!l9 zr$w$XvJQN_JDHy^r|t}MdeKt4a`wxOo~~}WUx_uzdgN+lT+2-9+tlZzO&uDvDaX@b zA5W;eaVu+@?o?`bcG7vSy>@CjyekWLVd#z}?qk!{Lq<@I$<6n+N8vn8LF?1P%4u6@ z_)nVcIZ$lR6W?>dJ%6D+NVh$@vd*3QZm#2XNLz!MS&9z(u{S5=owPMcuAb+(@JzQW zn**M`>`IbzSm(Qhce)*UOzu!9?Z=MHGxuSixod0xZ`hIf<}b|i{x7y8@0(}P^XRAW zKI<%}6tO<#*3X|tg!yY&Uq0MFqqBdmB65{ZcS3VDkUO(E+owHFuCim^Yws~C;T9v_ z*}?Vj-RRehd+MY1`yj+SF2$dVrntT1+==0=np+*`=)s)1D8}Cl)47?Q&)<)uiHJ2?r@>wJ#71@8xT6)CAWXN(}j-rRqdbdY@y>F zT>Gc13LWn}N~8N3#*G0KN!xR-t3C4`1(x_2{$p;;9GKyMtBAwZWUu8c#+Ud$YPRD2 z-O!FUje8Zy| zEY7u$br{ol*PQl;8D zD}(gjn`4ZYwvmr7$1dS>Q!1b9Qu*-yWkzRtcFWl(&+pa3@8(o~djo#)T?;<_o8YA! zY;H+S|DGVd-e(fui{aY!g8^Mw>ag7JmV9mv^6@e}Ah_EC9PbRE-d>1WA4P2PxqCTA zgtrY4i^tNv2d$HIHKB{of$m5D9ssG@gM3eWZ~*z?uJSwOzj_0s?oFfSpZG>+H?A$f zjvha*#btq|kaDfaD9t=bwOe+H>-fodr#`CNemRxk`n-<08_^Qzr^as>)l^QW=b8Ct zIp&LJr}(A6gk5-fWJlae0%szQzdxfI&x50p-nFT_bsfFR6Uu$QS&aSGPVQ6S4{#ni zfPLYVTXzuZ?n*Z+)?LJUW}TG8Cg)E&FL)Fg4d74fbF^oD_K85>w|h1HeLSpF?zGjD zTz^UpqxNk5<*-NUiRyTlrEzu4JgEgf^E1awJ6MGJU59<+r=XtPc(DX~>`1fS;!X{4 z_r!d)k)CBgm<-G_GPlToXUtaQqBC30`Q2AFSbtkV(e0zTF^78{KZSJ$9dr1d1NsZR zpUv$9_Oo{0e5&n&b*F7&ljN#r#<;Vdr-AkMaGn8TJ$q>yc;A8j&urVw!AZv=xLC)x zArJQ>KSSmy-TRTf+{a1$Qsw4}NS-m`yqs%o`nbN*kKJcCql{_u;C}219JgzF*T65> z0)6r9IA^MQE16p_yfH)p6&2w@0r@CUx)O( zUq^lRblY|VXn04BYxiz$;B4Dvu{K=jYQy3F*k$IC-G_(-58Au#oxMtW(`>~Qv|Dyb zj($6p;R!F?odf?cEWP60zoa`_=pd%h@eEJWIxhz9A)Ni_!w$s1$o!}IgYewrnArc$ zYn33?!T4i4q_@i10Qdid5B;?^;t;oj^6?&g7Q**CBF2ZUne&x6H@4{e-?`2z`6z z4l_TnBYl}r_TetslYL0X!k=W;A>9-2F=t#?@2}8XIHceh(OsKA+I$};Zuwm(r%R>a zznK<&mQ&!1EfjorL|SPx*k`VE`|D?xc4ZE<bqdsl*Vy3yZMOI&0BDGacMNJF(Sh_y*_*Fy!88^I1F~>)Dy$jqyFw& zaO%aU4`cmF9_I;<(^7fV10L~S>E!jY)P>LKseGOnjL}?|qE?=W-}8ju87cf0W|jv0 z_RpsRTI%S`R6gege4dHGYlyP=enNkbtYwfdDMGhJDL)%K7M&dMAMy$_t}sm%Sbw&Q=|8+p15jH`>UQH)V|{S zoGc9h)|nlJ;EOD}HEHHjV}Hw8v*O-+YkP%_ZJ$ zj+AxN0&vMu%C+~K!0unM*Gl_FQa{?>9qoL0)vt_b{TXv_`>k+xeR2Wf8B3eg1 zGu|%JzHH(2wg4;gKD~Nz-F3+IwF-FaD4-H(y)8gYybaGx`zP~f3+Ff$#`8P2Es&3X ztB_*}|5vrtoYC{10L}>~A<>jAX2#n4OEAYf8n*1bpbnh! z7r|1w_yW&+@+2Ys7LJQgP77X!4U$hW$j6_9yb&ckEV}pIjo6xp=IZQ3vgVvL)Qt9i zj)!Ye6TH=q9Q2$o)#da1pyc=1d~K7QYtp*W8_*dsOX%#TuGMoksPPq!-5k9gp817q zF=0NB0J8{l1m-gVI>ul0wjD~=k>6ZF=6qkH;XI}7zMJ9Yy$5^_!yG~7B_G;M+QrS7 zjjE@ht0p|ntQguCH*Hbq4`~B)GQ0tI;Cu>Y8IyT$5#@I_w`pg| zwCifcT}{xla2A#I;BFL9&aQvFT<&Gm{!xJp>a9^fHKdQP^v?)C_3*lToNsGaOIs@i zZ7s4NnHIEiz8zcjhT%}2g_%vkxc@}{!$$ER&IAZO`3;=TF4DIyx(;qh2IzjI*{ zZQ0$pV2rPyGo^km2;}=UCMaj1A~=uWm-*`G9aXG0wzYC%Yo*JqPj#4`OZ<~f-cRy*>S>5a~KF;`d;ryioGAbYK;U3G<G5!`#pe8r^jbbO~oIF{z0FR z{ex?#Gx)1uUd~?Pp0xS)f+y%B?t3X0nP*^xT4c_#?Zu5Tx=tI$w$&eVciU`5AAKY4 zHaS1g1LIMY=@W=0EP+3I9&j%1?#lUIPJEp1=D-=A?%?D+C+)i%6g)I_63PHATN8Z7uH!*7p(DVlE}~ z)6`fU<6gwS&Ek8-zFpg##waq3J|Euy5;p>AKkHydt#_uKZ(7<6`^zNz!89S=qW zID>Ptupg=KWT>S>)=2XBkr=*H+IB@J7g=NocegVHeez^K13k(XRY z$91p`6*Lk3{tUm8tT&L3qXingan+tU{oWV9QKQeGIZ&2|__i2-YFjQ5=&p>x3{+ui zlagQ4I&k5&pgAf^_ZdmIHwOExz&;RzeRv6TeI($&Z{O#H?i&GJ`o8)D7ydk!F1wSw z9tUhNk|QUuGlKDV-@d%SR>xokf!zSuNm1G%f!!aY8y49216UY0Xa0+!f2W6}FHHO? zz&l;;+joxeD*#5B`w_c83;*TAwS%)z7CssLp4FeILC9#~q&dp|Cj27-{OsU&F-ZHC z0PenVMn3L0Jrc~fj#Cj$2bPM_hj{#hIaS00)9Qt{AUdl_E?-`tHtLiUdqree>J@vv zM;G_xiEYNP{8lBcyt~6o*Yvl_E(%>`WSY)i`j*=;hv14&oF~Tw-EradH@mk6%k82$ z$rM@-C-2u=GRVozwFls>(z^HZtc)q{B`xD6dN1$E(O9GYdZ`PaUh~G3yC~zOCBNNf zvWsd~#&*uT22i_h*Dq^zR>m(KyH%Q9)-QGbqWf+;5k25Lc{aBfD(;*?%=L|;{E(B17@LVolH zRAU@P>m2zN?NB6Pc>usVMyQP0aZ-8Eu=26)@ z3kt4{X!K3*jHQRkr@GhkXKXP}!}?G!iuH3T=n?bW%iiSfd=J}!laS}&S(nsE6>{ir zgZ(^g?|Q#p6T07l-@2zMM?5ng*y=iYsv4wtTltz!8FGSAeK5Y-*>1Vyr z({xx}8)7@)^8x&6?5Z!Sb?#hqPj}tNTPf*I>WjlzcbvIVj+47^YC*A&vZv0H-1e`v z{Zn(C?k-=RV#|{(-~T^S8R@Ova=9Ja-D6=~t`q*zwJ_><0B^1pu~3Wgw+w&#_8lnR zZW$3L##-v=(^dRx1E*aMCsM@3DcjrZv7=2Hm@Hjw%VPUO079JS*uj4jk)%!sZz7v ze%hiFH*8+Jap$`A11A*6S58;kOD{VCz@3|sZa;}E*QsKyUY;y=LN-g6t}bucv}w!c z_LiO7)~;W@V*R%58@6oTE{w73;EBorhXzkt8JJs`Q;f4OM|;OQV3wj>C<%Ene(|U3 zecSr&B0kiP{dL+7xI=Lz+5pclaGkaZFM1CDE#ixGP1Irsbo=&w2LHz8=lh?= z(*fUkAJqW<_U$_Wf9ka+`yJ5sSpeJ7!tIkTe&yiwzq>Jyq6wb_l1YrzCHyxmhqfw% z{h)k~H({GZi>+14b5pBoKuiY`eVdMJk073Fv zr8dVDS(6>iBBTL8We)G-wFy9Sbor5LuGnhLb!we@6~H_qlR{Q3HD@}d=1PH~ZfljP zIe`IC;G~Fv>dION@BBQHBx+4JTdm?mb*^5Vo*+A3L{>Ahep~MpDs?3ZWN(U04sAd?do*30*P{i zgIJak*m!e#wm4m>H6{SfA`mLjsrG~=%wgvfK;^kcvr=uFA<*EAIRWHG0QVmXpk8Y% zCm@d{6#=wYwVN%lBCvqyuji8kWC#&^fsD^0`;I`4RmfCvB`Xi0VJw&k(M+c-uei-n zYPtw3QCI3WN%)P61ND=0|}P>nMi8hB*8&9h-qrjbPc;l$)(psF!5V{y;H+B>$bU$bdDNelEMNZM+) z@~th~*h*1_7;#FbR!6e3TSeA%g!C^nREmZ zdI8mIE7&yy6NgTs=YK*uS?E(48H>B7{*EkZ2{(8U@dgAW}{edHtMSsfw?)$hmJcp zRci=`0)_;kX0K^fXGu83;0klIJ=_U91mi038CpKIblG8yB?@d;r5~}SlFKkY(kF;Y zwO;L1VP&f2Rb{b+*%2P$WDe!dbp0Yyk6;)Q$nr*WwxI?>z}O=~LRzIx114Fj2hFsh zaM~KN!DAS~l(Ie1X@Vt;TuULdtrfMRLN4?))+)pBVdRLiXct$&(uobv(sPXvo$2EE zc&$t}Sq_wfXiuPa3CeMf6wx-C4HOzJahi$;as`oMm++cr3@f6w8M2yzSt*!eqg<_{ zGZR+8*vtW|RBThNc)v(f7dxF|d2*`SkVXjj^DMkxE3#^HLpTW_`AklCsw>(4awAv* zw;&jkY8yuj26JNu_qQ9*V-NFW%)t41}bK?-KLGg2$nT3AdE8eHHY~MUZ=zq zsl53DW}^aQlVm+w_z-5F0!JeYz{8l52$;6ZvK_%hNMO~KXu~0sQB2r?VRLA;n(bPr zhA|t0jB%<)LE5#6Mss{zVV5cGBcZluj){wLzIr1API6tMm% zdlDJZj;>$J>iOrZ_1}6bx*!UJB4Ev@9#k z&>)U@kf^@p(&o+`TX*gN8GyJ=Ny#L;p)8J>S*ocmn>Xy(vW;X}vl~%4G)k%f!V#7; zdxWyRe)GD@hV5&&Y~BpVZ2daLU1QayT=V#>EV+(@$7HiXeUL1VyWI^mj1dm6!-Lq0 z$5*yjzGv}S2)=j9tncBRd9`gNz;hgtLwFaG?bz62cr`f-UN6c#x_`)zfJLk;$@Tj>#xwPB03a#77pn08# z&{m9O@vU9Si8Ogaa@Yl+t!q`g(-aTNj(a0{PQBP~Dk#@vvu-W@NCC&01a2*Tr>ROA z88Wvh0`Cc4!#I8-X&TMR=2X=t7{L}^3vGz+I5;wDZdVYy`!t7d>UU(!+@av{TD^lI zN<01NAijzs4A@(zYnAE@ycGC$*}>7Qxl2)KqiSyfdV_-Oct)}u*6@V`3ux(b*nJ`} z8sAw~=xE;TRgmUCi<38i7R;L!I@{{3YSFrl4&k(gqy>lr%IGjYk7fZ*>Jf9df?CrU zpVZWij+(bB5RCvn0+Wv6dm~6XH3v6-s@1W{#|F*4N{J@J+6O6MZ$qmswW&4RCdaZk zqAfWCu4#whhrb=stVYGt(Zha+W>cxPU8jM^K8Sn!Bsu2@;;jLgHShAk#+G%M*#Md| z?^Y;VN_nzcUQXwb4*sBdkMgcJCnm({hx=sSYk8m>h+sJa|EB_L?bh7HnjPz}SaaoE zb@Q5wH=>*7Db4#7KXoM|1C=eC)@;~JM3xH^Y^6v@7qGEr(|W>lTzm=O;u->;$7O{M zPT$he7w{>>5bo%Q%wI+D;@Y)ac5dF`k_(RCRYeg}O&W26vw-Gvu z58N0Qkb=$?XF8LFj+wtPMj%yK&L%)m+lL~~qM#O5u3e?Mk3T2nVd)9nd z-=!A=kTV}qK!@`c7$U&(*b*&*JL3dc0oMu$%+lpZSTG+`;3NkQ%*Oy5G9Ql-<`wt} z1!BH}?pY;l*nBcZSWw`n6o^vL=@XWt=Aj5E#VxS74=ZePTE-KAvgXq|^{|d4Ksocj zlnUJxt$PqBBBc;)-wODw!;6IhO&0sTg~kIuuYikctt=Deu=_?RTE%vIwmHoS8h~F= z;Dwz@p~~~NRMtL{ksxor6cOnAm-XEmJV{hAe-{IiuwcFtgK@G7E&sg&QLh+^CKUD$ z3WEqjG;F@A0L*$F(J&50II)C?HzNo@j63;^;555HI83&y)dqp1=9>ys|4BiQdb+7* zr8ZuJmj~K0e6m{@R9BYkGZhE?wgSZz!mJG9gHB2%4!A(Rs}Pu|24?)=g~`nK6h;H= z2(#w<3bfK;cJq%4Xf^9~*gdAq;h2;!7<`bqI;OFl_xwQNGg>p4-2AYMa2F|u%s+cd zDhrT*QAl~J&DvMkzbdTQnr?I8ByiaL8-W_+C?dF6nPYGUVTP>vk;QCZzpc7%!;aeK zOSUND-<1!X?Q!R$>An8U}bjE-M(VxNcEM}HB)FkS&Nf=w%qiZdX9M$NA_k^7$ zM6Z%|$UH{yl$&YJF$gij&i@A`XG@X-jts(CdE5h0LV$+MA3X@}8$iS6zZ3*#!KEBA z`y!Yo9K~h=X>g#@bYmt%$kg0q6Ixzgt+qJEjZv0)iolE=uURwSzRt*d&h**$aia6s zVlHf24S6q^1N6NluS59Kq@YWR9>$G)@{Wci1S1BQ%uoyfX2@`qI}!+}cceG~jTtPe zD+r|-V}%{;04NLqS@Sdnpr*lyE*7BJhNw`0Dg@+lDFzsEI<=CuH^#Z`Vg;cs)NA8a znau;9Gf#J<;E4ZMr&l0?0!$v42UsQmJNg2Sw+Iy8Lda(bpJfs_n{tR4#{N8qC}qvq zC~EQug-ln`E9p&ZoDQfF9LN#k2sOPDHVBweZUkYG#HGeOu0`mWIa;9-)e}lMoX=Dk zf|!j)Q$|J5!nHhx@QQ{UknKzzGi(g*sCsE@?vw|2g zXIK_3c^@@r@*ROev}7or{Km{ug{iqzC<=+aLM%)LL9z)FqAv_82rbchR+vFWFwXNx zmp8cxMuiHnVDbdc70V1YwC6aDK<&iql&ObIK~qnaF?W=hI>Cru#?sG(m54ZdQ*uE- zQxmgc*~*j|C0UG`k&H~UQFcbA2pcn_8BE99F`|{Ls74Q2}aGGGieuZ_br8Q)vISGTaO1j4;!AgkmOH9WMfdXr0bz4IRC# zIbX4o2zdjRGZ$nG7DC!{4YX-QFxx0vxjF~)F;kTmm^0Oo0-biM*r-?nW;%i|v<58D z&O6i1x+TDrDQ_l1JeCGAhAV%LT$35b@bT0tO~iRjX;MPV0C#o_ zS88^!a0TS3AkT>*i;Y#lj0@&mi$TFF)oCXP%mi4TAIGUA2&e*eK^)~oK{O4>WpQLA z%OHf|Dx((9=BUC9LYg|F+luNmn`mrss0MeTF#%Dri1GTW5@knY0JIuGo5jwYv^BvM zfE$nJlCKD66qpH%!2)s>E@rD6qiL@JC1A2u%97W4x#Nhs$HI@LV?R8z8%LkS4=Ca zfG~F)h3SE!h7jkDqm~yNMa2yx)=gZy+A2;{eyqf8Fd1xqm_uYfSaC-Fs5Au1A{<=0 zsvW_}i}w0W9!txGsbQQVhqzdjK3dJ5t4@`w67-;7Us7C<<`fz|v9Jck%n1Q)kgzc(N^m$)oqSP7o>YF=?UM6$YY1;ynN)#}0xDXxG3R~K$r zaYG1nb>T)7H;jN+7j9H>BZzx-;l>mZ~wsffZ#g1q^6kCq8afBts5Tal%3*|A4SQjC75)sgj zAlyZa%oQ+{YDVj?u20mp@xpDEO@bq zMKKX^(kFEH5k%!SrpMwql*Xw%hk^bN39JM^Wg{5#&0)BIJWj!FTgbhJd4muX5V3NZ zgK^25Vyv7XbY+K0tP{zYfc}c9F_U5nk4RGz4IhM~mJ(zUd+H*v&P^V|AJBzmE}9mb z+ETC>@)TV_oGQusqGl7jNEAPWuvHqKOT{AmFv3_YHwU^oy7{G|v?t6A5UYwX8d{u- zkXeMRh=gZ^d5TvSqF@L+1sG@U5Yp_Wg%Q3T$H`l4AjZlORWm z9ioR2L_0`j!{VWyBGhm@gAwDI$mx;pwkQq*v#h2!%u_`Y#@pMf@W|VJ-p;;)~!2ediGV zh;TCqJz_H@g#jv$C`g2I9O)D(x`2R4giiJ+Bp*UhB*M=$=&q{!smX^C6N%8AGYS}f z&jk+L!p_DyELLL*QCtCMi#xc42t#Yh5h3i+gx@%W{VLdogHd>8x!RI_D=Y&B+OnsN zVj~HnoT+%CDQuXVUX|pNM+_yBma)|&0XBMh0$HtYHYCF?Q1p1Xst~d|@hpk{9C({WQPjRDW&f|0rB86A%n0bN6O3Gq8Ko{}Zk4PFp z+j&YrQ!*EO8u}suu58wNoMa9cV~%>al2^<+PcT*O6lqAT3!oBK%_W|qQo|rC{(&O# z83d0k)fi0=@z{THna7K3pz+Fp2liuZ@H8?Pj%H z-n=SF((WK;GJ!pr;yi&(!Mr+2GhO7Fp3)4W*3zi5SV_4qNyM#!T8?4!nj~^2D&&Z{ zK8XyQbrwr0uT2t!T_cNil^Z;PRdm#@BOAi=6R+<{*D7K!5S5Ox{iZG|Y|L6dH_okI zLXtB#caf+|1(&xdeohuEF}L(0nw^GWR8|FZYZnz%tNw{+He_z=O%|{l#!bRe!&S$4 zPRBvX4#VGF9&c5J9*OyqY}I7xxJ%E|z$d`~h^lUj9m;aQp2wDom>byfGm8=MO&;Z$ z!;`9w+tfp5ZyJeqKoxP=yxEgjixEZCAl``ibC37Age0T5%{kJ7C&4-#_eqP3n#Jb6 zyVHm`a-$YQ%A$E|8l|6PLaLIv$J0zyF>ww3M=e>|-0Nu&lT4$jn74TvTY7E_*N`%-F_>;qqOc#Pt=Ssn|x2(emA%#AYNEvLl|dj3ICv zkkBq;54|?*BK8v|aPTs#)3QD@o-Jn0`x0m>Y`lqJjufJp!@cXi4#}I1E$XJlytzMt z=a>KoYn2RB%A{c4pCGc`6dU-sm5QY(4w=795IKc#P*j>>^H&KPJzZi4X|>nI5%Ylr z6=KNVZWKq&UnlS^6Si0}j0Q=@%m)+rN!0sXOe6B9?uRW%>>s3P{a`g$%6nvW)MVLf9{$loT=G-`6Vg{*N< z=3@ywg5Wp{;51*#n~x_5#N2rf1@nmn0hH=OV#s00e6kx~M&v;p(`;$je5xDY#wOY+ zF5Q5^h}aPkTBW60xzZ@f?DUpZ8e1Vv6>z zLwyeM#z#CFyvtb93D5}PdNkrUwE%}euESwt>xFe^&Bb4#XgQele~6Q4WQH~fZYa+K za_@oI=o~EkSL4K0?0sGZuJxHItbJ?10ZjAeYjHa4POPIji|TMxsg2-XSq9Y{WwSbC@H2Gfs>-y*dTltH7v}G959G zcG0D<8#UjGlX?Aw%{7NXKl3M$u!Y*(R?RP(yWwtSP?y4t*A;?GT)7parruR zP{@sz&G+K8?FLpjCt)zVMw#4b#e6?bOv{h4O6D6lqNLeZ%|FJeY{Q9DjhlapQ=zDE z&WdO?)==j#l>8u01O3j~>1Vfi7*>85Cl~ds$f4RUoWWL0tvC!V{~RYn&hG3GCL_vU zI$jPNN&Y2HOS>m6D@8w;#QkfWS_;&e#uNaP4x9au`L{R`3x$1kwmpvxn;*sTwFVj; zT1{t;ay$X~aTtIgH7FaCGQ9FgbegdeGoDKwUG(DMX3D;P6G( z%=1v0?Q!ZTvstbU^e2%*!YLh&JaT#9fGz@*$YK#;@50)6S#A*Vb#55ql1&{e=hz1~ zxC$Gwl>h?7rMPrPx7p zsKau_WtI^L-4-~4tWWSbDW}8a%!wgJ*C{v-F>0U3Ii$Qf$rI@6i=8{&L6^~(bU6w!;W{Xl{%)W{q#dVYjGs+!$+BZu) zs9cll2sA_prm(x%ndk6wM#=VTY?@}SV>qbXg8(QJ;Lx%on2wg(=+M^%jl;+eUBjXf zwo>50gj|V`$80=rnsKa06->*aczPPg!J%~66@5t%jE!Ihaj=XU}WVcYfPkoL6q2I+BnmVz94r{V4-NW$M7&CX6aZy z$5u%sGjmDY>?C6jz~MA-630;&7#s{vVkV|}It94Axj2a{aVIEH1+zAZT7^TKu!cYm znRQ7dIuQ2O;b=F`QJVEhYf@&{Hq9t=$C<=}p$s3ad%-xq70kutO6t?6DrEN~2wLNCB zFL{f{;4~4U>r(M(U0IyP-|CS}AtM-~URkp}K-9_|#xMcTnH?T(4J^QUv(tlh;|ZVz zbGd_J77p%Q6ylr!JK6HN+|Y=5X$oHEH$;z`t5eV|D-0>d%*z}_X%604RRZhzTpn|W z-425Zm&@9Q-!UYPS5d@Qc@pglT;}BdMZqJ4nZ#qv#>ukf2#c#rTt!p8E=e@osBi?I z(4a&W?e#S&B(m#Y@qivFf&JZcY+8PP*k&$;QnsNit|xjWBhGetBz>02?xK z+6+Y(M<3hQ%xy`MQl~NQY7l+%_7psVBs`)ZndHqKDHKtO(KYW(!B4`vNGDe&L*}j| z5ke_kN3M8I!{!Z1q6C`}^TwVOp3kUxlcz#lat1DjiZN#PdJJyWlBr8ITQqO>;2JJW z(U4m;TQYy{5u^{1W%Cw~)N#hkP%(FVB#Men*f2h_OyD9I)oj(gwHJ|^38{c_b5AcS zXAMY)SpB`8&SKyQ+URW+j_%vycsRjxp-sT)i2J%JTzs+$clYhxXkAzo^}^$QM>m06 zxKxrM^UiJ(af_u4!{%KctyZbkl3-mG9`3tSaC5<0cNL!Qdpr#lJr^Y}n)i02;;BpK ze|i$DLC>IU-j|Av^Qf4==%Uh*La)bSG2wdO?}@a_i#IgKEt$Y)&HK~w+?5G@&itjv zb2UNscL@$V9R50i;{XtC%^I&_r^5#wMGN6L8QpM71g2lt)2nceA4sKe(eNrpmXC8b&%HV=n%Le%U;A)+ec;-3x)EF%~Ct0FG`zd}42 z6zf#)s;Cwb8~;p5Q^5qbT&*=_p-fd%G@lJA0xsCAfOFPLRWhFosUVBS`cSY5T>`8Y z%jWYTO&BNV^4=pMGGY*Fq-ws9pmCa0A;!%YLp<6V7cNMnQX;DUrI0{Nn~0KB5nTUr zNQHiZs4JU;U*6zVRDTx|u`r11h;~I7MA*L)5^#w{bSHbnynags^Y=+QF!I%mNc%s8 z1a=h#{J4IuEl_jBvH282?0;&GH|!#76=C$_4X-kbqX5TSwRg5O2q-go-|7ei)LqamiA7lFmz9_+j(U zAsKd|bF(?z^A2`cVjiQ*Xw~wF`4>6zheLnIo8GsI@<`xvUIl7~&lo%-mpx;Mmp&>8 zS1WD4)SNK9bZ}`dyL4!1_23D(_7Uvx@ZTTcV7orNxBevj^!H!lx(qzLhjgC&;+>fK z9JsKIV}q-4{EPR%R&W7YO>Ti*jo(%LT8*2}cpvCAu9vIgO>f)80a^U!{m}e!pSvYG zN#$@w{CK#-Ifa$Kar1XViI8Q*{4pgFZ?*id6u9o4R?f^3`y}Vhqr|dBiLdkJ zHC7sgqlxLCr-kP$w&mOX&7y+KMQf^;W+TL08DPo*BGRLr0S^$k!%T4uQ1P*prM9eU z;pSF8T}qA#{3Ks+&|GfM{L%bwcF~2DKUZYpwywUe6!x0S|LEq0Y~(tpAE%<+9h^TA zYSfIzm6wo`-276J0gD*$5FWGs)OHk<6`=W?!)|LH(_)c=uf+G(fyx4 zn{R$0XcScT`hB~f0))4i2g60Nmd!yNiB<(s4f9l;7G`P(2_mVJvtJb3nY_rF817Q<{AuxZpfd77FfhjEH z5Ba}R^8^cR5{2Mmhj~iY2)CqMw}di6PjcbV>*UQ{KXjwK={-ZQm*4g>p_}ZBy)Ecw zd5tdoxm8{<5b{=>hm=&z*)8(hUHNm{5?n$t#|wC}W@(@Moik@ezw>5Le#h?7J56*Q zYhpb2!RSBSnFG49Qn{~@tYfLLf~2=WUEhtvjZEgX-C%du-3@}mg(OtJ`Nl>(J)dJ}jBTj&fCPonBmcdYoFU04n zBh#JTdgH}Ra(mLls%Ljk(_Qi!UZeEsCE6W$2^%Ekb+qz4a1c8Sydmi_lq|eX?!kl1 zM-DQ7D>NYZ=t1Vg?l)dPCci%}|9fg_qsD;p8qpi1FcQbVllp6T9Hro!f;v0?LIu zcXV@zrKxD;T z#lO#(yAL&Q*RdLqk_5k zZPD+X(V^Pj6x6@i^eyg^7AL~2!0MNQiMWF@uhcPG?^)I{C?>nTe|(=b4tG1!T_^L% zhV4`bEar>ccmrv%#ewQ|cn$M#nbh zLEHDa^hCz6l}~*n{9(M;fxVQ_-hKAfBkdbYiCfRUCht*^&5IN7+Y;~F6Yo0`?>pn~ zvC1{q#F6F|iT76~-mgu(A8mMd{xJq$$mMPL#~I#uKVWzr&I$O$x6FW^ZFm>`bMQqr z!%qyIW_Wk}8HRVmFO|=*4I19-4v#r6U!fQ>yncPe@PhR*!|T(}kuN)(XL#fJ1@fhd zB5p9n{f_blzACPl;|KTT@^HgT!I$G!UtF<^`*86YEBOrag}BNV7iaRzBD_d@g?#31 zrQy}r7a3k`z1r~R>OV8Qlllb)*L3if>9q#49Zc_dDfFfM7E)Y!xa1MnI(|u@Y#p)+ z@eR?CLOe)Af=U2!GHGL;uZ7RdYSN{2UluM#@88MQ+&PQ)x$R!UM+lw;dR2V z=citHCGaf$ysHt<8^iBx=yO^8kLGG0pm-z>h(iN}>nabaT3ML8rI6pViLd za6yerrBgyQF8w&dujc!*t4(K@#jS2nsnpLGk&ybjC2-QH;#u9q@EZ3X-Q;>h0+KVY za}c>6_O-!V&fMtUC94>nMG1@B z#P5ynxO?tiXJJsd+Fq}-JQ^^jgzm!(Pf2Tgg0`Z!qs>cp3;7TOg9GO5C)JEcMJ$wy zm!w;V;wi~?QxDmMHT;scIYk7rS;_5rr}laMXc$ko&5{P^Po7%Sdb2dGSWf~k+2Hq# zZj}J#_8Oan6=FsD4+bphPOIN7Sggg8PZT^IzjQ3eg9t5~y9UPbr(PN%7r1T?<&}95)uS zA%waoG%W04RumaD(#EuyqIea(IrYSZ`T)hXwx^|l$Ia;}P~2jBMmHFD-JY2O1@Y1( zI40ARe2MPD%_g}K19vT#2FFD~dBEXHM0esAl5|f}fCQPnJ@-7}h6Ws6YkR&xBtMyp zE$hxNydJkGXtnjcszX1QqmwpaV!wa$*nHNSvbA5O(avOvuxM*PKBGM;@kNC(u_!SM zMqewEy3{RYKrA;CkDIfSME1tw!BjZz*%r76$aahEM>!+bBpJtm!-N0p?ZOwFsP;@f z(NsSzQ5DgB(f1saXxU|4tW<~PH20=s&^+EI7@HZp+-SF^+^y30b#nm0QYxv zBLcPZ(@yen3#}c>N>lj6qwFj`Im1)VzQbdHfOBw;$S$9*IN$x`hb_+QF4JsF49o?B z(c<%~80W?*qoD zxCRwd?vr?7`S!m!SHN=Mm<%{vq{F8xGxtNwK}%cLfs15lN4rxfc@*3@n^s;S14ZZEpd_=b!On zy|&FLxVs~|C^;d>3RW*F;~ZYEA`5OX zo)JH{z%L<@o!nEx=8js>ZyX)bho9kBex5~u`z{ef;Zt+b9n}?;F}j?(s-gy0P*3bd zY6iU+#9BgD(f5`P@58i_){;IXzO9SC7QKC^pG^pt+F+mV9Kt1dunx=+D87q}s2Hm9 zWMS5WbC5bOsrMsAK#SN69;l6u2Wkt~AD!@z(JMa`h3~?WUYl;GT5nV_J>g+|o6>!K zlFIkLnS@RZ&ZF_e5R3ugNkX-pNvO)7gnAA6o0bw`n)Cl)_ zWQ4|GuZR7?Z{L}_@wabZKmHI)!h>(E_Q?yV^Bf2L~_-$8&m(1Jxl{Ws3@ma~Cf_!IF z(jj_fHXabqgL_Z9Q%U)v@*`YkPFfI8S58S4%80O+`Eo1`HBf>sCQ?$y^D80kwa8ud zjff8&v@Ecg?@S|P$dyxOJ>m7ci3x65# z-@nx13tjj(g8yZR46A%YU3h$n#GHh#uK3|D{Bfwq)<(rop*68*BjPSkSYN+}9D5O0 zCLgx5I;i-KaHrLf_Bhaf9HD2@&O-My_^U`uty<1;9X}2oGd!&_p3^1cI@ZB8PsuK! zW$Qy8551Ck^x;oUY$)>=p{Lb|fD<3LMN=qegG&(dglRqi$V$Y^nZ{AgaaF`VAuX!N zjNXu>%{$Uq-&hLhm%C_nsAPhW?_#_!L$v;>e$O4%Z>~l9Kjr)FHzCbK`%QxefG7Ok zIhKOVbujq7^I;>1r#T4kkMB1PDlL(QIZtBI;;DYuoat|P(&??;3&vQOG;=@F_xheW mze&Cr3iNw2tvwESaNp+QAA~gB`D`CRz5g(zu{Jc|^Zx;Rg{WTu literal 0 HcmV?d00001 diff --git a/BPQMail.c b/BPQMail.c new file mode 100644 index 0000000..73d06ac --- /dev/null +++ b/BPQMail.c @@ -0,0 +1,3633 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// + +// Version 1.0.0.17re + +// Split Messasge, User and BBS Editing from Main Config. +// Add word wrap to Console input and output +// Flash Console on chat user connect +// Fix processing Name response in chat mode +// Fix processing of *RTL from station not defined as a Chat Node +// Fix overlength lines ln List responses +// Housekeeping expires BIDs +// Killing a message removes it from the forwarding counts + +// Version 1.0.0.18 + +// Save User Database when name is entered or updated so it is not lost on a crash +// Fix Protocol Error in Compressed Forwarding when switching direction +// Add Housekeeping results dialog. + +// Version 1.0.0.19 + +// Allow PACLEN in forward scripts. +// Store and forward messages with CRLF as line ends +// Send Disconnect after FQ ( for LinFBB) +// "Last Listed" is saved if MailChat is closed without closing Console +// Maximum acceptable message length can be specified (in Forwarding Config) + +// Version 1.0.0.20 + +// Fix error in saving forwarding config (introduced in .19) +// Limit size of FBB forwarding block. +// Clear old connection (instead of new) if duplicate connect on Chat Node-Node link +// Send FA for Compressed Mail (was sending FB for both Compressed and Uncompressed) + +// Version 1.0.0.21 + +// Fix Connect Script Processing (wasn't waiting for CONNECTED from last step) +// Implement Defer +// Fix MBL-style forwarding +// Fix Add User (Params were not saved) +// Add SC (Send Copy) Command +// Accept call@bbs as well as call @ bbs + +// Version 1.0.0.22 + +// Implement RB RP LN LR LF LN L$ Commands. +// Implement QTH and ZIP Commands. +// Entering an empty Title cancels the message. +// Uses HomeBBS field to set @ field for local users. +// Creates basic WP Database. +// Uses WP to lookup @ field for non-local calls. +// Console "Actions" Menu renamed "Options". +// Excluded flag is actioned. +// Asks user to set HomeBBS if not already set. +// Fix "Shrinking Message" problem, where message got shorter each time it was read Initroduced in .19). +// Flash Server window when anyone connects to chat (If Console Option "Flash on Chat User Connect" set). + +// Version 1.0.0.23 + +// Fix R: line scan bug + +// Version 1.0.0.24 + +// Fix closing console window on 'B'. +// Fix Message Creation time. +// Enable Delete function in WP edit dialog + +// Version 1.0.0.25 + +// Implement K< and K> commands +// Experimental support for B1 and B2 forwarding +// Experimental UI System +// Fix extracting QTH from WP updates + +// Version 1.0.0.26 + +// Add YN etc responses for FBB B1/B2 + +// Version 1.0.0.27 + +// Fix crash if NULL received as start of a packet. +// Add Save WP command +// Make B2 flag BBS-specific. +// Implement B2 Send + +// Version 1.0.0.28 + +// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net +// Flag messages as Held if smtp server rejects from or to addresses +// Fix Kill to (K> Call) +// Edit Message dialog shows latest first +// Add chat debug window to try to track down occasional chat connection problems + +// Version 1.0.0.29 + +// Add loads of try/excspt + +// Version 1.0.0.30 + +// Writes Debug output to LOG_DEBUG_X and Monitor Window + +// Version 1.0.0.32 + +// Allow use of GoogleMail for ISP functions +// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc +// Email Housekeeping Results to SYSOP + +// Version 1.0.0.33 + +// Housekeeping now runs at Maintenance Time. Maintenance Interval removed. +// Allow multiple numbers on R and K commands +// Fix L command with single number +// Log if Forward count is out of step with messages to forward. +// UI Processing improved and F< command implemented + +// Version 1.0.0.34 + +// Semaphore Chat Messages +// Display Semaphore Clashes +// More Program Error Traps +// Kill Messages more than BIDLifetime old + +// Version 1.0.0.35 + +// Test for Mike - Remove B1 check from Parse_SID + +// Version 1.0.0.36 + +// Fix calculation of Housekeeping Time. +// Set dialog box background explicitly. +// Remove tray entry for chat debug window. +// Add date to log file name. +// Add Actions Menu option to disable logging. +// Fix size of main window when it changes between versions. + +// Version 1.0.0.37 + +// Implement Paging. +// Fix L< command (was giving no messages). +// Implement LR LR mmm-nnn LR nnn- (and L nnn-) +// KM should no longer kill SYSOP bulls. +// ISP interfaces allows SMTP Auth to be configured +// SMTP Client would fail to send any more messages if a connection failed + +// Version 1.0.0.38 + +// Don't include killed messages in L commands (except LK!) +// Implement l@ +// Add forwarding timebands +// Allow resizing of main window. +// Add Ver command. + +// Version 1.0.1.1 + +// First Public Beta + +// Fix part line handling in Console +// Maintenance deletes old log files. +// Add option to delete files to the recycle bin. + +// Version 1.0.2.1 + +// Allow all Node SYSOP commands in connect scripts. +// Implement FBB B1 Protocol with Resume +// Make FBB Max Block size settable for each BBS. +// Add extra logging when Chat Sessions refused. +// Fix Crash on invalid housekeeping override. +// Add Hold Messages option. +// Trap CRT Errors +// Sort Actions/Start Forwarding List + +// Version 1.0.2.2 + +// Fill in gaps in BBS Number sequence +// Fix PE if ctext contains } +// Run Houskeeping at startup if previous Housekeeping was missed + +// Version 1.0.2.3 + +// Add configured nodes to /p listing + +// Version 1.0.2.4 + +// Fix RMS (it wanted B2 not B12) +// Send messages if available after rejecting all proposals +// Dont try to send msg back to originator. + +// Version 1.0.2.5 + +// Fix timeband processing when none specified. +// Improved Chat Help display. +// Add helpful responses to /n /q and /t + +// Version 1.0.2.6 + +// Kill Personal WP messages after processing +// Make sure a node doesnt try to "join" or "leave" a node as a user. +// More tracing to try to track down lost topic links. +// Add command recall to Console +// Show users in new topic when changing topic +// Add Send From Clipboard" Action + +// Version 1.0.2.7 + +// Hold messages from the future, or with invalid dates. +// Add KH (kill held) command. +// Send Message to SYSOP when a new user connects. + +// Version 1.0.2.8 + +// Don't reject personal message on Dup BID unless we already have an unforwarded copy. +// Hold Looping messages. +// Warn SYSOP of held messages. + +// Version 1.0.2.9 + +// Close connecton on receipt of *** DONE (MBL style forwarding). +// Improved validation in link_drop (Chat Node) +// Change to welcome prompt and Msg Header for Outpost. +// Fix Connect Script processing for KA Nodes + +// Version 1.0.3.1 + +// Fix incorrect sending of NO - BID. +// Fix problems caused by a user being connected to more than one chat node. +// Show idle time on Chat /u display. +// Rewrite forwarding by HA. +// Add "Bad Words" Test. +// Add reason for holding to SYSOP "Message Held" Message. +// Make topics case-insensitive. +// Allow SR for smtp mail. +// Try to fix some user's "Add User" problem. + + +// Version 1.0.3.2 + +// Fix program error when prcessing - response in FBB forwarding. +// Fix code to flag messages as sent. + + +// Version 1.0.3.3 + +// Attempt to fix message loop on topic_change +// Fix loop if compressed size is greater than 32K when receiving with B1 protocol. +// Fix selection of B1 + +// Version 1.0.3.4 + +// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above) +// Add Basic NNTP Interface +// Fix possible loop in lzhuf encode + +// Version 1.0.3.5 + +// Fix forwarding of Held Messages +// More attempts to fix Chat crashes. +// Limit join/leave problem with mismatched nodes. +// Add Chat Node Monitoring System. +// Change order of elements in nntp addresses (now to.at, was at.to) + +// Version 1.0.3.6 + +// Restart and Exit if too many errors +// Fix forwarding of killed messages. +// Fix Forwarding to PaKet. +// Fix problem if BBS signon contains words from the "Fail" list + +// Version 1.0.3.7 + +// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4 +// Add last message to edit users +// Change Console and Monitor Buffer sizes +// Don't flag msg as 'Y' on read if it was Held or Killed + +// Version 1.0.3.8 + +// Don't connect if all messages for a BBS are held. +// Hold message if From or To are missing. +// Fix parsing of /n and /q commands +// fix possible loop on changing name or qth + +// Version 1.0.3.9 + +// More Chat fixes and monitoring +// Added additional console for chat + +// Version 1.0.3.10 + +// Fix for corruption of CIrcuit-Node chain. + +// Version 1.0.3.11 + +// Fix flow control for SMTP and NNTP + +// Version 1.0.3.12 + +// Fix crash in SendChatStatus if no Chat Links Defined. +// Disable Chat Mode if there is no ApplCall for ChatApplNum, +// Add Edit Message to Manage Messages Dialog +// NNTP needs authentication + + +// Version 1.0.3.13 + +// Fix Chat ApplCall warning when ChatAppl = 0 +// Add NNTP NEWGROUPS Command +// Fix MBL Forwarding (remove extra > prompt after SP) + +// Version 1.0.3.14 + +// Fix topic switch code. +// Send SYSOP messages on POP3 interface if User SYSOP flag is set. +// NNTP only needs Authentication for posting, not reading. + +// Version 1.0.3.15 + +// Fix reset of First to Forward after househeeping + +// Version 1.0.3.16 + +// Fix check of HA for terminating WW +// MBL Mode remove extra > prompts +// Fix program error if WP record has unexpected format +// Connect Script changes for WINMOR +// Fix typo in unconfigured node has connected message + +// Version 1.0.3.17 + +// Fix forwarding of Personals + +// Version 1.0.3.18 + +// Fix detection of misconfigured nodes to work with new nodes. +// Limit connection attempt rate when a chat node is unavailable. +// Fix Program Error on long input lines (> ~250 chars). + +// Version 1.0.3.19 + +// Fix Restart of B2 mode transfers. +// Fix error if other end offers B1 and you are configured for B2 only. + + +// Version 1.0.3.20 + +// Fix Paging in Chat Mode. +// Report Node Versions. + +// Version 1.0.3.21 + +// Check node is not already known when processing OK +// Add option to suppress emailing of housekeeping results + +// Version 1.0.3.22 + +// Correct Version processing when user connects via the network +// Add time controlled forwarding scripts + +// Version 1.0.3.23 + +// Changes to RMS forwarding + +// Version 1.0.3.24 + +// Fix RMS: from SMTP interface +// Accept RMS/ instead of RMS: for Thunderbird + +// Version 1.0.3.25 + +// Accept smtp: addresses from smtp client, and route to ISP gateway. +// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS. + +// Version 1.0.3.26 + +// Improve display of rms and smtp messages in message lists and message display. + +// Version 1.0.3.27 + +// Correct code that prevents mail being retured to originating BBS. +// Tidy stuck Nodes and Topics when all links close +// Fix B2 handling of @ to TO Address. + +// Version 1.0.3.28 + +// Ensure user Record for the BBS Call has BBS bit set. +// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set. +// Add user configurable welcome messages. + +// Version 1.0.3.29 + +// Add AUTH feature to Rig Control + +// Version 1.0.3.30 + +// Process Paclink Header (;FW:) + +// Version 1.0.3.31 + +// Process Messages with attachments. +// Add inactivity timeout to Chat Console sessions. + +// Version 1.0.3.32 + +// Fix for Paclink > BBS Addresses + +// Version 1.0.3.33 + +// Fix multiple transfers per session for B2. +// Kill messages eent to paclink. +// Add option to forward messages on arrival. + +// Version 1.0.3.34 + +// Fix bbs addresses to winlink. +// Fix adding @winlink.org to imcoming paclink msgs + +// Version 1.0.3.35 + +// Fix bbs addresses to winlink. (Again) + +// Version 1.0.3.36 + +// Restart changes for RMS/paclink + +// Version 1.0.3.37 + +// Fix for RMS Express forwarding + +// Version 1.0.3.38 + +// Fixes for smtp and lower case packet addresses from Airmail +// Fix missing > afer NO - Bid in MBL mode + +// Version 1.0.3.39 + +// Use ;FW: for RMS polling. + +// Version 1.0.3.40 + +// Add ELSE Option to connect scripts. + +// Version 1.0.3.41 + +// Improved handling of Multiple Addresses +// Add user colours to chat. + +// Version 1.0.3.42 + +// Poll multiple SSID's for RMS +// Colour support for BPQTEerminal +// New /C chat command to toggle colour on or off. + +// Version 1.0.3.43 + +// Add SKIPPROMPT command to forward scripts + +// Version 1.0.4.1 + +// Non - Beta Release +// Fix possible crash/corruption with long B2 messages + +// Version 1.0.4.2 + +// Add @winlink.org to the B2 From addresss if it is just a callsign +// Route Flood Bulls on TO as well as @ + +// Version 1.0.4.3 + +// Handle Packet Addresses from RMS Express +// Fix for Housekeeping B$ messages + +// Version 1.0.4.4 + +// Remove B2 header and all but the Body part from messages forwared using MBL +// Fix handling of ;FW: from RMS Express + +// Version 1.0.4.5 + +// Disable Paging on forwarding sessions. +// Kill Msgs sent to RMS Exxpress +// Add Name to Chat *** Joined msg + +// Version 1.0.4.6 + +// Pass smtp:winlink.org messages from Airmail to local user check +// Only apply local user check to RMS: messages @winlink.org +// Check locally input smtp: messages for local winlink.org users +// Provide facility to allow only one connect on a port + +// Version 1.0.4.8 + +// Only reset last listed on L or LR commands. + +// Version 1.0.4.9 + +// Fix error in handling smtp: messages to winlink.org addresses from Airmail + +// Version 1.0.4.10 + +// Fix Badwords processing +// Add Connect Script PAUSE command + +// Version 1.0.4.11 + +// Suppress display and listing of held messages +// Add option to exclude SYSOP messages from LM, KM, etc +// Fix crash whan receiving messages with long lines via plain text forwarding + +// Version 1.0.4.12 Jul 2010 + +// Route P messages on AT +// Allow Applications above 8 + +// Version 1.0.4.13 Aug 2010 + +// Fix TidyString for addresses of form John Wiseman +// Add Try/Except around socket routines + +// Version 1.0.4.14 Aug 2010 + +// Trap "Error - TNC Not Ready" in forward script response +// Fix restart after program error +// Add INFO command +// Add SYSOP-configurable HELP Text. + +// Version 1.0.4.15 Aug 2010 + +// Semaphore Connect/Disconnect +// Semaphore RemoveTempBIDS + +// Version 1.0.4.16 Aug 2010 + +// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS) + +// Version 1.0.4.17 Aug 2010 + +// Fix receiving multiple messages in FBB Uncompressed Mode +// Try to trap phantom chat node connections +// Add delay to close + + +// Version 1.0.4.18 Aug 2010 + +// Add "Send SYSTEM messages to SYSOP Call" Option +// set fwd bit on local winlink.org msgs if user is a BBS +// add winlink.org to from address of messages from WL2K that don't already have an @ + +// Version 1.0.4.19 Sept 2010 + +// Build a B2 From: address if possible, so RMS Express can reply to packet messages. +// Fix handling of addresses from WL2K with SSID's +// L@ now only matches up to length of input string. +// Remove "Type H for help" from login prompt. + +// Version 1.0.4.20 Sept 2010 + +// Process FBB 'E' response +// Handle FROM addresses with an @BBS +// Fix FROM addresses with @ on end. +// Extend delay before close after sending FQ on winmor/pactor sessions. + +// Version 1.0.4.21 Sept 2010 + +// Fix handling B2 From: with an HA +// Add "Expert User" welcome message. + +// Version 1.0.4.22 Sept 2010 + +// Version 1.0.4.23 Oct 2010 + +// Add Dup message supression +// Dont change B2 from if going to RMS + +// Version 1.0.4.24 Oct 2010 + +// Add "Save Registry Config" command +// Add forwarding on wildcarded TO for NTS +// Add option to force text mode forwarding +// Define new users as a temporaty BBS if SID received in reply to Name prompt +// Reduce delay before sending close after sending FQ on pactor sessions +// Fix processing of MIME boundary from GMail + +// Send /ex instead of ctrl/z for text mode forwarding +// Send [WL2K-BPQ... SID if user flagged as RMS Express +// Fix Chat Map reporting when more than one AXIP port +// Add Message State D for NTS Messages +// Forward messages in priority order - T, P, B +// Add Reject and Hold Filters +// Fix holding messages to local RMS users when received as part of a multiple addressee message + +// Version 1.0.4.25 Nov 2010 + +// Renumbered for release +// Add option to save Registry Config during Housekeeping + +// Version 1.0.4.26 Nov 2010 + +// Fix F> loop when doing MBL forwarding between BPQ BBSes +// Allow multiple To: addresses, separated by ; +// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages. +// Set Unforwarded Bulls to status '$' +// Accept MARS and USA as continent codes for MARS Packet Addresses +// Add option to send Non-delivery notifications. + +// Version 1.0.4.27 Dec 2010 + +// Add MSGTYPES fwd file option + +// Version 1.0.4.28 Dec 2010 + +// Renumbered to for release + +// Version 1.0.4.30 Dec 2010 + +// Fix rescan requeuing where bull was rejected by a BBS +// Fiz flagging bulls received by NNTP with $ if they need to be forwarded. +// Add Chat Keepalive option. +// Fix bug in non-delivery notification. + +// Version 1.0.4.32 Jan 2011 + +// Allow "Send from Clipboard" to send to rms: or smtp: +// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS) +// Fix corruption of messages converted to B2 if body contains binary data +// Fix occasional program error when forwarding B2 messages +// Limit FBB protocol data blocks to 250 to try to fix restart problem. +// Add F2 to F5 to open windows. + +// Version 1.0.4.33 Jan 2011 + +// Fix holding old bulls with forwarding info. + +// Version 1.0.4.33 Jan 2011 + +// Prevent transfer restarting after a program error. +// Allow Housekeeping to kill held messages. + +// Version 1.0.4.35 Jan 2011 + +// Add Size limits for P and T messages to MSGTYPES command +// Fix Error in MBL processing when blank lines received (introduced in .33) +// Trap possible PE in Send_MON_Datagram +// Don't use paging on chat sessions + +// Version 1.0.4.36 Jan 2011 + +// Fix error after handling first FBB block. +// Add $X and $x welcome message options. + +// Version 1.0.4.37 Jan 2011 + +// Change L command not to list the last message if no new ones are available +// Add LC I I@ IH IZ commands +// Add option to send warning to sysop if forwarded P or T message has nowhere to go +// Fixes for Winpack Compressed Download +// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set. +// Add console copy/paste. +// Add "No Bulls" Option. +// Add "Mail For" Beacon. +// Tidied up Tab order in config dialogs to help text-to-speech programs. +// Limit MaxMsgno to 99000. + +// Version 1.0.4.38 Feb 2011 + +// Renumbered for release + +// Version 1.0.4.40 April 2011 + +// Add POLLRMS command + +// Changes for Vista/Win7 (registry key change) +// Workaround for changes to RMS Express +// Fix AUTH bug in SMTP server +// Add filter to Edit Messages dialog + +// Version 1.0.4.41 April 2011 + +// Extend B2 proposals to other BPQMail systems so Reject Filter will work. +// Add Edit User Command +// Use internal Registry Save routine instead of Regedit +// Fix Start Forward/All +// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag) +// Add FWD SYSOP command +// Fix security on POLLRMS command +// Add AUTH command +// Leave selection in same place after Delete User +// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k +// Add option to show name as well as call on Chat messages +// Fix program error if you try to define more than 80 BBS's + +// Version 1.0.4.45 October 2011 + +// Changes to program error reporting. +// BBS "Returh to Node" command added +// Move config to "Standard" location (BPQ Directory/BPQMailChat) . +// Fix crash if "Edit Message" clicked with no message selected. + +// Version 1.0.4.46 October 2011 + +// Fix BaseDir test when BaseDir ends with \ or / +// Fix long BaseDir values (>50 chars) + +// Version 1.4.47.1 January 2012 + +// Call CloseBPQ32 on exit +// Add option to flash window instead of sounding bell on Chat Connects +// Add ShowRMS SYSOP command +// Update WP with I records from R: lines +// Send WP Updates +// Fix Paclen on Pactor-like sessions +// Fix SID and Prompt when RMS Express User is set +// Try to stop loop in Program Error/Restarting code +// Trap "UNABLE TO CONNECT" response in connect script +// Add facility to print messages or save them to a text file + +// Version 1.4.48.1 January 2012 + +// Add Send Message (as well as Send from Clipboard) +// Fix Email From: Address when forwaring using B2 +// Send WP from BBSCALL not SYSOPCALL +// Send Chat Map reports via BPQ32.dll + + +// Version 1.4.49.1 February 2012 + + +// Fix Setting Paclink mode on SNOS connects +// Remove creation of debugging file for each message +// Add Message Export and Import functions +// All printing of more than one message at a time +// Add command to toggle "Expert" status + +// Version 1.4.50.1 February 2012 + +// Fix forwarding to RMS Express users +// Route messages received via B2 to an Internet email address to RMS +// Add Reverse Poll interval +// Add full FROM address to POP3 messages +// Include HOMEBBS command in Help + + +// Version 1.4.51.1 June 2012 + +// Allow bulls to be sent from RMS Express. +// Handle BASE64 and Quoted-printable encoding of single part messages +// Work round for RMS Express "All proposals rejected" Bug. + +// Version 1.4.52.1 August 2012 + +// Fix size limit on B2 To List when sending to multiple dests +// Fix initialisation of DIRMES.SYS control record +// Allow use of Tracker and UZ7HO ports for UI messages + +// Version 1.4.53.1 September 2012 + +// Fix crash if R: line with out a CR found. + +// Version 1.4.54.1 ?? 2012 + +// Add configurable prompts +// Fix KISS-Only Test +// Send EHLO instead of HELO when Authentication is needed on SMTP session +// Add option to use local tome for bbs forwarding config +// Allow comment lines (; or @) or single space in fwd scripts +// Fix loss of forwarding info if SAVE is clicked before selecting a call + +// Version 1.4.55.1 June 2013 + +// Add option to remove users that have not connected for a long time. +// Add l@ smtp: +// Fix From: sent to POP3 Client when meaages is from RMS +// Display Email From on Manage Messages + +// Version 1.4.56.1 July 2013 + +// Add timeout +// Verify prompts +// Add IDLETIME command + + + +// Version 1.4.57.1 + +// Change default IDLETIME +// Fix display of BBS's in Web "Manage Messages" +// Add separate househeeping lifetines for T messages +// Don't change flag on forwarded or delivered messages if they sre subsequently read +// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet +// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured +// Fix receiving NTS messages via B2 +// Add option to send "Mail For", but not FBB Headers +// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems +// Fix Endian bug in FBB Compression code + + +// Version 1.4.58.1 + +// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag +// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink +// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink +// Collect stats by message type +// Fix Non-Delivery notifications to SMTP messages +// Add Message Type Stats to BBS Trafic Report +// Add "Batch forward to email" +// Add EXPORT command +// Allow more BBS records +// Allow lower case connect scripts +// Fix POP3 LIST command +// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding +// Fix duplicates of SP SYSOP@WW Messages +// Add command line option (tidymail) to delete redundant Mail files +// Add command line option (nohomebbs) to suppress HomeBBS prompt + +// 59 April 2014 + +// Add FLARQ Mail Mode +// Fix possible crash saving restart data +// Add script command ADDLF for connect scripts over Telnet +// Add recogniton of URONODE connected message +// Add option to stop Name prompt +// Add new RMS Express users with "RMS Express User" flag set +// Validate HTML Pages +// Add NTS swap file +// Add basic File list and read functions +// Fix Traffic report + +// 60 + +// Fix security hole in readfile + +// 61 August 2014 +// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS +// Dont treat "Attempting downlink" as a failure +// Add option to read messages during a list +// Fix crash during message renumber on MAC +// Timeout response to SID to try to avoid hang on an incomplete connection. +// Save config in file instead of registry +// Fix Manage Messages "EXPORT" option and check filename on EXPORT command +// Fix reverse forward prompt in MBL mode. +// Fix From address in POP3 messages where path is @winlink.org +// Fix possible program error in T message procesing +// Add MaxAge param (for incoming Bulls) + + +//62 November 2014 +// Add ZIP and Permit Bulls flag to Manage Users +// Allow users to kill their own B and anyone to kill T messages +// Improve saving of "Last Listed" +// Fix LL when paging +// Send Date received in R: Line (should fix B2 message restarts) +// Fix occasional crash in terminal part line processing +// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT +// Fix possible retry loop when message is deferred (FBB '=' response); +// Don't remove Attachments from received bulls. + +//63 Feb 2015 + +// Fix creating Bulls from RMS Express messages. +// Fix PE if message with no To: received. +// Fix setting "RMS Express User" flag on new connects from RMS Express +// Fix deleting 'T' messages downloaded by RMS Express +// Include MPS messages in count of messages to forward. +// Add new Welcome Message variable $F for messages to forward +// Fix setting Type in B2 header when usong NTS: or BULL: +// Remove trailing spaces from BID when Creating Message from Clipboard. +// Improved handling of FBB B1/B2 Restarts. + +//64 September 2015 + +// Fix Message Type in msgs from RMS Express to Internet +// Reopen Monitor window if open when program list closed +// Only apply NTS alias file to NTS Messages +// Fix failure to store some encrypted ISP passwords +// Allow EDITUSER to change "RMS Express User" flag +// Fix reporting of Config File errors +// Fix Finding MPS Messages (First to Forward was being used incorrectly) +// Add "Save Attachment" to Web Mgmt Interface +// Support Secure Signon on Forwarding sessions to CMS +// Save Forwarding config when BBS flag on user is cleared +// Pass internally generated SYSOP messages through routing process +// Add POP3 TOP command. +// Don't set 'T' messages to 'Y' when read. +// Add optional temporary connect script on "FWD NOW" command +// Add automatic import facility +// Accept RMS mail to BBS Call even if "Poll RMS" not set. + +// 65 November 2015 + +// Fix loading Housekeeping value for forwarded bulls. +// Fix re-using Fwd script override in timer driven forwarding. +// Add ampr.org handling +// Add "Dont forward" match on TO address for NTS +// Allow listing a combinatiom of state and type, such as LNT or LPF +// Fix handling ISP messages from gmail without a '+' +// Add basic WebMail support + +// 66 + +// Autoimport messages as Dummy Call, not SYSOP Call +// Add "My Messages" display option to WebMail +// Create .csv extract of User List during hourekeeping. +// Fix processing of NTS Alising of @ Addresses +// Don't reroute Delivered NTS Messages +// Add option to stop users killing T messages +// Add multicast Receive +// Fix initialising new message database format field +// Fix "Forward Messages to BBS Call" option. +// Add Filter WP Bulls option and allow multiple WP "TO" addresses +// Fix deleting P WP messages for other stations +// Fix saving blank lines in forwarding config +// Fix paging on L@ and l< +// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script +// Run DeleteRedundantMessages before renumbering messages +// Connect script now tries ELSE lines if prompt not received from remote BBS +// Send connecting call instead of BBS Name when connecting to CMS server. +// Add BID filter to Manage Messages +// Fix handling of over long suject lines in IMPORT +// Allow comments before ELSE in connect script +// Add Copy and Clear to Multicast Window +// Fix possible duplicate messages with MBL forwarding +// Set "Permit EMail" on IMPORT dummy User. +// Fix repeated running of housekeeping if clock is stepped forward. +// Fix corruption of CMS Pass field by Web interface +// Kill B2 WP bulls if FilterWPBulls set +// Include Message Type in BPQ B2 proposal extensions + +// 6.0.14.1 July 2017 + +// Fix corruption of BBSNumber if RMS Ex User and BBS both checked +// Tread B messages without an AT as Flood. +// Make sure Message headers are always saved to disk when a message status changes +// Reject message instead of failing session if TO address too long in FBB forwarding +// Fix error when FBB restart data exactly fills a packet. +// Fix possible generation of msg number zero in send nondlivery notification +// Fix problem with Web "Manage Messages" when stray message number zero appears +// Fix Crash in AMPR forward when host missing from VIA +// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS +// Fix test for Status "D" in forward check. +// Don't cancel AUTH on SMTP RSET +// Fix "nowhere to go" message on some messages sent to smtp addresses +// Add @ from Home BBS or WP is not spcified in "Send from Clipboard" + +// 6.0.15.1 Feb 2018 + +// Fix PE if Filename missing from FILE connect script command +// Suppress reporting errors after receiving FQ +// Fix problem caused by trailing spaces on callsign in WP database +// Support mixed case WINLINK Passwords + +// 6.0.16.1 March 2018 + +// Make sure messages sent to WL2K don;'t have @ on from: address +// If message to saildocs add R: line as an X header instead of to body +// Close session if more than 4 Invalid Commmad responses sent +// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client + +// 6.0.17.1 November 2018 + +// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq +// Accept an internet email address without rms: or smtp: +// Fix "Forward messages for BBS Call" when TO isn't BBS Call +// Accept NNTP commands in either case +// Add NNTP BODY command +// Timeout POP or SMTP TCP connections that are open too long +// Add YAPP support +// Fix connect script when Node CTEXT contains "} BBS " +// Fix handling null H Route +// Detect and correct duplicate BBS Numbers +// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B) +// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages +// Add support for Winlink HTML Forms to WebMail interface +// Update B2 header when using NTS alias file with B2 messages + +// 6.0.18.1 January 2019 + +// Ensure callsigns in WP database are upper case. +// Various fixes for Webmail +// Fix sending direct to ampr.org addresses +// Use SYSOP Call as default for Webmail if set +// Preparations for 64 bit version + + +// 6.0.19.1 September 2019 + +// Trap missing HTML reply Template or HTML files +// Fix case problems in HTML Templates +// Fix setting To call on reply to HTML messages +// More preparations for 64 bit including saving WP info as a text file. +// Set "RMS Express User" when a new user connects using PAT +// Increace maximum length on Forwarding Alias string in Web interface +// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS +// Fix program error if READ used without a filename +// Trap reject messages from Winlink CMS +// Fix "delete to recycle bin" on Linux +// Handle Radio Only Messages (-T or -R suffix on calling station) +// Fix program error on saving empty Alias list on Web Forwarding page +// Add REQDIR and REQFIL +// Experimental Blocked Uncompressed forwarding +// Security fix for YAPP +// Fix WebMail Cancel Send Message +// Fix processing Hold Message response from Winlink Express + +// 6.0.20.1 April 2020 + +// Improvments to YAPP +// Add Copy forwarding config +// Add Next and Previous buttons to Webmail message read screen +// Move HTML templates from HTMLPages to inline code. +// Fix Paclen on YAPP send +// Fix bug in handling "RMS Express User" +// Fix WINPACK compressed forwarding +// Add option to send P messages to more than one BBS +// Add "Default to Don't Add WINLINK.ORG" Config option +// Re-read Badwords.sys during Housekeeping +// Add BID Hold and Reject Filters +// On SMTP Send try HELO if EHLO rejected +// Allow SID response timeout to be configured per BBS +// Fix sending bulls with PAT +// Set "Forward Messages to BBS Call" when routing Bulls on TO +// Add option to send Mail For Message to APRS +// Fix WP update +// Fix Holding messages from Webmail Interface +// Add RMR command +// Add REROUTEMSGS BBS SYSOP command +// Disable null passwords and check Exclude flag in Webmail Signin +// Add basic Webmail logging + +// 6.0.21.1 December 2020 + +// Remove nulls from displayed messages. +// Fix Holding messages from SMTP and POP3 Interfaces +// Various fixes for handling messages to/from Internet email addresses +// Fix saving Email From field in Manage Messages +// Fix sending WL2K traffic reports via TriMode. +// Fix removing successive CR from Webmail Message display +// Fix Wildcarded @ forwarding +// Fix message type when receiving NTS Msgs form Airmail +// Fix address on SERVICE messages from Winlink +// Add multiple TO processing to Webmail non-template messages +// Don't backup config file if reading it fails +// Include Port and Freq on Connected log record +// Make sure welcome mesages don't end in > +// Allow flagging unread T messages as Delivered +// Replace \ with # in forward script so commands starting with # can be sent +// Fix forwarding NTS on TO field +// Fix possible crash in text mode forwarding +// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured +// Add DOHOUSEKEEPING sysop command +// Add MARS continent code +// Try to trap 'zombie' BBS Sessions +// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory. +// Fix corruption of message length when reading R2 message via Read command +// Fix paging on List command and add new combinations of List options +// Fix NNTP list and LC command when bulls are killed + +// 6.0.22.1 August 2021 + +// Fix flagging messages with attachments as read. +// Fix possible corruption of WP database and subsequent crash on reloading. +// Fix format of Web Manage Messages display +// Include SETNEXTMESSAGENUMBER in SYSOP Help Message +// Fix occasional "Incoming Connect from SWITCH" +// Fix L> with numeric dests +// Improved diagnostic for MailTCP select() error. +// Clear "RMS Express User" if user is changed to a BBS +// Fix saving Window positions on exit +// Fix parsing ReplyTemplate name in Webmail +// Handle multiple addressees for WebMail Forms messages to packet stations +// Add option to allow only known users to connect +// Add basic callsign validation to From address +// Add option to forward a user's messages to Winlink +// Move User config to main config file. +// Update message status whne reading a Forms Webmail message +// Speed up killing multiple messages +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command + +// 6.0.23.1 June 2022 + +// Fix crash when ; added to call in send commands +// Allow smtp/ override for messages from RMS Express to send via ISP gateway +// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured +// Recompiled for Web Interface changes in Node +// Add RMS Relay SYNC Mode (.17) +// Add Protocol changes for Relay RO forwarding +// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay +// Fix B2 Header Date in Webmail message with sttachments. +// Fix bug when using YAPP with VARA (.27) +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command +// Add mechsnism to send bbs log records to qttermtcp. (32) +// Add MFJ forwarding Mode (No @BBS on send) +// Fix handling CR/LF split over packet boundaries +// Add Header and Footers for Webmail Send (42) +// Fix Maintenance Interval in LinBPQ (53) +// Add RMS: to valid from addresses (.56) +// Fix Web management on Android deviced (.58) +// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70) +// Check Badword and Reject filters before processing WP Messages + +// 6.0.24.1 ?? 2022 + +// Fix ' in Webmail subject (8) +// Change web buttons to white on black when pressed (10) + + +#include "bpqmail.h" +#define MAIL +#include "Versions.h" + +#include "GetVersion.h" + +#define MAX_LOADSTRING 100 + +typedef int (WINAPI FAR *FARPROCX)(); +typedef int (WINAPI FAR *FARPROCZ)(); + +FARPROCX pDllBPQTRACE; +FARPROCZ pGetLOC; + +BOOL WINE = FALSE; + +INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +VOID SetupNTSAliases(char * FN); + +HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default +char * REGTREETEXT = "HKEY_LOCAL_MACHINE"; + +// Global Variables: +HINSTANCE hInst; // current instance +TCHAR szTitle[MAX_LOADSTRING]; // The title bar text +TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +UINT BPQMsg; + +HWND MainWnd; +HWND hWndSess; +RECT MainRect; +HMENU hActionMenu; +static HMENU hMenu; +HMENU hDisMenu; // Disconnect Menu Handle +HMENU hFWDMenu; // Forward Menu Handle + +int SessX, SessY, SessWidth; // Params for Session Window + +char szBuff[80]; + +#define MaxSockets 64 + +int _MYTIMEZONE = 0; + +ConnectionInfo Connections[MaxSockets+1]; + +//struct SEM AllocSemaphore = {0, 0}; +//struct SEM ConSemaphore = {0, 0}; +//struct SEM OutputSEM = {0, 0}; + +//struct UserInfo ** UserRecPtr=NULL; +//int NumberofUsers=0; + +//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes + +//struct MsgInfo ** MsgHddrPtr=NULL; +//int NumberofMessages=0; + +//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search + +//BIDRec ** BIDRecPtr=NULL; +//int NumberofBIDs=0; + +extern BIDRec ** TempBIDRecPtr; +//int NumberofTempBIDs=0; + +//WPRec ** WPRecPtr=NULL; +//int NumberofWPrecs=0; + +extern char ** BadWords; +//int NumberofBadWords=0; +extern char * BadFile; + +//int LatestMsg = 0; +//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg +//int HighestBBSNumber = 0; + +//int MaxMsgno = 60000; +//int BidLifetime = 60; +//int MaintInterval = 24; +//int MaintTime = 0; +//int UserLifetime = 0; + + +BOOL cfgMinToTray; + +BOOL DisconnectOnClose; + +extern char PasswordMsg[100]; + +char cfgHOSTPROMPT[100]; + +char cfgCTEXT[100]; + +char cfgLOCALECHO[100]; + +char AttemptsMsg[]; +char disMsg[]; + +char LoginMsg[]; + +char BlankCall[]; + + +ULONG BBSApplMask; +ULONG ChatApplMask; + +int BBSApplNum; + +//int StartStream=0; +int NumberofStreams; +int MaxStreams; + +extern char BBSSID[]; +extern char ChatSID[]; + +extern char NewUserPrompt[100]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +extern BOOL DontNeedHomeBBS; + +char BBSName[100]; +char MailForText[100]; + +char SignoffMsg[100]; + +char AbortedMsg[100]; + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +char BaseDir[MAX_PATH]; +char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% + +char MailDir[MAX_PATH]; + +char RlineVer[50]; + +extern BOOL KISSOnly; + +extern BOOL OpenMon; + +extern struct ALIAS ** NTSAliases; + +extern int EnableUI; +extern int RefuseBulls; +extern int SendSYStoSYSOPCall; +extern int SendBBStoSYSOPCall; +extern int DontHoldNewUsers; +extern int ForwardToMe; + +extern int MailForInterval; + +char zeros[NBMASK]; // For forward bitmask tests + +time_t MaintClock; // Time to run housekeeping + +struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. + +// Filter Params + +char ** RejFrom; // Reject on FROM Call +char ** RejTo; // Reject on TO Call +char ** RejAt; // Reject on AT Call +char ** RejBID; // Reject on BID + +char ** HoldFrom; // Hold on FROM Call +char ** HoldTo; // Hold on TO Call +char ** HoldAt; // Hold on AT Call +char ** HoldBID; // Hold on BID + + +// Send WP Params + +BOOL SendWP; +char SendWPVIA[81]; +char SendWPTO[11]; +int SendWPType; + + +int ProgramErrors = 0; + +UCHAR BPQDirectory[260] = ""; + + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +ATOM RegisterMainWindowClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +unsigned long _beginthread( void( *start_address )(VOID * DParam), + unsigned stack_size, VOID * DParam); + +VOID SendMailForThread(VOID * Param); +BOOL CreatePipeThread(); +int DeleteRedundantMessages(); +VOID BBSSlowTimer(); +VOID CopyConfigFile(char * ConfigName); +BOOL CreateMulticastConsole(); +char * CheckToAddress(CIRCUIT * conn, char * Addr); +BOOL CheckifPacket(char * Via); +int GetHTMLForms(); + +struct _EXCEPTION_POINTERS exinfox; + +CONTEXT ContextRecord; +EXCEPTION_RECORD ExceptionRecord; + +DWORD Stack[16]; + +BOOL Restarting = FALSE; + +Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg) +{ + unsigned int SPPtr; + unsigned int SPVal; + + memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord)); + memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord)); + + SPPtr = ContextRecord.Esp; + + Debugprintf("BPQMail *** Program Error %x at %x in %s", + ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg); + + + __asm{ + + mov eax, SPPtr + mov SPVal,eax + lea edi,Stack + mov esi,eax + mov ecx,64 + rep movsb + + } + + Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", + ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx, + ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal); + + Debugprintf("Stack:"); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); + +} + + + +void myInvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) +{ + Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); + + if (expression && function && file) + { + Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression); + Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function); + Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line); + } +} + +// If program gets too many program errors, it will restart itself and shut down + +VOID CheckProgramErrors() +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char ProgName[256]; + + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); + + exit(0); + } +} + + +VOID WriteMiniDump() +{ +#ifdef WIN32 + + HANDLE hFile; + BOOL ret; + char FN[256]; + + sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // +#ifdef WIN32 + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + + _asm{ + + mov eax,1 + mov ebx, Semaphore + xchg [ebx],eax // this instruction is locked + + cmp eax,0 + jne loop1 // someone else got it - try again +; +; ok, weve got the semaphore +; + } +#else + + while (Semaphore->Flag) + usleep(10000); + + Semaphore->Flag = 1; + +#endif + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + Semaphore->Flag = 0; + + return; +} + +char * CmdLine; + +extern int configSaved; + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + MSG msg; + HACCEL hAccelTable; + int BPQStream, n; + struct UserInfo * user; + struct _EXCEPTION_POINTERS exinfo; + _invalid_parameter_handler oldHandler, newHandler; + char Msg[100]; + int i = 60; + struct NNTPRec * NNTPREC; + struct NNTPRec * SaveNNTPREC; + + CmdLine = _strdup(lpCmdLine); + _strlwr(CmdLine); + + if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs + { + hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0, + CW_USEDEFAULT, 100, 550, 70, + NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + while (i-- > 0) + { + sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i); + SetWindowText(hWnd, Msg); + + Sleep(1000); + } + + DestroyWindow(hWnd); + } + + __try { + + // Trap CRT Errors + + newHandler = myInvalidParameterHandler; + oldHandler = _set_invalid_parameter_handler(newHandler); + + // Initialize global strings + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat)); + + // Main message loop: + + Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting"); + Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting"); + Debugprintf("BPQMail Starting"); + + if (pDllBPQTRACE == 0) + Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable"); + + + } My__except_Routine("Init"); + + while (GetMessage(&msg, NULL, 0, 0)) + { + __try + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + #define EXCEPTMSG "GetMessageLoop" + #include "StdExcept.c" + + CheckProgramErrors(); + } + } + + __try + { + for (n = 0; n < NumberofStreams; n++) + { + BPQStream=Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + + hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0, + 150, 200, 350, 40, NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + Sleep(1000); // A bit of time for links to close + + DestroyWindow(hWnd); + + if (ConsHeader[0]->hConsole) + DestroyWindow(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DestroyWindow(ConsHeader[1]->hConsole); + if (hMonitor) + { + DestroyWindow(hMonitor); + hMonitor = (HWND)1; // For status Save + } + + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + + configSaved = 1; + SaveConfig(ConfigName); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + // Free all allocated memory + + for (n = 0; n <= NumberofUsers; n++) + { + user = UserRecPtr[n]; + + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + free(user->Temp); + + free(user); + } + + free(UserRecPtr); + + for (n = 0; n <= NumberofMessages; n++) + free(MsgHddrPtr[n]); + + free(MsgHddrPtr); + + for (n = 0; n <= NumberofWPrecs; n++) + free(WPRecPtr[n]); + + free(WPRecPtr); + + for (n = 0; n <= NumberofBIDs; n++) + free(BIDRecPtr[n]); + + free(BIDRecPtr); + + if (TempBIDRecPtr) + free(TempBIDRecPtr); + + NNTPREC = FirstNNTPRec; + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + n = 0; + + if (Aliases) + { + while(Aliases[n]) + { + free(Aliases[n]->Dest); + free(Aliases[n]); + n++; + } + + free(Aliases); + FreeList(AliasText); + } + + n = 0; + + if (NTSAliases) + { + while(NTSAliases[n]) + { + free(NTSAliases[n]->Dest); + free(NTSAliases[n]); + n++; + } + + free(NTSAliases); + } + + FreeOverrides(); + + FreeList(RejFrom); + FreeList(RejTo); + FreeList(RejAt); + FreeList(RejBID); + FreeList(HoldFrom); + FreeList(HoldTo); + FreeList(HoldAt); + FreeList(HoldBID); + FreeList(SendWPAddrs); + + Free_UI(); + + for (n=1; n<20; n++) + { + if (MyElements[n]) free(MyElements[n]); + } + + free(WelcomeMsg); + free(NewWelcomeMsg); + free(ExpertWelcomeMsg); + + free(Prompt); + free(NewPrompt); + free(ExpertPrompt); + + FreeWebMailMallocs(); + + free(CmdLine); + + _CrtDumpMemoryLeaks(); + + } + My__except_Routine("Close Processing"); + + CloseBPQ32(); // Close Ext Drivers if last bpq32 process + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +// COMMENTS: +// +// This function and its usage are only necessary if you want this code +// to be compatible with Win32 systems prior to the 'RegisterClassEx' +// function that was added to Windows 95. It is important to call this function +// so that the application will get 'well formed' small icons associated +// with it. +// +// +#define BGCOLOUR RGB(236,233,216) +//#define BGCOLOUR RGB(245,245,245) + +HBRUSH bgBrush; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = DLGWINDOWEXTRA; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = bgBrush; + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON)); + + return RegisterClassEx(&wcex); +} + + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HWND hWnd; + +int AXIPPort = 0; + +char LOC[7] = ""; + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + char Title[80]; + WSADATA WsaData; + HMENU hTopMenu; // handle of menu + HKEY hKey=0; + int retCode; + RECT InitRect; + RECT SessRect; + struct _EXCEPTION_POINTERS exinfo; + + HMODULE ExtDriver = LoadLibrary("bpq32.dll"); + + if (ExtDriver) + { + pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8"); + pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0"); + + if (pGetLOC) + { + char * pLOC = (char *)pGetLOC(); + memcpy(LOC, pLOC, 6); + } + } + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE =TRUE; + Debugprintf("Running under WINE"); + } + + + REGTREE = GetRegistryKey(); + REGTREETEXT = GetRegistryKeyText(); + + Sleep(1000); + + { + int n; + struct _EXTPORTDATA * PORTVEC; + + KISSOnly = TRUE; + + for (n=1; n <= GetNumberofPorts(); n++) + { + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0) + KISSOnly = FALSE; + + if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR + KISSOnly = FALSE; + + if (AXIPPort == 0) + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0) + { + AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER; + KISSOnly = FALSE; + } + } + } + } + } + + hInst = hInstance; + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + return FALSE; + } + + MainWnd = hWnd; + + GetVersionInfo(NULL); + + sprintf(Title,"G8BPQ Mail Server Version %s", VersionString); + + sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]); + + SetWindowText(hWnd,Title); + + hWndSess = GetDlgItem(hWnd, 100); + + GetWindowRect(hWnd, &InitRect); + GetWindowRect(hWndSess, &SessRect); + + SessX = SessRect.left - InitRect.left ; + SessY = SessRect.top -InitRect.top; + SessWidth = SessRect.right - SessRect.left; + + // Get handles for updating menu items + + hTopMenu=GetMenu(MainWnd); + hActionMenu=GetSubMenu(hTopMenu,0); + + hFWDMenu=GetSubMenu(hActionMenu,0); + hMenu=GetSubMenu(hActionMenu,1); + hDisMenu=GetSubMenu(hActionMenu,2); + + CheckTimer(); + + cfgMinToTray = GetMinimizetoTrayFlag(); + + if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) + if (cfgMinToTray) + { + ShowWindow(hWnd, SW_HIDE); + } + else + { + ShowWindow(hWnd, nCmdShow); + } + else + ShowWindow(hWnd, nCmdShow); + + UpdateWindow(hWnd); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + __try { + + return Initialise(); + + }My__except_Routine("Initialise"); + + return FALSE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + int state,change; + ConnectionInfo * conn; + struct _EXCEPTION_POINTERS exinfo; + + + if (message == BPQMsg) + { + if (lParam & BPQMonitorAvail) + { + __try + { + DoBBSMonitorData(wParam); + } + My__except_Routine("DoMonitorData"); + + return 0; + + } + if (lParam & BPQDataAvail) + { + // Dont trap error at this level - let Node error handler pick it up +// __try +// { + DoReceivedData(wParam); +// } +// My__except_Routine("DoReceivedData") + return 0; + } + if (lParam & BPQStateChange) + { + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + __try + { + SessionState(wParam, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + __try {Connected(wParam);} + My__except_Routine("Connected"); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + __try{Disconnected(wParam);} + My__except_Routine("Disconnected"); + FreeSemaphore(&ConSemaphore); + } + } + } + My__except_Routine("DoStateChange"); + + } + + return 0; + } + + + switch (message) + { + + case WM_KEYUP: + + switch (wParam) + { + case VK_F2: + CreateConsole(-1); + return 0; + + case VK_F3: + CreateMulticastConsole(); + return 0; + + case VK_F4: + CreateMonitor(); + return 0; + + case VK_TAB: + return TRUE; + + break; + + + + } + return 0; + + case WM_TIMER: + + if (wParam == 1) // Slow = 10 secs + { + __try + { + time_t NOW = time(NULL); + struct tm * tm; + RefreshMainWindow(); + CheckTimer(); + TCPTimer(); + BBSSlowTimer(); + FWDTimerProc(); + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + My__except_Routine("Slow Timer"); + } + else + __try + { + TrytoSend(); + TCPFastTimer(); + } + My__except_Routine("TrytoSend"); + + return (0); + + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)hActionMenu) + { + if (IsClipboardFormatAvailable(CF_TEXT)) + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED ); + + return TRUE; + } + + if (wParam == (WPARAM)hFWDMenu) + { + // Set up Forward Menu + + struct UserInfo * user; + char MenuLine[30]; + + for (user = BBSChain; user; user = user->BBSNext) + { + sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user)); + + if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, + MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0) + + AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine); + } + return TRUE; + } + + if (wParam == (WPARAM)hDisMenu) + { + // Set up Disconnect Menu + + CIRCUIT * conn; + char MenuLine[30]; + int n; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND); + + if (conn->Active) + { + sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); + AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine); + } + } + return TRUE; + } + break; + + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + + if (wmEvent == LBN_DBLCLK) + + break; + + if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) + { + // disconnect user + + conn=&Connections[wmId-IDM_DISCONNECT]; + + if (conn->Active) + { + Disconnect(conn->BPQStream); + } + } + + if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100) + { + StartForwarding(wmId - IDM_FORWARD_ALL, NULL); + return 0; + } + + switch (wmId) + { + case IDM_LOGBBS: + + ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS); + break; + + case IDM_LOGCHAT: + + ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); + break; + + case IDM_LOGTCP: + + ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP); + break; + + case IDM_HOUSEKEEPING: + + DoHouseKeeping(TRUE); + + break; + + case IDM_CONSOLE: + + CreateConsole(-1); + break; + + case IDM_MCMONITOR: + + CreateMulticastConsole(); + break; + + case IDM_MONITOR: + + CreateMonitor(); + break; + + case RESCANMSGS: + + ReRouteMessages(); + break; + + case IDM_IMPORT: + + ImportMessages(NULL, "", FALSE); + break; + + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + + case ID_HELP_ONLINEHELP: + + ShellExecute(hWnd,"open", + "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html", + "", NULL, SW_SHOWNORMAL); + + break; + + case IDM_CONFIG: + DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc); + break; + + case IDM_USERS: + DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc); + break; + + case IDM_FWD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc); + break; + + case IDM_MESSAGES: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc); + break; + + case IDM_WP: + DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc); + break; + + case ID_ACTIONS_SENDMSGFROMCLIPBOARD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc); + break; + + case ID_ACTIONS_SENDMESSAGE: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc); + break; + + case ID_MULTICAST: + + MulticastRX = !MulticastRX; + CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED); + break; + + case IDM_EXIT: + DestroyWindow(hWnd); + break; + + + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_SIZE: + + if (wParam == SIZE_MINIMIZED) + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + return (0); + + + case WM_SIZING: + { + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE); + + return TRUE; + } + + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + // TODO: Add any drawing code here... + EndPaint(hWnd, &ps); + break; + + case WM_DESTROY: + + GetWindowRect(MainWnd, &MainRect); // For save soutine + if (ConsHeader[0]->hConsole) + GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine + if (ConsHeader[1]->hConsole) + GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine + if (hMonitor) + GetWindowRect(hMonitor, &MonitorRect); // For save soutine + + KillTimer(hWnd,1); + KillTimer(hWnd,2); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); + + return TRUE; + + case WM_SIZING: + { + HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1); + + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE); + + return TRUE; + } + + case WM_COMMAND: + + if (LOWORD(wParam) == IDSEND) + { + char status [3]; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + char FileList[32768]; + BIDRec * BIDRec; + int MsgLen; + char * MailBuffer; + char MsgFile[MAX_PATH]; + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + char HDest[61]; + char Destcopy[61]; + char * Vptr; + char * FileName[100]; + int FileLen[100]; + char * FileBody[100]; + int n, Files = 0; + int TotalFileSize = 0; + char * NewMsg; + + GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60); + strcpy(Destcopy, HDest); + + GetDlgItemText(hDlg, IDC_MSGBID, BID, 13); + strlop(BID, ' '); + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767); + + // if there are attachments, check that they can be opened ane read + + n = 0; + + if (FileList[0]) + { + FILE * Handle; + struct stat STAT; + char * ptr1 = FileList, * ptr2; + + while(ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, ';'); + + if (ptr2) + *(ptr2++) = 0; + + FileName[n++] = ptr1; + + ptr1 = ptr2; + } + + FileName[n] = 0; + + // read the files + + Files = n; + n = 0; + + while (FileName[n]) + { + if (stat(FileName[n], &STAT) == -1) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't find file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileLen[n] = STAT.st_size; + + Handle = fopen(FileName[n], "rb"); + + if (Handle == NULL) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't open file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileBody[n] = malloc(FileLen[n]+1); + + fread(FileBody[n], 1, FileLen[n], Handle); + + fclose(Handle); + + TotalFileSize += FileLen[n]; + n++; + } + } + + if (strlen(HDest) == 0) + { + MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR); + return TRUE; + } + + if (strlen(BID)) + { + if (LookupBID(BID)) + { + // Duplicate bid + + MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, SYSOPCall); + + Vptr = strlop(Destcopy, '@'); + + if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @ + { + Vptr = strchr(Destcopy, '!'); + strcpy(Msg->via, Vptr); + strlop(Destcopy, '!'); + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + goto gotAddr; + } + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + + _strupr(Msg->to); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr = HDest; + memmove(HDest, &HDest[4], strlen(HDest)); + strcpy(Msg->to, "RMS"); + + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr = HDest; + memmove(HDest, &HDest[5], strlen(HDest)); + Msg->to[0] = 0; + } + } + else if (Vptr) + { + // If looks like a valid email address, treat as such + + int tolen = (Vptr - Destcopy) - 1; + + if (tolen > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = HDest; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else + { + MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + } + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, Vptr); + } +gotAddr: + GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61); + GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); + Msg->type = status[0]; + Msg->status = 'N'; + + if (strlen(BID) == 0) + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0); + + MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments + + if (Files) + { + char DateString[80]; + struct tm * tm; + + char Type[16] = "Private"; + + // Get Type + + if (Msg->type == 'B') + strcpy(Type, "Bulletin"); + else if (Msg->type == 'T') + strcpy(Type, "Traffic"); + + // Create a B2 Message + + // B2 Header + + NewMsg = MailBuffer + 1000; + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + // Remove last Source Route + + if (strchr(HDest, '!')) + { + char * bang = HDest + strlen(HDest); + + while (*(--bang) != '!'); // Find last ! + + *(bang) = 0; // remove it; + } + + NewMsg += sprintf(NewMsg, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName); + + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen); + + for (n = 0; n < Files; n++) + { + char * p = FileName[n], * q; + + // Remove any path + + q = strchr(p, '\\'); + + while (q) + { + if (q) + *q++ = 0; + p = q; + q = strchr(p, '\\'); + } + + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1); + NewMsg += MsgLen; + NewMsg += sprintf(NewMsg, "\r\n"); + + for (n = 0; n < Files; n++) + { + memcpy(NewMsg, FileBody[n], FileLen[n]); + NewMsg += FileLen[n]; + free(FileBody[n]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + Msg->length = NewMsg - (MailBuffer + 1000); + NewMsg = MailBuffer + 1000; + Msg->B2Flags = B2Msg | Attachments; + } + + else + { + GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1); + Msg->length = MsgLen; + NewMsg = MailBuffer; + } + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = CreateFile(MsgFile, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL); + CloseHandle(hFile); + } + + free(MailBuffer); + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + EndDialog(hDlg, LOWORD(wParam)); + + return TRUE; + } + + + if (LOWORD(wParam) == IDSelectFiles) + { + char FileNames[2048]; + char FullFileNames[32768]; + OPENFILENAME Ofn; + int err; + + FileNames[0] = 0; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileNames; + Ofn.nMaxFile = 2048; + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; + Ofn.lpstrTitle = NULL;//; + + if (GetOpenFileName(&Ofn)) + { + // if one is selected, a single string is returned, if more than one, a single + // path, followed by all the strings, duuble null terminated. + + char * Names[101]; // Allow up to 100 names + int n = 0; + char * ptr = FileNames; + + while (*ptr) + { + Names[n++] = ptr; + ptr += strlen(ptr); + ptr++; + } + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768); + + if (strlen(FullFileNames)) + strcat(FullFileNames, ";"); + + if (n == 1) + { + // Single Select + + strcat(FullFileNames, FileNames); + } + else + { + int i = 1; + + while(i < n) + { + strcat(FullFileNames, Names[0]); + strcat(FullFileNames, "\\"); + strcat(FullFileNames, Names[i]); + i++; + if (i < n) + strcat(FullFileNames, ";"); + } + } + SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames); + } + else + err = GetLastError(); + return (INT_PTR)TRUE; + } + + + if (LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HGLOBAL hglb; + LPTSTR lptstr; + + switch (message) + { + case WM_INITDIALOG: + + SetWindowText(hDlg, "Send Message from Clipboard"); + + if (!IsClipboardFormatAvailable(CF_TEXT)) + break; + + if (!OpenClipboard(hDlg)) + break; + + hglb = GetClipboardData(CF_TEXT); + + if (hglb != NULL) + { + lptstr = GlobalLock(hglb); + + if (lptstr != NULL) + { + SetDlgItemText(hDlg, IDC_EDIT1, lptstr); + GlobalUnlock(hglb); + } + } + CloseClipboard(); + } + + return SendMsgDialogProc(hDlg, message, wParam, lParam); + +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +SMTPMsgs = 0; + +int RefreshMainWindow() +{ + char msg[80]; + CIRCUIT * conn; + int i,n, SYSOPMsgs = 0, HeldMsgs = 0; + time_t now; + struct tm * tm; + char tim[20]; + + SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); + + SMTPMsgs = 0; + + for (n = 0; n < NumberofStreams; n++) + { + conn=&Connections[n]; + + if (!conn->Active) + { + strcpy(msg,"Idle"); + } + else + { + { + if (conn->UserPointer == 0) + strcpy(msg,"Logging in"); + else + { + i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", + conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, + "BBS", conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); + } + + SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); + + n = 0; + + for (i=1; i <= NumberofMessages; i++) + { + if (MsgHddrPtr[i]->status == 'N') + { + if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) + SYSOPMsgs++; + else + if (MsgHddrPtr[i]->to[0] == 0) + SMTPMsgs++; + } + else + { + if (MsgHddrPtr[i]->status == 'H') + HeldMsgs++; + } + } + + SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); + + SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE); + + now = time(NULL); + + tm = gmtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_UTC, tim); + + tm = localtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_LOCAL, tim); + + + return 0; +} + +#define MAX_PENDING_CONNECTS 4 + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +SOCKADDR_IN local_sin; /* Local socket - internet style */ + +PSOCKADDR_IN psin; + +SOCKET sock; + + + +BOOL Initialise() +{ + int i, len; + ConnectionInfo * conn; + struct UserInfo * user = NULL; + HKEY hKey=0; + char * ptr1; + int Attrs, ret; + char msg[500]; + TIME_ZONE_INFORMATION TimeZoneInformation; + struct stat STAT; + + GetTimeZoneInformation(&TimeZoneInformation); + + _tzset(); + _MYTIMEZONE = timezone; + _MYTIMEZONE = TimeZoneInformation.Bias * 60; + + // Register message for posting by BPQDLL + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + // See if we need to warn of possible problem with BaseDir moved by installer + + strcpy(BPQDirectory, GetBPQDirectory()); + + sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory); + + len = strlen(BaseDir); + ptr1 = BaseDir; + + while (*ptr1) + { + if (*(ptr1) == '/') *(ptr1) = '\\'; + ptr1++; + } + + // Make Sure BASEDIR Exists + + Attrs = GetFileAttributes(BaseDir); + + if (Attrs == -1) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO); + + if (ret == IDYES) + { + ret = CreateDirectory(BaseDir, NULL); + if (ret == 0) + { + MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP); + return FALSE; + } + } + else + { + MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP); + return FALSE; + } + } + else + { + if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY)) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP); + + return FALSE; + } + } + + initUTF8(); + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "\\"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "\\"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "\\"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "\\"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "\\"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "\\"); + strcat(MailDir, "Mail"); + + CreateDirectory(MailDir, NULL); // Just in case + + strcpy(ConfigName, BaseDir); + strcat(ConfigName, "\\"); + strcat(ConfigName, "BPQMail.cfg"); + + UsingingRegConfig = FALSE; + + // if config file exists use it else try to get from Registry + + if (stat(ConfigName, &STAT) == -1) + { + UsingingRegConfig = TRUE; + + if (GetConfigFromRegistry()) + { + SaveConfig(ConfigName); + } + else + { + int retCode; + + strcpy(BBSName, GetNodeCall()); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + + sprintf(msg, "No configuration found - Dummy Config created"); + + retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL); + + if (retCode == IDCANCEL) + return FALSE; + + SaveConfig(ConfigName); + } + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + ret = MessageBox(NULL, + "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP); + return FALSE; + } + + // Got a Config File + + if (MainRect.right < 100 || MainRect.bottom < 100) + { + GetWindowRect(MainWnd, &MainRect); + } + + MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); + + if (OpenMon) + CreateMonitor(); + + BBSApplMask = 1<<(BBSApplNum-1); + + ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE); + + // Make backup copies of Databases + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + + UsingingRegConfig = FALSE; + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + // Allocate Streams + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + + BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask); + Disconnect(conn->BPQStream); + } + + InitialiseTCP(); + + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (BBSApplNum) + { + SetupUIInterface(); + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + } + + if (cfgMinToTray) + { + AddTrayMenuItem(MainWnd, "Mail Server"); + } + + SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) + SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms) + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = _mkgmtime(tm); + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + } + + if (strstr(CmdLine, "tidymail")) + DeleteRedundantMessages(); + + if (strstr(CmdLine, "nohomebbs")) + DontNeedHomeBBS = TRUE; + + if (strstr(CmdLine, "DontCheckFromCall")) + DontCheckFromCall = TRUE; + + CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); + + RefreshMainWindow(); + +// CreateWPReport(); + + CreatePipeThread(); + + return TRUE; +} + +int ConnectState(Stream) +{ + int state; + + SessionStateNoAck(Stream, &state); + return state; +} +UCHAR * EncodeCall(UCHAR * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; + +} + +/* +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo) +{ + struct UserInfo * user; + + int i = FWDInfo->UserIndex; + + if (i == -1) + { + FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users + } + + for (i++; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + FWDInfo->UserIndex = i; + strcpy(FWDInfo->UserCall, user->Call); + FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20; + return ; + } + } + + // Finished Scan + + FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0; +} +*/ + +#ifndef NEWROUTING + +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) +{ +} +VOID SetupMyHA() +{ +} +VOID SetupFwdAliases() +{ +} + +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) +{ + struct UserInfo * bbs; + struct BBSForwardingInfo * ForwardingInfo; + char ATBBS[41]; + char * HRoute; + int Count =0; + + strcpy(ATBBS, Msg->via); + HRoute = strlop(ATBBS, '.'); + + if (Msg->type == 'P') + { + // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSToList(Msg, bbs, ForwardingInfo)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + return FALSE; + } + + // Bulls go to all matching BBSs, so the order of checking doesn't matter + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + Count++; + } + } + + return Count; +} +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** Calls; + char ** HRoutes; + int i, j; + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + + // Check AT distributions + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + + + return FALSE; + +} + +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Calls; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) +{ + char ** Calls; + + // Check AT distributions + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** HRoutes; + int i, j; + + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + return FALSE; +} + +#endif + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} diff --git a/BPQMail.rc b/BPQMail.rc new file mode 100644 index 0000000..24bda1f --- /dev/null +++ b/BPQMail.rc @@ -0,0 +1,1475 @@ +//Microsoft Developer Studio generated resource script. +// +#include "BPQMailrc.h" + +// Generated Help ID header file +#define APSTUDIO_HIDDEN_SYMBOLS +#include "BPQMailrc.hm" +#undef APSTUDIO_HIDDEN_SYMBOLS + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "AFXRES.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#define MAILCHAT +#include "..\CommonSource\Versions.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROPPAGE_LARGE DIALOG DISCARDABLE 0, 0, 235, 156 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Property Page" +FONT 8, "MS Sans Serif" +BEGIN +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO MOVEABLE PURE +BEGIN + IDD_PROPPAGE_LARGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +BPQMAIL DIALOG DISCARDABLE 120, 50, 294, 165 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "G8BPQ Mail and Chat Server 1.0.0.10 Beta July 2009" +CLASS "BPQMail" +FONT 8, "FixedSys" +BEGIN + LTEXT " User Callsign Stream Conf Queue",101,3,4,184, + 10 + LTEXT "UTC",IDC_STATIC,197,3,15,10 + LTEXT "Local",IDC_STATIC,241,3,21,10 + LTEXT "",IDC_UTC,215,3,25,10 + LTEXT "",IDC_LOCAL,269,3,25,10 + LISTBOX 100,2,16,190,130,WS_VSCROLL + LTEXT "Msgs",IDC_STATIC,197,21,40,10 + LTEXT "",IDC_MSGS,243,21,20,8 + LTEXT "Sysop Msgs",IDC_STATIC,197,32,40,10 + LTEXT "Held Msgs",IDC_STATIC,197,43,40,10 + LTEXT "",IDC_SYSOPMSGS,243,32,20,8 + LTEXT "",IDC_HELD,243,43,20,8 + LTEXT "SMTP Msgs",IDC_STATIC,197,54,40,10 + LTEXT "",IDC_SMTP,243,54,20,8 + LTEXT "Msg SEM Clashes",IDC_STATIC,197,70,63,10 + LTEXT "Alloc SEM Clashes",IDC_STATIC,197,81,68,10 + LTEXT "0",IDC_MSGSEM,274,70,20,10 + LTEXT "0",IDC_ALLOCSEM,274,81,20,10 + LTEXT "Con SEM Clashes",IDC_STATIC,197,92,68,10 + LTEXT "0",IDC_CONSEM,274,92,20,10 +END + +CONSOLEWINDOW DIALOG DISCARDABLE 17, 25, 400, 301 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Mail Console" +MENU CONSOLEMENU +CLASS "CONSOLEWINDOW" +FONT 8, "FixedSys" +BEGIN + EDITTEXT 118,24,228,348,15,ES_AUTOHSCROLL | ES_NOHIDESEL +END + +BPQMONWINDOW DIALOG DISCARDABLE 17, 25, 400, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Mail Monitor" +MENU MENU_2 +CLASS "BPQMONWINDOW" +FONT 8, "FixedSys" +BEGIN + LISTBOX 121,6,25,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_HSCROLL +END + +BPQMCWINDOW DIALOG DISCARDABLE 17, 25, 500, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Multicast Monitor" +MENU MENU_1 +CLASS "BPQMCWINDOW" +FONT 8, "FixedSys" +BEGIN +END + +BPQDEBUGWINDOW DIALOG DISCARDABLE 17, 25, 400, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Mail Debug Window" +MENU MENU_3 +CLASS "BPQDEBUGWINDOW" +FONT 8, "FixedSys" +BEGIN + LISTBOX 122,5,156,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_HSCROLL +END + +102 DIALOG DISCARDABLE 0, 0, 385, 301 +STYLE WS_POPUP | WS_CAPTION | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Configuration" +FONT 8, "System" +BEGIN +END + +BBS_CONFIG DIALOG DISCARDABLE 0, 0, 381, 306 +STYLE WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "BBS Call is the base callsign, without SSID. This is not necessarily the same as the Application Callsign defined in the BPQ32 configuration. SYSOP Call is the callsign used by the local console.", + IDC_STATIC,10,4,354,23 + LTEXT "The Application Number defines which BPQ32 Application gives access to the BBS. Note this is the APPLNumber (1-32) not an Application Mask, as used in many BPQ32 programs.", + IDC_STATIC,10,27,353,18 + LTEXT "The eMail Server Params configure the NNTP, SMTP and POP3 Servers, which allow normal Internet email and News clients to get messages from, and post mesages to the BBS. If you don't want to use this, set them to zero.. ", + IDC_STATIC,8,52,357,27 + LTEXT "Enable UI System activates FBB compatible UI broadcasting of Message Headers", + IDC_STATIC,8,80,357,15 + LTEXT "BBS Call",IDC_STATIC,7,105,37,12 + EDITTEXT IDC_BBSCall,83,103,47,14,ES_UPPERCASE + LTEXT "SYSOP Call",IDC_STATIC,138,105,42,12 + EDITTEXT IDC_SYSOPCALL,184,103,42,14,ES_UPPERCASE + CONTROL "Send SYSTEM Msgs to SYSOP call",IDC_SYSTOSYSOPCALL, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,231, + 104,129,12 + LTEXT "H Route",IDC_STATIC,7,122,37,12 + EDITTEXT IDC_HRoute,83,120,119,13,ES_UPPERCASE + LTEXT "BBS Appl Number",IDC_STATIC,7,138,61,10 + EDITTEXT IDC_BBSAppl,83,136,29,12 + LTEXT "Streams",IDC_STATIC,130,138,32,10 + EDITTEXT IDC_BBSStreams,168,136,29,12 + CONTROL "Refuse Bulls",IDC_REFUSEBULLS,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,211,138,53,10 + CONTROL "Enable FBB UI System",IDC_ENABLEUI,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,6,157,87,10 + LTEXT "Send Mail For Beacons Every",IDC_STATIC,102,157,100,12 + EDITTEXT MAILFOR_MINS,205,156,22,12,ES_UPPERCASE + LTEXT "Minutes",IDC_STATIC,232,157,30,12 + PUSHBUTTON "Config UI Ports and Digis",IDC_UICONFIG,273,155,91,13 + LTEXT "eMail Server Params:",IDC_STATIC,7,227,135,10 + LTEXT "NNTP Port",IDC_STATIC,7,243,39,12 + EDITTEXT IDC_NNTPPort,47,241,29,14 + LTEXT "POP3 Port",IDC_STATIC,90,243,37,12 + EDITTEXT IDC_POP3Port,130,241,29,14 + LTEXT "SMTP Port",IDC_STATIC,170,243,37,12 + EDITTEXT IDC_SMTPPort,210,241,29,14 + CONTROL "Enable Remote Access",1009,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,249,240,91,14 + PUSHBUTTON "Save",IDC_BBSSAVE,159,281,50,14,BS_CENTER | BS_VCENTER + CONTROL "Don't hold messages from new users",IDC_DONTHOLDNEW, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,5, + 189,134,12 + CONTROL "Forward Messages to BBS Call",IDC_FORWARDTOBBS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,145,189,114, + 12 + CONTROL "Redirect msgs to BBS Call to SYSOP Call", + IDC_BBSTOSYSOPCALL,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,213,120,146,12 + CONTROL "Don't Request Home BBS",IDC_NOHOMEBBS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,260,174,99,12 + CONTROL "Don't Request Name",IDC_NONAME,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,176,174,79,12 + EDITTEXT IDC_AMPR,72,258,119,13 + LTEXT "AMPR Address",IDC_STATIC,6,260,60,12 + CONTROL "Send ampr.org mail to AMPR host",IDC_FORWARDAMPR, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,258,124,14 + CONTROL "Allow users to kill T msgs",IDC_USERRKILLT,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,264,189,95,12 + CONTROL "Set Don't add WINLINK.ORG flag on new users", + IDC_DEFAULTNOWINLINK,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,5,174,162,12 + CONTROL "Don't allow unknown users",IDC_KNOWNUSERS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,277,138,98,10 + CONTROL "Don't Check From Calls",IDC_DONTCHECKFROM,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,5,204,95,12 +END + +CHAT_CONFIG DIALOG DISCARDABLE 0, 0, 381, 266 +STYLE WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Chat Appl Number",-1,115,109,61,8 + EDITTEXT 2001,185,106,29,14 + LTEXT "Nodes to link to",-1,115,129,53,8 + EDITTEXT 2002,185,125,87,88,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",2100,172,226,50,14,BS_CENTER | BS_VCENTER + LTEXT "The Application Number defines which BPQ32 Application gives access to the Chat Server. Note this is the APPLNumber (1-32) not an Application Mask, as uses in many BPQ32 programs.", + -1,10,10,353,18 + LTEXT "The Nodes to link to box defines which other Chat Nodes should be connected to, or from which connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. Note these must be directly connectable - ie in your NODES table.", + -1,10,30,355,25 + LTEXT "The Callsign of the Chat Node is not defined here - It is obtained from the BPQ32 APPLCALL parameter corresponding to the Chat Appl Number.", + -1,10,60,360,25 +END + +IDD_USEREDIT DIALOGEX 20, 20, 293, 281 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit User" +FONT 8, "System" +BEGIN + COMBOBOX 5000,7,10,57,123,CBS_SIMPLE | CBS_SORT | CBS_UPPERCASE | + WS_VSCROLL | WS_TABSTOP + CONTROL "BBS",IDC_BBSFLAG,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,9,57,10 + CONTROL "PMS",IDC_PMSFLAG,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,22,57,10 + CONTROL "Sysop",IDC_SYSOP,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,35,57,10 + CONTROL "Expert",IDC_EXPERT,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,48,57,10 + CONTROL "Excluded",IDC_EXCLUDED,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,72,61,57,10 + CONTROL "Hold Messages",IDC_HOLDMAIL,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,72,74,65,10 + CONTROL "NTS MPS",IDC_NTSMPS,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,87,69,10 + CONTROL "Permit Email",IDC_EMAIL,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,163,9,57,10 + CONTROL "Poll RMS for SSID's",IDC_POLLRMS,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,23,78,8,0, + HIDC_POLLRMS + EDITTEXT RMS_SSID1,244,21,13,12,ES_AUTOHSCROLL + EDITTEXT RMS_SSID2,258,21,13,12,ES_AUTOHSCROLL + EDITTEXT RMS_SSID3,272,21,13,12,ES_AUTOHSCROLL + CONTROL "RMS Express User",RMS_EXPRESS_USER,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,35,77,10 + LTEXT "Last Listed",IDC_STATIC,163,87,38,12 + EDITTEXT IDC_LASTLISTED,248,85,29,12,ES_AUTOHSCROLL + LTEXT "Name",IDC_STATIC,7,184,35,14 + EDITTEXT IDC_NAME,55,182,96,14,ES_AUTOHSCROLL + LTEXT "Password",IDC_STATIC,7,202,35,14 + EDITTEXT IDC_PASSWORD,55,200,89,14,ES_AUTOHSCROLL + LTEXT "QTH",IDC_STATIC,7,220,35,14 + EDITTEXT IDC_QTH,55,218,130,14,ES_AUTOHSCROLL + LTEXT "Home BBS",IDC_STATIC,7,238,40,14 + EDITTEXT IDC_HOMEBBS,55,236,162,14,ES_UPPERCASE | ES_AUTOHSCROLL + PUSHBUTTON "Add user",IDC_ADDUSER,79,257,42,14,BS_CENTER | + BS_VCENTER + PUSHBUTTON "Delete user",5101,124,257,42,14,BS_CENTER | BS_VCENTER + PUSHBUTTON "Save",5102,169,257,42,14,BS_CENTER | BS_VCENTER + LTEXT "Connects in",IDC_STATIC,10,131,45,10 + LTEXT "Connects out",IDC_STATIC,10,142,45,10 + LTEXT "0",CONN_IN,57,131,26,10,0,WS_EX_RIGHT + LTEXT "0",CONN_OUT,57,142,26,10,0,WS_EX_RIGHT + LTEXT "Msgs in",IDC_STATIC,87,131,35,10 + LTEXT "Msgs out",IDC_STATIC,87,142,35,10 + LTEXT "0",MSGS_IN,125,131,26,10,0,WS_EX_RIGHT + LTEXT "0",MSGS_OUT,125,142,26,10,0,WS_EX_RIGHT + LTEXT "Rejects in",IDC_STATIC,159,131,35,10 + LTEXT "Rejects out",IDC_STATIC,159,142,40,10 + LTEXT "0",REJECTS_IN,201,131,26,10,0,WS_EX_RIGHT + LTEXT "0",REJECTS_OUT,201,142,26,10,0,WS_EX_RIGHT + LTEXT "Bytes out",IDC_STATIC,10,164,35,10 + LTEXT "",BYTES_OUT,45,164,38,10,0,WS_EX_RIGHT + LTEXT "Bytes in",IDC_STATIC,10,153,35,10 + LTEXT "0",BYTES_IN,45,153,38,10,0,WS_EX_RIGHT + LTEXT "Last Connect",IDC_STATIC,87,153,45,10 + LTEXT "Never",LASTCONNECT,134,153,93,10 + CONTROL "Don't add @winlink.org",NO_WINLINKdotORG,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,47,100,10 + LTEXT "ZIP",IDC_STATIC,189,221,12,11 + EDITTEXT IDC_UZIP,207,219,38,12,ES_AUTOHSCROLL + CONTROL "Allow sending Bulls",ALLOW_BULLS,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,61,100,10 + CONTROL "Include SYSOP msgs in LM",IDC_SYSOP_IN_LM,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_DISABLED | WS_TABSTOP,163, + 74,100,10 + EDITTEXT IDC_CMSPASS,187,201,88,14,ES_PASSWORD + CONTROL "CMS Pass",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | + WS_GROUP,149,203,34,14 + CONTROL "Send Mail For to APRS ssid",IDC_APRSMFOR,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,72,113,108,10 + EDITTEXT IDC_APRSSSID,182,112,16,12,ES_AUTOHSCROLL + CONTROL "Redirect to RMS",IDC_RMSREDIRECT,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,72,100,69,10 +END + +ISP_CONFIG DIALOG DISCARDABLE 26, 5, 381, 284 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + EDITTEXT 3001,95,110,100,15 + LTEXT "SMTP Server",-1,10,130,53,13 + LTEXT "The system relies on having an email domain which supports forwarding of all email addresses to a fixed mailbox. For example, I could register domain mycall.org.uk, and have any mail sent to anyone@mycall.org.uk forwarded to mymailbox@myisp.com", + 1,10,34,349,25 + LTEXT "POP3 Server",-1,10,146,61,13 + LTEXT "ISP Account Name",-1,10,163,63,13 + EDITTEXT IDC_ISPSMTPName,95,127,148,15 + EDITTEXT 3004,95,144,147,15 + EDITTEXT 3006,95,161,147,15 + LTEXT "Port",-1,250,147,18,13 + LTEXT "Port",-1,250,130,19,13 + EDITTEXT 3005,275,144,29,15 + EDITTEXT 3003,275,127,29,15 + LTEXT "This page configures the BBS <> Internet Mail Gateway. The Gateway allows local users to send messages to Internet email addresses, and get replies from those messages", + -1,10,10,353,20 + LTEXT "My Domain",-1,10,112,51,8 + LTEXT "ISP Account Password",-1,10,180,80,13 + EDITTEXT 3007,95,178,147,15,ES_PASSWORD + LTEXT "POP3 Poll Interval (Seconds)",-1,10,216,80,18 + EDITTEXT 3008,95,219,29,12 + LTEXT "WARNING This feature my be illegal in some administrations. Make sure your authorities permit forwarding mail from the Amateur Service before enabling it", + -1,10,64,348,15 + CONTROL "Enable Internet Gateway",3000,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_LEFT | WS_TABSTOP,10,90,90,15 + DEFPUSHBUTTON "Save",3100,165,261,57,17,BS_CENTER | BS_VCENTER + CONTROL "SMTP Server Requires Authentication",ISP_SMTP_AUTH, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_LEFT | + BS_MULTILINE | WS_TABSTOP,250,159,69,32 + EDITTEXT SMTP_EHELO,95,196,100,15 + LTEXT "SMTP ""EHELO"" Domain",-1,10,198,80,8 +END + +IDD_FORWARDING DIALOG DISCARDABLE 20, 20, 457, 327 +STYLE WS_POPUP | WS_CAPTION | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Manage Forwarding" +FONT 8, "System" +BEGIN + LTEXT "Forwarding Rules are define here. BBS Records are created by setting the BBS flag on a User Record.", + IDC_STATIC,5,7,368,10 + LTEXT "The Enable Forwarding flag allows you to disable forwarding without losing the messages to be forwarded. ", + IDC_STATIC,5,20,373,8 + GROUPBOX "Global Params",IDC_STATIC,2,33,114,263 + LTEXT "Max Size to Send",IDC_STATIC,5,50,58,12 + EDITTEXT IDC_MAXSEND,66,49,26,12,ES_AUTOHSCROLL + LTEXT "Max Size to Recv",IDC_STATIC,5,68,58,12 + EDITTEXT IDC_MAXRECV,66,66,26,12,ES_AUTOHSCROLL + CONTROL "Warn if no route for P or T",IDC_WARNNOROUTE,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,101,103,8 + LTEXT "Aliases",IDC_STATIC,5,144,57,13 + EDITTEXT IDC_ALIAS,4,162,99,81,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + CONTROL "Readdress Locally Input",IDC_READDRESSLOCAL,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_DISABLED | WS_TABSTOP,4,246,97,8 + CONTROL "Readdress Received",IDC_READDRESSRXED,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_DISABLED | WS_TABSTOP,4,260,97,8 + GROUPBOX "Per-BBS Params",IDC_STATIC,121,33,326,263 + LTEXT "BBS",IDC_STATIC,128,46,57,10 + COMBOBOX IDC_BBS,122,59,50,60,CBS_SIMPLE | CBS_OEMCONVERT | + CBS_SORT | CBS_NOINTEGRALHEIGHT | WS_VSCROLL + LTEXT "To Calls:",IDC_STATIC,186,46,40,10 + EDITTEXT IDC_TOCALLS,177,59,50,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "AT Calls:",IDC_STATIC,242,46,43,10 + EDITTEXT IDC_ATCALLS,233,59,50,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Times",IDC_STATIC,299,46,30,13 + EDITTEXT IDC_FWDTIMES,288,59,50,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Connect Script",IDC_STATIC,348,46,57,13 + EDITTEXT IDC_CALL,343,59,92,60,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + LTEXT "Hierarchical Routes (Flood Bulls)",IDC_STATIC,141,126, + 114,12 + EDITTEXT IDC_HROUTES,122,140,148,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL + LTEXT "HR (Personals and Directed Bulls)",IDC_STATIC,281,126, + 125,12 + EDITTEXT IDC_HROUTESP,280,141,151,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL + LTEXT "Interval (Secs)",IDC_STATIC,359,205,48,8 + EDITTEXT IDC_FWDINT,253,204,22,12,ES_AUTOHSCROLL + CONTROL "Enable Forwarding",4000,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_LEFT | WS_TABSTOP,122,206,72,8 + CONTROL "Request Reverse",4006,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_LEFT | WS_TABSTOP,286,206,67,8 + CONTROL "Send new messages without waiting for poll timer", + IDC_SENDNEW,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | + BS_MULTILINE | WS_TABSTOP,122,221,182,8 + LTEXT "BBS HA",IDC_STATIC,125,242,30,12 + EDITTEXT IDC_BBSHA,162,240,134,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Max Block",IDC_STATIC,368,241,37,12 + EDITTEXT IDC_MAXBLOCK,407,240,26,12,ES_AUTOHSCROLL + CONTROL "Send Personal Mail Only",IDC_PERSONALONLY,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,321,221,108,8 + CONTROL "Allow Binary",IDC_ALLOWCOMP,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,125,260,58,8 + CONTROL "Use B1 Protocol",IDC_USEB1,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,185,260,72,8 + CONTROL "Use B2 Protocol",IDC_USEB2,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,261,260,67,8 + DEFPUSHBUTTON "? Help",IDC_HRHELP,381,6,33,11,BS_CENTER | BS_VCENTER | + NOT WS_TABSTOP + DEFPUSHBUTTON "Save",4100,352,303,57,17,BS_CENTER | BS_VCENTER + LTEXT "Interval (Secs)",IDC_STATIC,198,206,48,8 + EDITTEXT IDC_REVFWDINT,407,205,23,12,ES_AUTOHSCROLL + CONTROL "Use Local Time ",IDC_USELOCALTIME,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,116,103,8 + CONTROL "Send ctrl/Z instead of /ex",IDC_CTRLZ,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,333,260,100,8 + LTEXT "Max Bull Age",IDC_STATIC,5,86,58,12 + EDITTEXT IDC_MAXAGE,66,84,26,12,ES_AUTOHSCROLL + CONTROL "FBB Blocked",IDC_BLOCKED,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,301,241,61,8 + PUSHBUTTON "Copy From BBS",COPYCONFIG,160,303,57,17,BS_CENTER | + BS_VCENTER + EDITTEXT COPYFROMCALL,227,303,35,17,ES_UPPERCASE | ES_AUTOHSCROLL + CONTROL "Send P to more than 1 BBS",IDC_MULTIP,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,130,103,8 + LTEXT "Incoming Connect Timeout",IDC_STATIC,125,278,95,12 + EDITTEXT IDC_CONTIMEOUT,219,276,22,12,ES_AUTOHSCROLL +END + +IDD_USERADDED_BOX DIALOG DISCARDABLE 176, 132, 129, 68 +STYLE DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Ok",0,47,48,36,17,BS_CENTER | BS_VCENTER + LTEXT "Label0",5050,5,10,117,32 +END + +IDD_MSGEDIT DIALOGEX 20, 20, 421, 298 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit Message" +FONT 8, "System", 0, 0, 0x1 +BEGIN + RTEXT "Msg",IDC_STATIC,7,7,19,8 + LISTBOX 0,33,7,65,81,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_MSGTYPE,125,9,25,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_MSGSTATUS,185,9,25,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + RTEXT "From",IDC_STATIC,7,98,19,8 + EDITTEXT 6001,33,95,65,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "BID",IDC_STATIC,7,114,19,8 + EDITTEXT 6002,33,111,65,14,ES_AUTOHSCROLL + RTEXT "To",IDC_STATIC,7,132,19,8 + EDITTEXT 6003,33,128,65,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "Via",IDC_STATIC,4,190,25,8 + EDITTEXT 6004,33,186,177,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "Subject",IDC_STATIC,4,206,25,8 + EDITTEXT 6005,33,203,177,14,ES_AUTOHSCROLL + LTEXT "Status",IDC_STATIC,160,10,20,8 + RTEXT "Sent",IDC_STATIC,116,96,32,8 + EDITTEXT 6018,153,95,57,14,ES_AUTOHSCROLL + RTEXT "Received",IDC_STATIC,116,113,32,8 + EDITTEXT 6019,153,111,57,14,ES_AUTOHSCROLL + RTEXT "Last Changed",IDC_STATIC,103,130,45,8 + EDITTEXT 6021,153,128,57,14,ES_AUTOHSCROLL + RTEXT "Size",IDC_STATIC,116,147,32,8 + EDITTEXT 6020,153,145,55,14,ES_AUTOHSCROLL + LTEXT "Type",IDC_STATIC,105,10,17,8 + CONTROL "",25,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,7,35,10 + CONTROL "",26,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,17,35,10 + CONTROL "",27,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,27,35,10 + CONTROL "",28,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,37,35,10 + CONTROL "",29,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,47,35,10 + CONTROL "",30,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,57,35,10 + CONTROL "",31,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,67,35,10 + CONTROL "",32,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,77,35,10 + CONTROL "",33,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,87,35,10 + CONTROL "",34,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,97,35,10 + CONTROL "",35,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,107,35,10 + CONTROL "",36,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,117,35,10 + CONTROL "",37,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,127,35,10 + CONTROL "",38,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,137,35,10 + CONTROL "",39,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,147,35,10 + CONTROL "",40,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,157,35,10 + CONTROL "",41,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,167,35,10 + CONTROL "",42,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,177,35,10 + CONTROL "",43,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,187,35,10 + CONTROL "",44,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,197,35,10 + CONTROL "",45,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,208,35,10 + CONTROL "",46,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,218,35,10 + CONTROL "",47,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,228,35,10 + CONTROL "",48,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,238,35,10 + CONTROL "",49,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,248,35,10 + CONTROL "",50,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,7,35,10 + CONTROL "",51,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,17,35,10 + CONTROL "",52,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,27,35,10 + CONTROL "",53,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,37,35,10 + CONTROL "",54,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,47,35,10 + CONTROL "",55,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,57,35,10 + CONTROL "",56,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,67,35,10 + CONTROL "",57,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,77,35,10 + CONTROL "",58,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,87,35,10 + CONTROL "",59,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,97,35,10 + CONTROL "",60,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,107,35,10 + CONTROL "",61,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,117,35,10 + CONTROL "",62,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,127,35,10 + CONTROL "",63,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,137,35,10 + CONTROL "",64,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,147,35,10 + CONTROL "",65,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,157,35,10 + CONTROL "",66,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,167,35,10 + CONTROL "",67,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,177,35,10 + CONTROL "",68,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,187,35,10 + CONTROL "",69,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,197,35,10 + CONTROL "",70,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,207,35,10 + CONTROL "",71,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,217,35,10 + CONTROL "",72,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,227,35,10 + CONTROL "",73,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,237,35,10 + CONTROL "",74,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,247,35,10 + CONTROL "",75,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,7,35,10 + CONTROL "",76,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,17,35,10 + CONTROL "",77,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,27,35,10 + CONTROL "",78,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,37,35,10 + CONTROL "",79,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,47,35,10 + CONTROL "",80,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,57,35,10 + CONTROL "",81,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,67,35,10 + CONTROL "",82,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,77,35,10 + CONTROL "",83,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,87,35,10 + CONTROL "",84,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,97,35,10 + CONTROL "",85,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,107,35,10 + CONTROL "",86,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,117,35,10 + CONTROL "",87,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,127,35,10 + CONTROL "",88,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,137,35,10 + CONTROL "",89,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,147,35,10 + CONTROL "",90,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,157,35,10 + CONTROL "",91,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,167,35,10 + CONTROL "",92,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,177,35,10 + CONTROL "",93,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,187,35,10 + CONTROL "",94,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,197,35,10 + CONTROL "",95,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,207,35,10 + CONTROL "",96,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,217,35,10 + CONTROL "",97,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,227,35,10 + CONTROL "",98,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,237,35,10 + CONTROL "",99,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,247,35,10 + CONTROL "",100,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,7,35,10 + CONTROL "",101,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,18,35,10 + CONTROL "",102,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,27,35,10 + CONTROL "",103,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,37,35,10 + CONTROL "",104,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,47,35,10 + LTEXT "Key",IDC_STATIC,317,267,35,9 + CONTROL "Dont Send",205,"Button",BS_3STATE | BS_CENTER | + BS_PUSHLIKE,262,281,43,10 + CONTROL "Send",206,"Button",BS_3STATE | BS_CENTER | BS_PUSHLIKE, + 311,280,35,10 + CONTROL "Sent",207,"Button",BS_3STATE | BS_CENTER | BS_PUSHLIKE, + 353,280,35,10 + DEFPUSHBUTTON "Edit Text",IDC_EDITTEXT,24,275,38,15,BS_CENTER | + BS_VCENTER + DEFPUSHBUTTON "Print",IDC_PRINTMSG,150,275,24,15,BS_CENTER | + BS_VCENTER + RTEXT "Filter From",IDC_STATIC,104,30,38,8 + EDITTEXT FILTER_FROM,147,28,62,14,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT FILTER_TO,147,44,62,14,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT FILTER_VIA,147,60,62,14,ES_UPPERCASE | ES_AUTOHSCROLL,0, + HFILTER_VIA + RTEXT "Filter To",IDC_STATIC,104,46,38,8 + RTEXT "Filter Via",IDC_STATIC,104,62,38,8 + DEFPUSHBUTTON "Save",IDC_SAVEMSG,67,275,24,15,BS_CENTER | BS_VCENTER + DEFPUSHBUTTON "Save to File",IDC_SAVETOFILE,96,275,49,15,BS_CENTER | + BS_VCENTER + DEFPUSHBUTTON "Export",IDC_EXPORT,179,275,32,15,BS_CENTER | BS_VCENTER + EDITTEXT EMAILFROM,33,169,177,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "Email From",IDC_STATIC,4,168,25,17 + CONTROL "",105,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,57,35,10 + CONTROL "",106,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,67,35,10 + CONTROL "",107,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,77,35,10 + CONTROL "",108,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,87,35,10 + CONTROL "",109,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,97,35,10 + CONTROL "",110,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,107,35,10 + CONTROL "",111,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,118,35,10 + CONTROL "",112,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,127,35,10 + CONTROL "",113,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,137,35,10 + CONTROL "",114,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,147,35,10 + CONTROL "",115,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,157,35,10 + CONTROL "",116,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,167,35,10 + CONTROL "",117,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,177,35,10 + CONTROL "",118,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,187,35,10 + CONTROL "",119,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,197,35,10 + CONTROL "",120,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,207,35,10 + CONTROL "",121,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,217,35,10 + CONTROL "",122,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,227,35,10 + CONTROL "",123,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,237,35,10 + CONTROL "",124,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,247,35,10 + CONTROL "",148,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,237,35,10 + CONTROL "",125,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,8,35,10 + CONTROL "",126,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,17,35,10 + CONTROL "",127,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,27,35,10 + CONTROL "",128,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,37,35,10 + CONTROL "",129,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,47,35,10 + CONTROL "",130,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,57,35,10 + CONTROL "",131,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,67,35,10 + CONTROL "",132,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,77,35,10 + CONTROL "",133,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,87,35,10 + CONTROL "",134,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,97,35,10 + CONTROL "",135,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,108,35,10 + CONTROL "",136,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,117,35,10 + CONTROL "",137,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,127,35,10 + CONTROL "",138,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,137,35,10 + CONTROL "",139,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,147,35,10 + CONTROL "",140,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,157,35,10 + CONTROL "",141,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,167,35,10 + CONTROL "",150,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,247,35,10 + CONTROL "",142,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,177,35,10 + CONTROL "",143,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,187,35,10 + CONTROL "",144,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,197,35,10 + CONTROL "",145,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,207,35,10 + CONTROL "",146,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,217,35,10 + CONTROL "",147,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,227,35,10 + EDITTEXT FILTER_BID,147,76,62,14,ES_UPPERCASE | ES_AUTOHSCROLL,0, + HFILTER_BID + RTEXT "Filter BID",IDC_STATIC,104,78,38,8 +END + +WELCOMEMSG DIALOG DISCARDABLE 26, 5, 365, 281 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Normal User Welcome Message",IDC_STATIC,5,7,130,8 + EDITTEXT IDM_USERMSG,5,20,340,50,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "New User Welcome Message",IDC_STATIC,5,75,130,8 + EDITTEXT IDM_NEWUSERMSG,5,89,340,65,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "Expert User Welcome Message",IDC_STATIC,5,159,130,8 + EDITTEXT IDM_EXPERTUSERMSG,5,172,340,12,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_MSGSAVE,166,264,50,14,BS_CENTER | BS_VCENTER + LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages", + IDC_STATIC,0,222,365,8 + LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user", + IDC_STATIC,0,234,365,10 + LTEXT "Signoff Message",IDC_STATIC,5,190,130,8 + EDITTEXT IDM_SIGNOFF,5,202,340,12,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "$F : Number of mesages to be forwarded to user (if a BBS)", + IDC_STATIC,0,246,365,10 +END + +BBSPROMPTS DIALOG DISCARDABLE 26, 5, 381, 266 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Normal User Prompt",IDC_STATIC,5,7,130,8 + EDITTEXT IDM_USERMSG,5,20,340,35,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "New User Prompt",IDC_STATIC,5,59,130,8 + EDITTEXT IDM_NEWUSERMSG,5,70,340,35,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "Expert User Prompt",IDC_STATIC,5,110,130,8 + EDITTEXT IDM_EXPERTUSERMSG,5,120,340,35,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_PROMPTSAVE,166,250,50,14,BS_CENTER | + BS_VCENTER + LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages", + IDC_STATIC,2,165,353,8 + LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user", + IDC_STATIC,3,175,347,10 +END + +IDD_MAINTRESULTS DIALOG DISCARDABLE 0, 0, 181, 114 +STYLE DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME +CAPTION "Maintenance Results" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,66,91,50,14 + LTEXT "Killed Messsages Removed",IDC_STATIC,11,10,103,10 + LTEXT "Messages Killed",IDC_STATIC,11,21,103,10 + LTEXT "Live Messages",IDC_STATIC,11,32,104,10 + LTEXT "Total Messages",IDC_STATIC,11,43,103,10 + LTEXT "BIDs Removed",IDC_STATIC,11,58,103,10 + LTEXT "BIDs Left",IDC_STATIC,11,70,103,10 + LTEXT "Static",IDC_REMOVED,146,10,30,10 + LTEXT "Static",IDC_KILLED,146,21,30,10 + LTEXT "Static",IDC_LIVE,146,32,30,10 + LTEXT "Static",IDC_TOTAL,146,43,30,10 + LTEXT "Static",IDC_BIDSREMOVED,146,58,30,10 + LTEXT "Static",IDC_BIDSLEFT,146,69,30,10 +END + +IDD_EDITWP DIALOG DISCARDABLE 0, 0, 398, 265 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "EDIT WP Record" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_WP,9,9,76,190,CBS_SIMPLE | CBS_SORT | CBS_UPPERCASE | + WS_VSCROLL | WS_TABSTOP + LTEXT "Name",-1,100,11,47,10 + EDITTEXT IDC_WPNAME,165,10,128,12,ES_AUTOHSCROLL + LTEXT "Home BBS 1",-1,100,26,53,10 + EDITTEXT IDC_HOMEBBS1,165,25,216,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Home BBS 2",-1,100,41,53,10 + EDITTEXT IDC_HOMEBBS2,165,40,216,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "QTH 1",-1,100,56,47,10 + EDITTEXT IDC_QTH1,165,55,215,12,ES_AUTOHSCROLL + LTEXT "QTH 2",-1,100,71,47,10 + EDITTEXT IDC_QTH2,165,70,215,12,ES_AUTOHSCROLL + LTEXT "ZIP 1",-1,100,86,47,12 + EDITTEXT IDC_ZIP1,165,85,128,12,ES_AUTOHSCROLL + LTEXT "ZIP 2",-1,100,101,47,12 + EDITTEXT IDC_ZIP2,165,100,128,12,ES_AUTOHSCROLL + LTEXT "Last Seen",-1,100,121,47,12 + EDITTEXT IDC_LASTSEEN,165,120,128,12,ES_AUTOHSCROLL + LTEXT "Last Modified",-1,100,136,47,12 + EDITTEXT IDC_LASTMODIFIED,165,135,128,12,ES_AUTOHSCROLL + LTEXT "Type",-1,100,156,47,12 + EDITTEXT IDC_TYPE,165,155,59,12,ES_AUTOHSCROLL + LTEXT "Changed",-1,100,171,47,12 + LTEXT "Seen",-1,100,186,47,12 + EDITTEXT IDC_CHANGED,165,170,59,12,ES_AUTOHSCROLL + EDITTEXT IDC_SEEN,165,185,59,12,ES_AUTOHSCROLL + PUSHBUTTON "Delete",IDC_DELETEWP,106,215,56,17,BS_CENTER | + BS_VCENTER + PUSHBUTTON "Save",IDC_SAVEWP,171,216,56,17,BS_CENTER | BS_VCENTER +END + +IDD_UICONFIG DIALOG DISCARDABLE 0, 0, 400, 151 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "UI Configuration" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Save",IDOK,7,98,50,14 + PUSHBUTTON "Cancel",IDCANCEL,342,98,50,14 + LTEXT "Mailfor Header",IDC_STATIC,5,5,50,10 + EDITTEXT IDC_MAILFOR,61,3,331,12,ES_AUTOHSCROLL + LTEXT "Send: Mail For Headers Null Mailfor",IDC_STATIC,242, + 25,127,10 + LTEXT "(use \\r to insert newline in message)",IDC_STATIC,62, + 16,125,10 +END + +IDD_CHATCOLCONFIG DIALOG DISCARDABLE 0, 0, 224, 120 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Chat Colour Configuration" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Save",IDOK,50,95,50,14 + PUSHBUTTON "Close",IDCANCEL,120,95,50,14 + COMBOBOX IDC_CHATCALLS,10,5,100,60,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CHATCOLOURS,115,5,100,60,CBS_DROPDOWNLIST | + CBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP +END + +IDD_UPDATECHATMAP DIALOG DISCARDABLE 0, 0, 274, 146 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Update Chat Map" +FONT 8, "System" +BEGIN + LTEXT "Click Help for full information about the Chat Network Map", + IDC_STATIC,5,10,195,15 + DEFPUSHBUTTON "? Help",IDC_MAPHELP,220,10,33,11,BS_CENTER | BS_VCENTER + LTEXT "Position",IDC_STATIC,5,30,35,10 + EDITTEXT IDC_MAPPOSITION,5,45,145,15,ES_AUTOHSCROLL + LTEXT "Popup Box Text. ",IDC_STATIC,5,65,70,9 + EDITTEXT IDC_POPUPTEXT,5,80,200,35,ES_MULTILINE | ES_AUTOHSCROLL | + WS_HSCROLL + LTEXT "Popup Mode",IDC_STATIC,215,80,49,10 + CONTROL "Hover",IDC_HOVER,"Button",BS_AUTORADIOBUTTON,219,92,45, + 10 + CONTROL "Click",IDC_CLICK,"Button",BS_AUTORADIOBUTTON,219,104,45, + 10 + DEFPUSHBUTTON "Send to Map System",IDSENDTOMAP,5,125,90,14 + DEFPUSHBUTTON "Save",IDOK,100,125,35,14 + PUSHBUTTON "Cancel",IDCANCEL,140,125,40,14 +END + +IDD_MSGFROMCLIPBOARD DIALOGEX 0, 0, 442, 306 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Send Message" +FONT 8, "System", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Send",IDSEND,333,5,50,14 + PUSHBUTTON "Cancel",IDCANCEL,333,25,50,14 + EDITTEXT IDC_EDIT1,4,57,431,245,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL, + WS_EX_ACCEPTFILES + LTEXT "To:",IDC_STATIC,66,8,12,8 + EDITTEXT IDC_MSGTO,83,7,147,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Title:",IDC_STATIC,8,26,22,8 + EDITTEXT IDC_MSGTITLE,50,24,277,12,ES_AUTOHSCROLL + LTEXT "BID:",IDC_STATIC,240,8,20,8 + EDITTEXT IDC_MSGBID,262,7,65,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Type",IDC_STATIC,8,8,20,8 + COMBOBOX IDC_MSGTYPE,34,7,25,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Select",IDSelectFiles,333,42,50,14 + LTEXT "Attachments",IDC_STATIC,8,43,42,8 + EDITTEXT IDC_ATTACHMENTS,50,41,277,12,ES_AUTOHSCROLL +END + +IDD_HRHELP DIALOG DISCARDABLE 0, 0, 415, 182 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hierarchical Forwarding Help" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,182,158,50,14 + EDITTEXT IDC_HRTEXT,4,4,405,150,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + +IDD_EDITMSGTEXT DIALOGEX 0, 0, 492, 356 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Edit Message" +FONT 8, "Fixedsys", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_MESSAGE,4,25,480,327,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Save",IDSAVE,5,5,40,17 + PUSHBUTTON "Cancel",IDCANCEL,125,5,40,17 + PUSHBUTTON "Save Attachments",IDC_SAVEATTACHMENTS,50,5,70,17, + WS_DISABLED +END + +MAINT DIALOG DISCARDABLE 26, 5, 382, 298 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "This sets the parameters for deleting old messages. Specify length of time (in days) for messages to be kept before being deleted", + IDC_STATIC,149,13,226,22 + LTEXT "Read",IDC_STATIC,159,46,46,10 + EDITTEXT IDM_PR,224,46,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Unread",IDC_STATIC,159,61,46,10 + EDITTEXT IDM_PUR,224,61,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Forwarded",IDC_STATIC,159,76,46,10 + EDITTEXT IDM_PF,224,76,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Not Forwarded",IDC_STATIC,159,91,49,10 + EDITTEXT IDM_PNF,224,91,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Forwarded",IDC_STATIC,264,46,49,10 + EDITTEXT IDM_BF,324,46,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Not Forwarded",IDC_STATIC,264,61,49,10 + EDITTEXT IDM_BNF,324,61,20,12,ES_CENTER | ES_AUTOHSCROLL + GROUPBOX "Personals",IDC_STATIC,149,36,108,107 + GROUPBOX "Bulletins",IDC_STATIC,259,36,103,44 + LTEXT "The following boxes allow you to specify different values for different Bulletin origins and destinations. Normally these apply to Sent Messages, to apply to unsent, check box below", + IDC_STATIC,151,148,214,27 + LTEXT "Specify Call, Lifetime, eg ALL, 10",IDC_STATIC,151,176, + 154,8 + LTEXT "From",IDC_STATIC,176,189,28,9 + EDITTEXT IDM_LTFROM,151,202,64,55,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "To",IDC_STATIC,247,189,27,10 + EDITTEXT IDM_LTTO,221,202,64,55,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "At",IDC_STATIC,316,189,15,8 + EDITTEXT IDM_LTAT,289,202,64,55,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_MAINTSAVE,166,279,50,14,BS_CENTER | + BS_VCENTER + GROUPBOX "Lifetimes",IDC_STATIC,144,0,231,275 + GROUPBOX "Parameters",IDC_STATIC,3,3,133,275 + LTEXT "Max Message Number",IDC_STATIC,7,68,77,10 + LTEXT "BID Lifetime (Days)",IDC_STATIC,7,87,77,10 + LTEXT "Maintenance Time (UTC) Enter as HHMM",IDC_STATIC,7,21, + 83,25 + EDITTEXT IDC_MAXMSG,102,67,25,12,ES_CENTER + EDITTEXT IDC_BIDLIFETIME,102,86,25,12,ES_CENTER | ES_AUTOHSCROLL + EDITTEXT IDC_MAINTTIME,102,23,25,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Log File Lifetime (Days)",IDC_STATIC,7,107,80,10 + EDITTEXT IDC_LOGLIFETIME,102,105,25,12,ES_CENTER | ES_AUTOHSCROLL + CONTROL "Delete Log and Message Files to Recycle Bin", + IDC_DELETETORECYCLE,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,5,142,115,20 + CONTROL "Supress Mailing of Housekeeping Results", + IDC_MAINTNOMAIL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | + BS_MULTILINE | WS_TABSTOP,5,182,115,20 + CONTROL "Generate Traffic Report",IDC_MAINTTRAFFIC,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,204,115,10 + CONTROL "Send Non-delivery Notifications for P and T messages", + IDC_MAINTNONDELIVERY,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,5,162,115,20 + CONTROL "Apply Overrides to Unsent Bulls",IDC_OVERRIDEUNSENT, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,153,261,195,10 + LTEXT "Delete Inactive Users (Days)",IDC_STATIC,7,127,93,10 + EDITTEXT IDC_USERLIFETIME,102,125,25,12,ES_CENTER | + ES_AUTOHSCROLL + GROUPBOX "NTS",IDC_STATIC,259,80,102,62 + LTEXT "Forwarded",IDC_STATIC,264,109,49,10 + EDITTEXT IDM_NTSF,324,108,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Not Forwarded",IDC_STATIC,264,124,49,10 + EDITTEXT IDM_NTSU,324,123,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Delivered",IDC_STATIC,264,94,49,10 + EDITTEXT IDM_NTSD,324,94,20,12,ES_CENTER | ES_AUTOHSCROLL + CONTROL "Save Registry",IDC_MAINTSAVEREG2,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,222,115,10 + LTEXT "Maintenance Interval (Hrs)",IDC_STATIC,7,47,93,10 + EDITTEXT IDC_MAINTINTERVAL,102,45,25,12,ES_CENTER | + ES_AUTOHSCROLL +END + +FILTERS DIALOG DISCARDABLE 26, 5, 382, 287 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Reject Messages:",IDC_STATIC,162,29,70,10 + LTEXT "From",IDC_STATIC,83,155,28,10 + EDITTEXT IDC_HOLDFROM,58,167,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "To",IDC_STATIC,152,155,27,10 + EDITTEXT IDC_HOLDTO,126,167,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "At",IDC_STATIC,223,155,15,10 + EDITTEXT IDC_HOLDAT,194,167,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDC_FILTERSAVE,171,266,50,14,BS_CENTER | + BS_VCENTER + LTEXT "From",IDC_STATIC,83,40,28,10 + EDITTEXT IDC_REJFROM,58,52,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "To",IDC_STATIC,154,40,27,10 + EDITTEXT IDC_REJTO,126,52,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "At",IDC_STATIC,223,40,15,10 + EDITTEXT IDC_REJAT,194,52,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Message Filtering Setup.",IDC_STATIC,152,10,95,15 + LTEXT "Hold Messages:",IDC_STATIC,166,143,60,9 + EDITTEXT IDC_REJBID,262,52,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + EDITTEXT IDC_HOLDBID,262,167,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "BID",IDC_STATIC,289,155,15,10 + LTEXT "BID",IDC_STATIC,289,41,15,10 +END + +WPUPDATE DIALOG DISCARDABLE 26, 5, 382, 287 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + CONTROL "Send WP Updates",IDC_SENDWP,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,90,18,71,10 + EDITTEXT IDC_WPTO,112,82,154,113,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + COMBOBOX IDC_WPTYPE,193,17,19,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Type",IDC_STATIC,172,18,21,10 + DEFPUSHBUTTON "Save",IDC_WPSAVE,165,204,50,14,BS_CENTER | BS_VCENTER + CONTROL "Reject WP Bulls",IDC_FILTERWPB,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,222,18,71,10 + LTEXT "Send To.\rBPQMail only automatically processes messages with TO of WP, so for P messages format should be WP@BBSCALL. ", + IDC_STATIC,98,38,176,35 +END + +IDD_RMSBULLDLG DIALOG DISCARDABLE 0, 0, 277, 258 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RMS Express Bull Configuration" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,66,237,50,14 + PUSHBUTTON "Cancel",IDCANCEL,164,237,50,14 + EDITTEXT IDC_TOCALLS,82,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_TOCALLS2,144,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_TOCALLS3,208,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_TOCALLS4,18,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Common",IDC_STATIC,63,47,48,8 + LTEXT "User Specific",IDC_STATIC,174,47,48,8 + LTEXT "TO AT",IDC_STATIC,39,62,79,8 + LTEXT "TO AT",IDC_STATIC,163,62,79, + 8 + LTEXT "This defines Bulls that will be downloaded to RMS Express Users. There are two lists, one for all RMS Express users and one per User. The lists will be combined, Updating the Common list will affect all users.", + IDC_STATIC,7,7,242,37 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO MOVEABLE PURE +BEGIN + 102, DIALOG + BEGIN + TOPMARGIN, 47 + END + + "BBS_CONFIG", DIALOG + BEGIN + BOTTOMMARGIN, 303 + HORZGUIDE, 87 + END + + IDD_USEREDIT, DIALOG + BEGIN + RIGHTMARGIN, 292 + BOTTOMMARGIN, 280 + END + + IDD_FORWARDING, DIALOG + BEGIN + RIGHTMARGIN, 444 + BOTTOMMARGIN, 314 + END + + IDD_USERADDED_BOX, DIALOG + BEGIN + BOTTOMMARGIN, 65 + END + + IDD_MSGEDIT, DIALOG + BEGIN + RIGHTMARGIN, 415 + BOTTOMMARGIN, 266 + END + + "WELCOMEMSG", DIALOG + BEGIN + BOTTOMMARGIN, 266 + END + + IDD_MAINTRESULTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 106 + END + + IDD_EDITWP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 391 + TOPMARGIN, 9 + BOTTOMMARGIN, 258 + END + + IDD_UICONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 392 + TOPMARGIN, 2 + BOTTOMMARGIN, 144 + END + + IDD_MSGFROMCLIPBOARD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 435 + TOPMARGIN, 7 + END + + IDD_HRHELP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 408 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_EDITMSGTEXT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 485 + TOPMARGIN, 7 + BOTTOMMARGIN, 349 + END + + "MAINT", DIALOG + BEGIN + RIGHTMARGIN, 381 + BOTTOMMARGIN, 266 + END + + "FILTERS", DIALOG + BEGIN + RIGHTMARGIN, 377 + END + + IDD_RMSBULLDLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 267 + TOPMARGIN, 7 + BOTTOMMARGIN, 251 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_BPQMailChat MENU DISCARDABLE +BEGIN + POPUP "Actions" + BEGIN + POPUP "Start Forwarding" + BEGIN + MENUITEM "All", IDM_FORWARD_ALL + END + POPUP "Logging Options" + BEGIN + MENUITEM "Log BBS Traffic", IDM_LOGBBS + MENUITEM "Log TCP Traffic", IDM_LOGTCP + END + POPUP "Disconnect User" + BEGIN + MENUITEM ".", IDM_DISCONNECT + END + MENUITEM "Housekeeping", IDM_HOUSEKEEPING + MENUITEM "Send Message", ID_ACTIONS_SENDMESSAGE + MENUITEM "Send Msg from Clipboard", ID_ACTIONS_SENDMSGFROMCLIPBOARD + + MENUITEM "Rerun Message Routing", RESCANMSGS + MENUITEM "Import Messages", IDM_IMPORT + END + POPUP "&Configuration" + BEGIN + MENUITEM "Main &Configuration", IDM_CONFIG + MENUITEM "Manage &Users", IDM_USERS + MENUITEM "Manage &Forwarding", IDM_FWD + MENUITEM "Manage &Messages", IDM_MESSAGES + MENUITEM "Manage &White Pages", IDM_WP + END + POPUP "Windows" + BEGIN + MENUITEM "BBS Console (F2)", IDM_CONSOLE + MENUITEM "Multicast Monitor (F3)", IDM_MCMONITOR + MENUITEM "Monitor (F4)", IDM_MONITOR + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + MENUITEM "Online Documentation", ID_HELP_ONLINEHELP + END +END + +CONSOLEMENU MENU DISCARDABLE +BEGIN + POPUP "Options" + BEGIN + MENUITEM "Enable Bells", BPQBELLS + MENUITEM "Flash instead of Beep on Bell", BPQFLASHONBELL + MENUITEM "Strip Linefeeds", BPQStripLF + MENUITEM "Wrap Input ", IDM_WRAPTEXT + MENUITEM "Beep if Input too long", IDM_WARNINPUT + MENUITEM "Close Window on exit", IDM_CLOSEWINDOW + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Output Window", BPQCOPYOUT + MENUITEM "Clear Output Window", BPQCLEAROUT + END + POPUP "Actions" + BEGIN + POPUP "Chat to user" + BEGIN + MENUITEM ".", BBSUSERCHAT + END + MENUITEM "End user chat", ENDUSERCHAT + END +END + +MENU_2 MENU DISCARDABLE +BEGIN + POPUP "Monitor" + BEGIN + MENUITEM "Monitor BBS", MONBBS + MENUITEM "Monitor TCP", MONTCP + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYOUT + MENUITEM "Clear Monitor Window", BPQCLEAROUT + END +END + +MENU_3 MENU DISCARDABLE +BEGIN + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYOUT + MENUITEM "Clear Monitor Window", BPQCLEAROUT + END +END + +MENU_1 MENU DISCARDABLE +BEGIN + POPUP "Edit" + BEGIN + MENUITEM "Copy", ID_EDIT_COPY + MENUITEM "Clear", ID_EDIT_CLEAR + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_TELNETSERVER ACCELERATORS MOVEABLE PURE +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE MOVEABLE PURE +BEGIN + "BPQMailrc.h\0" +END + +2 TEXTINCLUDE MOVEABLE PURE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""AFXRES.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#define MAILCHAT\r\n" + "#include ""..\\CommonSource\\Versions.h""\r\n" + "\0" +END + +3 TEXTINCLUDE MOVEABLE PURE +BEGIN + "#define MAIL\r\n" + "#include ""Versions.h""\r\n" + "#include ""StdVer.inc""\r\n" + "\0" +END + +1 TEXTINCLUDE MOVEABLE PURE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_USEREDIT AFX_DIALOG_LAYOUT MOVEABLE PURE +BEGIN + 0x0000 +END + +IDD_FORWARDING AFX_DIALOG_LAYOUT MOVEABLE PURE +BEGIN + 0x0000 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "BPQMail" + IDC_BPQMailChat "BPQMail" +END + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define MAIL +#include "Versions.h" +#include "StdVer.inc" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/BPQMail.vcproj b/BPQMail.vcproj new file mode 100644 index 0000000..3c7ef20 --- /dev/null +++ b/BPQMail.vcproj @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user b/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40182c4 --- /dev/null +++ b/BPQMail.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQMail.vcproj.HPLAPTOP.johnw.user b/BPQMail.vcproj.HPLAPTOP.johnw.user new file mode 100644 index 0000000..f361c86 --- /dev/null +++ b/BPQMail.vcproj.HPLAPTOP.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQMail.vcproj.SKIGACER.johnw.user b/BPQMail.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..b5b0536 --- /dev/null +++ b/BPQMail.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQMailConfig.c b/BPQMailConfig.c new file mode 100644 index 0000000..594881a --- /dev/null +++ b/BPQMailConfig.c @@ -0,0 +1,3821 @@ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Configuration Module + +#include "bpqmail.h" + + +#define C_PAGES 7 + +int CurrentPage=0; // Page currently on show in tabbed Dialog + +#define BBSPARAMS 0 +#define ISPPARAMS 1 +#define MAINTPARAMS 2 +#define WELCOMEMSGS 3 +#define PROMPTS 4 +#define FILTERS 5 +#define WPUPDATE 6 + +typedef struct tag_dlghdr { + +HWND hwndTab; // tab control +HWND hwndDisplay; // current child dialog box +RECT rcDisplay; // display rectangle for the tab control + + +DLGTEMPLATE *apRes[C_PAGES]; + +} DLGHDR; + +HWND hwndDlg; // Config Dialog +HWND hwndDisplay; // Current child dialog box + +HWND hCheck[33]; +HWND hNullCheck[33]; +HWND hSendMF[33]; +HWND hSendHDDR[33]; +HWND hLabel[33]; +HWND hUIBox[33]; +HFONT hFont; +LOGFONT LFTTYFONT ; + +char CurrentConfigCall[20]; // Current user or bbs +int CurrentConfigIndex; // Index of current user record +int CurrentMsgIndex; // Index of current Msg record +struct UserInfo * CurrentBBS; // User Record of selected BBS iin Forwarding Config; + +struct UserInfo * MsgBBSList[NBBBS+2] = {0}; // Sorted BBS List + +char InfoBoxText[100]; // Text to display in Config Info Popup + +char Filter_FROM[20]; +char Filter_TO[20]; +char Filter_VIA[60]; // Filters for Edit Message Dialog +char Filter_BID[16]; // Filters for Edit Message Dialog + +extern char LTFROMString[2048]; +extern char LTTOString[2048]; +extern char LTATString[2048]; +VOID * GetOverrideFromString(char * input); + +extern time_t MaintClock; // Time to run housekeeping + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName); +VOID WINAPI OnSelChanged(HWND hwndDlg); +VOID WINAPI OnChildDialogInit(HWND hwndDlg); +VOID WINAPI OnTabbedDialogInit(HWND hwndDlg); +VOID SaveWPConfig(HWND hDlg); +PrintMessage(struct MsgInfo * Msg); +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle); +VOID TidyPrompts(); +struct UserInfo * FindBBS(char * Name); + +INT_PTR CALLBACK UIDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK EditMsgTextDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +VOID SaveMAINTConfigFromDialog(); +VOID TidyWelcomeMsg(char ** pPrompt); + +// POP3 Password is encrypted by xor'ing it with an MD5 hash of the hostname and pop3 server name + + +double GetDlgItemFloat(HWND hDlg, int DlgItem, BOOL *lpTranslated, BOOL bSigned) +{ + char Num[32]; + double ret = 0.0; + + GetDlgItemText(hDlg, DlgItem, Num, 31); + return atof(Num); +} + + +BOOL SetDlgItemFloat(HWND hDlg, int DlgItem, double Value, BOOL bSigned) +{ + char Num[32]; + + sprintf(Num, "%.2f", Value); + + while (Num[strlen(Num) -1] == '0') + Num[strlen(Num) -1] = 0; + + if (Num[strlen(Num) -1] == '.') + Num[strlen(Num) -1] = 0; + + return SetDlgItemText(hDlg, DlgItem, Num); +} + + + +int ww, wh, w, h, hpos, vpos; +int xmargin; +int ymargin; + +INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + RECT Rect; + SCROLLINFO Sinfo; + + int ret; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + switch (message) + { + case WM_INITDIALOG: + + ShowScrollBar(hDlg, SB_BOTH, FALSE); // Hide them till needed + OnTabbedDialogInit(hDlg); + + // initialise scroll bars + + xmargin = 6; + ymargin = 2 + GetSystemMetrics(SM_CYCAPTION); + hpos = vpos = 0; + + return (INT_PTR)TRUE; + + case WM_SIZE: + + w = LOWORD(lParam); + h = HIWORD(lParam); + + // If window is smaller than client area enable scroll bars + + ret = GetWindowRect(hwndDisplay, &Rect); + ww = Rect.right - Rect.left; + wh = Rect.bottom - Rect.top; + + if (ww <= w && (wh + ymargin) <= h) + { + ShowScrollBar(hDlg, SB_BOTH, FALSE); // Hide them till needed + MoveWindow(hwndDisplay, xmargin, ymargin, ww, wh, TRUE); + hpos = vpos = 0; + return TRUE; + } + + ShowScrollBar(hDlg, SB_BOTH, TRUE); + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = ww + xmargin; + Sinfo.nPage = w; + Sinfo.nPos = hpos; + SetScrollInfo(hDlg, SB_HORZ, &Sinfo, TRUE); + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = wh + ymargin; + Sinfo.nPage = h; + Sinfo.nPos = hpos; + SetScrollInfo(hDlg, SB_VERT, &Sinfo, TRUE); + + return TRUE; + + case WM_HSCROLL: + + switch (LOWORD(wParam)) + { + case SB_PAGELEFT: + + hpos -= 20; + if (hpos < 0) + hpos = 0; + + goto UpdateHPos; + + case SB_LINELEFT: + + if (hpos) + hpos --; + + goto UpdateHPos; + + case SB_PAGERIGHT: + + hpos += 20; + goto UpdateHPos; + + case SB_LINERIGHT: + + hpos++; + goto UpdateHPos; + + case SB_THUMBPOSITION: + + hpos = HIWORD(wParam); + +UpdateHPos: + // Need to update Scroll Bar + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = ww + xmargin; + Sinfo.nPage = w; + Sinfo.nPos = hpos; + SetScrollInfo(hDlg, SB_HORZ, &Sinfo, TRUE); + + // Move Client Window + + MoveWindow(hwndDisplay, xmargin - hpos , ymargin - vpos , ww, wh, TRUE); + return TRUE; + } + + return TRUE; + + + case WM_VSCROLL: + + switch (LOWORD(wParam)) + { + case SB_PAGEUP: + + vpos -= 20; + if (vpos < 0) + vpos = 0; + + goto UpdateVPos; + + case SB_LINEUP: + + if (vpos) + vpos --; + + goto UpdateVPos; + + case SB_PAGEDOWN: + + vpos += 20; + goto UpdateVPos; + + case SB_LINEDOWN: + + vpos++; + goto UpdateVPos; + + case SB_THUMBPOSITION: + + vpos = HIWORD(wParam); + +UpdateVPos: + // Need to update Scroll Bar + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = wh + ymargin; + Sinfo.nPage = h; + Sinfo.nPos = vpos; + SetScrollInfo(hDlg, SB_VERT, &Sinfo, TRUE); + + // Move Client Window + + MoveWindow(hwndDisplay, xmargin - hpos , ymargin - vpos , ww, wh, TRUE); + return TRUE; + } + + return TRUE; + + + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + + OnSelChanged(hDlg); + + // Check if scroll now needed + + ret = GetWindowRect(hwndDisplay, &Rect); + ww = Rect.right - Rect.left; + wh = Rect.bottom - Rect.top; + + if (ww <= w && (wh + 27) <= h) + { + ShowScrollBar(hDlg, SB_BOTH, FALSE); // Hide them till needed + return TRUE; + } + + ShowScrollBar(hDlg, SB_BOTH, TRUE); + + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_KEYUP: + + if (wParam == VK_TAB) + return TRUE; + + + break; + + + + case WM_COMMAND: + + switch (LOWORD(wParam)) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + } + + return FALSE; + +} + +INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command; + + UNREFERENCED_PARAMETER(lParam); + + switch (message) + { + case WM_INITDIALOG: + + SetDlgItemText(hDlg, 5050, InfoBoxText); + + return (INT_PTR)TRUE; + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + case 0: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + } + break; + } + + return (INT_PTR)FALSE; +} + + + +INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ +// This processes messages from controls on the tab subpages + int Command; + + switch (message) + { + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hDlg); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + case WM_INITDIALOG: + OnChildDialogInit( hDlg); + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + if (Command == 2002) + return TRUE; + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDC_UICONFIG: + + DialogBox(hInst, MAKEINTRESOURCE(IDD_UICONFIG), hWnd, UIDialogProc); + return TRUE; + + case IDC_BBSSAVE: + + SaveBBSConfig(); + return TRUE; + + case IDC_ISPSAVE: + + SaveISPConfig(); + return TRUE; + + + case IDM_MAINTSAVE: + + SaveMAINTConfigFromDialog(); + return TRUE; + + + case IDM_MSGSAVE: + + SaveWelcomeMsgs(); + return TRUE; + + case IDM_PROMPTSAVE: + + SavePrompts(); + return TRUE; + + case IDC_FILTERSAVE: + + SaveFilters(hDlg); + return TRUE; + + case IDC_WPSAVE: + + SaveWPConfig(hDlg); + return TRUE; + + } + break; + + } + return (INT_PTR)FALSE; +} + + +//The following function processes the WM_INITDIALOG message for the main dialog box. The function allocates the DLGHDR structure, loads the dialog template resources for the child dialog boxes, and creates the tab control. + +//The size of each child dialog box is specified by the DLGTEMPLATE structure. The function examines the size of each dialog box and uses the macro for the TCM_ADJUSTRECT message to calculate an appropriate size for the tab control. Then it sizes the dialog box and positions the two buttons accordingly. This example sends TCM_ADJUSTRECT by using the TabCtrl_AdjustRect macro. + +VOID WINAPI OnTabbedDialogInit(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); + DWORD dwDlgBase = GetDialogBaseUnits(); + int cxMargin = LOWORD(dwDlgBase) / 4; + int cyMargin = HIWORD(dwDlgBase) / 8; + + TC_ITEM tie; + RECT rcTab; + + int i, pos; + INITCOMMONCONTROLSEX init; + + hwndDlg = hDlg; // Save Window Handle + + // Save a pointer to the DLGHDR structure. + + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); + + // Create the tab control. + + + init.dwICC=ICC_STANDARD_CLASSES; + init.dwSize=sizeof(init); + i=InitCommonControlsEx(&init); + + pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, 100, 100, hwndDlg, NULL, hInst, NULL); + + if (pHdr->hwndTab == NULL) { + + // handle error + + } + + // Add a tab for each of the child dialog boxes. + + tie.mask = TCIF_TEXT | TCIF_IMAGE; + + tie.iImage = -1; + + tie.pszText = "BBS Params"; + TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie); + + tie.pszText = "ISP Interface"; + TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie); + + tie.pszText = "Housekeeping"; + TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie); + + tie.pszText = "Welcome Msgs"; + TabCtrl_InsertItem(pHdr->hwndTab, 3, &tie); + + tie.pszText = "Prompts"; + TabCtrl_InsertItem(pHdr->hwndTab, 4, &tie); + + tie.pszText = "Msg Filters"; + TabCtrl_InsertItem(pHdr->hwndTab, 5, &tie); + + tie.pszText = "WP Update"; + TabCtrl_InsertItem(pHdr->hwndTab, 6, &tie); + + // Lock the resources for the three child dialog boxes. + + pHdr->apRes[0] = DoLockDlgRes("BBS_CONFIG"); + pHdr->apRes[1] = DoLockDlgRes("ISP_CONFIG"); + pHdr->apRes[2] = DoLockDlgRes("MAINT"); + pHdr->apRes[3] = DoLockDlgRes("WELCOMEMSG"); + pHdr->apRes[4] = DoLockDlgRes("BBSPROMPTS"); + pHdr->apRes[5] = DoLockDlgRes("FILTERS"); + pHdr->apRes[6] = DoLockDlgRes("WPUPDATE"); + + // Determine the bounding rectangle for all child dialog boxes. + + SetRectEmpty(&rcTab); + + for (i = 0; i < C_PAGES-1; i++) + { + if (pHdr->apRes[i]->cx > rcTab.right) + rcTab.right = pHdr->apRes[i]->cx; + + if (pHdr->apRes[i]->cy > rcTab.bottom) + rcTab.bottom = pHdr->apRes[i]->cy; + + } + + MapDialogRect(hwndDlg, &rcTab); + +// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; + +// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; + + // Calculate how large to make the tab control, so + + // the display area can accomodate all the child dialog boxes. + + TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); + + OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); + + // Calculate the display rectangle. + + CopyRect(&pHdr->rcDisplay, &rcTab); + + TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); + + // Set the size and position of the tab control, buttons, + + // and dialog box. + + SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); + + // Move the Buttons to bottom of page + + pos=rcTab.left+cxMargin; + + + // Size the dialog box. + + SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), + rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), + SWP_NOMOVE | SWP_NOZORDER); + + // Simulate selection of the first item. + + OnSelChanged(hwndDlg); + +} + +// DoLockDlgRes - loads and locks a dialog template resource. + +// Returns a pointer to the locked resource. + +// lpszResName - name of the resource + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) +{ + HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG); + HGLOBAL hglb = LoadResource(hInst, hrsrc); + + return (DLGTEMPLATE *) LockResource(hglb); +} + +//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. + +// OnSelChanged - processes the TCN_SELCHANGE notification. + +// hwndDlg - handle of the parent dialog box + +VOID WINAPI OnSelChanged(HWND hwndDlg) +{ + char Nodes[1000]=""; + char Text[10000]=""; + char Line[80]; + struct Override ** Call; + char Time[10]; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); + + // Destroy the current child dialog box, if any. + + if (pHdr->hwndDisplay != NULL) + + DestroyWindow(pHdr->hwndDisplay); + + // Create the new child dialog box. + + pHdr->hwndDisplay = CreateDialogIndirect(hInst, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); + + hwndDisplay = pHdr->hwndDisplay; // Save + + // Fill in the controls + + switch (CurrentPage) + { + case BBSPARAMS: + + SetDlgItemText(pHdr->hwndDisplay, IDC_BBSCall, BBSName); + SetDlgItemText(pHdr->hwndDisplay, IDC_SYSOPCALL, SYSOPCall); + CheckDlgButton(pHdr->hwndDisplay, IDC_SYSTOSYSOPCALL, SendSYStoSYSOPCall); + CheckDlgButton(pHdr->hwndDisplay, IDC_BBSTOSYSOPCALL, SendBBStoSYSOPCall); + CheckDlgButton(pHdr->hwndDisplay, IDC_DONTHOLDNEW, DontHoldNewUsers); + CheckDlgButton(pHdr->hwndDisplay, IDC_FORWARDTOBBS, ForwardToMe); + CheckDlgButton(pHdr->hwndDisplay, IDC_NONAME, AllowAnon); + CheckDlgButton(pHdr->hwndDisplay, IDC_USERRKILLT, !UserCantKillT); // Note negative logic + CheckDlgButton(pHdr->hwndDisplay, IDC_NOHOMEBBS, DontNeedHomeBBS); + CheckDlgButton(pHdr->hwndDisplay, IDC_DONTCHECKFROM, DontCheckFromCall); + CheckDlgButton(pHdr->hwndDisplay, IDC_DEFAULTNOWINLINK, DefaultNoWINLINK); + + + + SetDlgItemText(pHdr->hwndDisplay, IDC_HRoute, HRoute); + SetDlgItemText(pHdr->hwndDisplay, IDC_BaseDir, BaseDirRaw); + SetDlgItemInt(pHdr->hwndDisplay, IDC_BBSAppl, BBSApplNum, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_BBSStreams, MaxStreams, FALSE); + CheckDlgButton(pHdr->hwndDisplay, IDC_ENABLEUI, EnableUI); + SetDlgItemInt(pHdr->hwndDisplay, MAILFOR_MINS, MailForInterval, FALSE); + CheckDlgButton(pHdr->hwndDisplay, IDC_REFUSEBULLS, RefuseBulls); + CheckDlgButton(pHdr->hwndDisplay, IDC_KNOWNUSERS, OnlyKnown); + SetDlgItemInt(pHdr->hwndDisplay, IDC_POP3Port, POP3InPort, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_NNTPPort, NNTPInPort, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_SMTPPort, SMTPInPort, FALSE); + CheckDlgButton(pHdr->hwndDisplay, IDC_REMOTEEMAIL, RemoteEmail); + SetDlgItemText(pHdr->hwndDisplay, IDC_AMPR, AMPRDomain); + CheckDlgButton(pHdr->hwndDisplay, IDC_FORWARDAMPR, SendAMPRDirect); + + + break; + + case ISPPARAMS: + + CheckDlgButton(pHdr->hwndDisplay, IDC_ISP_Gateway_Enabled, ISP_Gateway_Enabled); + + SetDlgItemInt(pHdr->hwndDisplay, IDC_POP3Timer, ISPPOP3Interval, FALSE); + + SetDlgItemText(pHdr->hwndDisplay, IDC_MyMailDomain, MyDomain); + + SetDlgItemText(pHdr->hwndDisplay, IDC_ISPSMTPName, ISPSMTPName); + SetDlgItemText(pHdr->hwndDisplay, SMTP_EHELO, ISPEHLOName); + SetDlgItemInt(pHdr->hwndDisplay, IDC_ISPSMTPPort, ISPSMTPPort, FALSE); + + SetDlgItemText(pHdr->hwndDisplay, IDC_ISPPOP3Name, ISPPOP3Name); + SetDlgItemInt(pHdr->hwndDisplay, IDC_ISPPOP3Port, ISPPOP3Port, FALSE); + + SetDlgItemText(pHdr->hwndDisplay, IDC_ISPAccountName, ISPAccountName); + SetDlgItemText(pHdr->hwndDisplay, IDC_ISPAccountPass, ISPAccountPass); + + CheckDlgButton(pHdr->hwndDisplay, ISP_SMTP_AUTH, SMTPAuthNeeded); + + break; + + case MAINTPARAMS: + + SetDlgItemInt(pHdr->hwndDisplay, IDC_MAXMSG, MaxMsgno, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_BIDLIFETIME, BidLifetime, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_USERLIFETIME, UserLifetime, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_LOGLIFETIME, LogAge, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDC_MAINTINTERVAL, MaintInterval, FALSE); + sprintf(Time, "%04d", MaintTime); + SetDlgItemText(pHdr->hwndDisplay, IDC_MAINTTIME, Time); + + SetDlgItemFloat(pHdr->hwndDisplay, IDM_PR, PR, FALSE); + SetDlgItemFloat(pHdr->hwndDisplay, IDM_PUR, PUR, FALSE); + SetDlgItemFloat(pHdr->hwndDisplay, IDM_PF, PF, FALSE); + SetDlgItemFloat(pHdr->hwndDisplay, IDM_PNF, PNF, FALSE); + + SetDlgItemInt(pHdr->hwndDisplay, IDM_BF, BF, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDM_BNF, BNF, FALSE); + + SetDlgItemInt(pHdr->hwndDisplay, IDM_NTSD, NTSD, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDM_NTSF, NTSF, FALSE); + SetDlgItemInt(pHdr->hwndDisplay, IDM_NTSU, NTSU, FALSE); + + CheckDlgButton(pHdr->hwndDisplay, IDC_DELETETORECYCLE, DeletetoRecycleBin); + CheckDlgButton(pHdr->hwndDisplay, IDC_MAINTNOMAIL, SuppressMaintEmail); + CheckDlgButton(pHdr->hwndDisplay, IDC_MAINTSAVEREG, SaveRegDuringMaint); + CheckDlgButton(pHdr->hwndDisplay, IDC_OVERRIDEUNSENT, OverrideUnsent); + CheckDlgButton(pHdr->hwndDisplay, IDC_MAINTNONDELIVERY , SendNonDeliveryMsgs); + CheckDlgButton(pHdr->hwndDisplay, IDC_MAINTTRAFFIC, GenerateTrafficReport); + + if (LTFROM) + { + Call = LTFROM; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDM_LTFROM, Text); + + Text[0] = 0; + + if (LTTO) + { + Call = LTTO; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDM_LTTO, Text); + + Text[0] = 0; + + if (LTAT) + { + Call = LTAT; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDM_LTAT, Text); + + Text[0] = 0; + + break; + + + case WELCOMEMSGS: + + SetDlgItemText(pHdr->hwndDisplay, IDM_USERMSG, WelcomeMsg); + SetDlgItemText(pHdr->hwndDisplay, IDM_NEWUSERMSG, NewWelcomeMsg); + SetDlgItemText(pHdr->hwndDisplay, IDM_EXPERTUSERMSG, ExpertWelcomeMsg); + SetDlgItemText(pHdr->hwndDisplay, IDM_SIGNOFF, SignoffMsg); + + break; + + case PROMPTS: + + SetDlgItemText(pHdr->hwndDisplay, IDM_USERMSG, Prompt); + SetDlgItemText(pHdr->hwndDisplay, IDM_NEWUSERMSG, NewPrompt); + SetDlgItemText(pHdr->hwndDisplay, IDM_EXPERTUSERMSG, ExpertPrompt); + + break; + case FILTERS: + + Text[0] = 0; + + if (RejFrom) + { + char ** Call = RejFrom; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_REJFROM, Text); + + Text[0] = 0; + + if (RejTo) + { + char ** Call = RejTo; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_REJTO, Text); + + Text[0] = 0; + + if (RejAt) + { + char ** Call = RejAt; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_REJAT, Text); + + Text[0] = 0; + + if (RejBID) + { + char ** Call = RejBID; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_REJBID, Text); + + Text[0] = 0; + + if (HoldFrom) + { + char ** Call = HoldFrom; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_HOLDFROM, Text); + + Text[0] = 0; + + if (HoldTo) + { + char ** Call = HoldTo; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_HOLDTO, Text); + + Text[0] = 0; + + if (HoldAt) + { + char ** Call = HoldAt; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + + SetDlgItemText(pHdr->hwndDisplay, IDC_HOLDAT, Text); + + Text[0] = 0; + + if (HoldBID) + { + char ** Call = HoldBID; + while(Call[0]) + { + sprintf(Line, "%s\r\n", Call[0]); + strcat(Text, Line); + Call++; + } + } + SetDlgItemText(pHdr->hwndDisplay, IDC_HOLDBID, Text); + + + + break; + + case WPUPDATE: + + if (SendWPAddrs) + { + char ** Calls = SendWPAddrs; + char Text[10000]=""; + + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + + SetDlgItemText(hwndDisplay, IDC_WPTO, Text); + } + + SendDlgItemMessage(hwndDisplay, IDC_WPTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); + SendDlgItemMessage(hwndDisplay, IDC_WPTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); + + + SendDlgItemMessage(hwndDisplay, IDC_WPTYPE, CB_SETCURSEL, SendWPType, 0); + + CheckDlgButton(hwndDisplay, IDC_SENDWP, SendWP); +// CheckDlgButton(hwndDisplay, IDC_SENDWP, NoWPGuesses); + CheckDlgButton(hwndDisplay, IDC_FILTERWPB, FilterWPBulls); + + break; + } + + ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); +} + +//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. + +// OnChildDialogInit - Positions the child dialog box to fall + +// within the display area of the tab control. + +VOID WINAPI OnChildDialogInit(HWND hwndDlg) +{ + HWND hwndParent = GetParent(hwndDlg); + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); + + SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); +} + +void SetForwardingPage(HWND hDlg, struct UserInfo * user) +{ + + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + char ** Calls; + char Text[100000]=""; + + Calls = ForwardingInfo->TOCalls; + + if (Calls) + { + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + } + SetDlgItemText(hDlg, IDC_TOCALLS, Text); + + Text[0] = 0; + + Calls = ForwardingInfo->ATCalls; + + if (Calls) + { + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + } + + SetDlgItemText(hDlg, IDC_ATCALLS, Text); + + Text[0] = 0; + Calls = ForwardingInfo->Haddresses; + + if (Calls) + { + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + + } + + SetDlgItemText(hDlg, IDC_HROUTES, Text); + + Text[0] = 0; + Calls = ForwardingInfo->HaddressesP; + + if (Calls) + { + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + + } + + SetDlgItemText(hDlg, IDC_HROUTESP, Text); + + Text[0] = 0; + + Calls = ForwardingInfo->FWDTimes; + + if (Calls) + { + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + } + + SetDlgItemText(hDlg, IDC_FWDTIMES, Text); + + Text[0] = 0; + Calls = ForwardingInfo->ConnectScript; + + if (Calls) + { + while(Calls[0]) + { + strcat(Text, Calls[0]); + strcat(Text, "\r\n"); + Calls++; + } + + } + + SetDlgItemText(hDlg, IDC_CALL, Text); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + CheckDlgButton(hDlg, IDC_FWDENABLE, ForwardingInfo->Enabled); + CheckDlgButton(hDlg, IDC_REVERSE, ForwardingInfo->ReverseFlag); + CheckDlgButton(hDlg, IDC_BLOCKED, ForwardingInfo->AllowBlocked); + CheckDlgButton(hDlg, IDC_ALLOWCOMP, ForwardingInfo->AllowCompressed); + CheckDlgButton(hDlg, IDC_USEB1, ForwardingInfo->AllowB1); + CheckDlgButton(hDlg, IDC_USEB2, ForwardingInfo->AllowB2); + CheckDlgButton(hDlg, IDC_CTRLZ, ForwardingInfo->SendCTRLZ); + CheckDlgButton(hDlg, IDC_PERSONALONLY, ForwardingInfo->PersonalOnly); + CheckDlgButton(hDlg, IDC_SENDNEW, ForwardingInfo->SendNew); + SetDlgItemInt(hDlg, IDC_FWDINT, ForwardingInfo->FwdInterval, FALSE); + SetDlgItemInt(hDlg, IDC_REVFWDINT, ForwardingInfo->RevFwdInterval, FALSE); + SetDlgItemInt(hDlg, IDC_MAXBLOCK, ForwardingInfo->MaxFBBBlockSize, FALSE); + SetDlgItemInt(hDlg, IDC_CONTIMEOUT, ForwardingInfo->ConTimeout, FALSE); + SetDlgItemText(hDlg, IDC_BBSHA, ForwardingInfo->BBSHA); + + SetFocus(GetDlgItem(hDlg, IDC_TOCALLS)); + return; +} + +int Do_BBS_Sel_Changed(HWND hDlg) +{ + // Update BBS display with newly selected BBS + + struct UserInfo * user; + + int Sel = SendDlgItemMessage(hDlg, IDC_BBS, CB_GETCURSEL, 0, 0); + + SendDlgItemMessage(hDlg, IDC_BBS, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentConfigCall); + + for (user = BBSChain; user; user = user->BBSNext) + { + if (strcmp(user->Call, CurrentConfigCall) == 0) + { + CurrentBBS = user; + SetForwardingPage(hDlg, user); // moved to separate routine as also called from copy config + } + } + return 0; + +} +int Sel; + +int Do_User_Sel_Changed(HWND hDlg) +{ + + // Update BBS display with newly selected BBS + + struct UserInfo * user; + + Sel = SendDlgItemMessage(hDlg, IDC_USER, CB_GETCURSEL, 0, 0); + + if (Sel == -1) + SendDlgItemMessage(hDlg, IDC_USER, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentConfigCall); + else + SendDlgItemMessage(hDlg, IDC_USER, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentConfigCall); + + for (CurrentConfigIndex = 1; CurrentConfigIndex <= NumberofUsers; CurrentConfigIndex++) + { + user = UserRecPtr[CurrentConfigIndex]; + + if (_stricmp(user->Call, CurrentConfigCall) == 0) + { + struct tm *tm; + char Date[80]; + char * Dateptr; + int i, n, s; + char SSID[10]; + + int ConnectsIn; + int ConnectsOut; + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; + int MsgsRejectedOut; + int BytesForwardedIn; + int BytesForwardedOut; +// char MsgsIn[80]; +// char MsgsOut[80]; +// char BytesIn[80]; +// char BytesOut[80]; +// char RejIn[80]; +// char RejOut[80]; + + i = 0; + + ConnectsIn = user->Total.ConnectsIn - user->Last.ConnectsIn; + ConnectsOut = user->Total.ConnectsOut - user->Last.ConnectsOut; + + MsgsReceived = MsgsSent = MsgsRejectedIn = MsgsRejectedOut = BytesForwardedIn = BytesForwardedOut = 0; + + for (n = 0; n < 4; n++) + { + MsgsReceived += user->Total.MsgsReceived[n] - user->Last.MsgsReceived[n]; + MsgsSent += user->Total.MsgsSent[n] - user->Last.MsgsSent[n]; + BytesForwardedIn += user->Total.BytesForwardedIn[n] - user->Last.BytesForwardedIn[n]; + BytesForwardedOut += user->Total.BytesForwardedOut[n] - user->Last.BytesForwardedOut[n]; + MsgsRejectedIn += user->Total.MsgsRejectedIn[n] - user->Last.MsgsRejectedIn[n]; + MsgsRejectedOut += user->Total.MsgsRejectedOut[n] - user->Last.MsgsRejectedOut[n]; + } + + + + SetDlgItemText(hDlg, IDC_NAME, user->Name); + SetDlgItemText(hDlg, IDC_PASSWORD, user->pass); + SetDlgItemText(hDlg, IDC_QTH, user->Address); + SetDlgItemText(hDlg, IDC_UZIP, user->ZIP); + SetDlgItemText(hDlg, IDC_HOMEBBS, user->HomeBBS); + SetDlgItemText(hDlg, IDC_CMSPASS, user->CMSPass); + + SetDlgItemInt(hDlg, IDC_LASTLISTED, user->lastmsg, TRUE); + + CheckDlgButton(hDlg, IDC_SYSOP, (user->flags & F_SYSOP)); + CheckDlgButton(hDlg, IDC_BBSFLAG, (user->flags & F_BBS)); + CheckDlgButton(hDlg, IDC_PMSFLAG, (user->flags & F_PMS)); + CheckDlgButton(hDlg, IDC_EXPERT, (user->flags & F_Expert)); + CheckDlgButton(hDlg, IDC_EXCLUDED, (user->flags & F_Excluded)); + CheckDlgButton(hDlg, IDC_EMAIL, (user->flags & F_EMAIL)); + CheckDlgButton(hDlg, IDC_HOLDMAIL, (user->flags & F_HOLDMAIL)); + CheckDlgButton(hDlg, ALLOW_BULLS, (user->flags & F_NOBULLS) == 0); // Node inverted flag + CheckDlgButton(hDlg, IDC_NTSMPS, (user->flags & F_NTSMPS)); + CheckDlgButton(hDlg, IDC_APRSMFOR, (user->flags & F_APRSMFOR)); + CheckDlgButton(hDlg, IDC_POLLRMS, (user->flags & F_POLLRMS)); + CheckDlgButton(hDlg, IDC_SYSOP_IN_LM, (user->flags & F_SYSOP_IN_LM)); + CheckDlgButton(hDlg, RMS_EXPRESS_USER, (user->flags & F_Temp_B2_BBS)); + CheckDlgButton(hDlg, NO_WINLINKdotORG, (user->flags & F_NOWINLINK)); + CheckDlgButton(hDlg, IDC_RMSREDIRECT, (user->flags & F_RMSREDIRECT)); + + EnableWindow(GetDlgItem(hDlg, IDC_SYSOP_IN_LM), user->flags & F_SYSOP); + + SetDlgItemInt(hDlg, CONN_IN, ConnectsIn, FALSE); + SetDlgItemInt(hDlg, CONN_OUT, ConnectsOut, FALSE); + SetDlgItemInt(hDlg, MSGS_IN, MsgsReceived, FALSE); + SetDlgItemInt(hDlg, MSGS_OUT, MsgsSent, FALSE); + SetDlgItemInt(hDlg, REJECTS_IN, MsgsRejectedIn, FALSE); + SetDlgItemInt(hDlg, REJECTS_OUT, MsgsRejectedOut, FALSE); + SetDlgItemInt(hDlg, BYTES_IN, BytesForwardedIn, FALSE); + SetDlgItemInt(hDlg, BYTES_OUT, BytesForwardedOut, FALSE); + +/* + for (i = 0; i < 3; i++) + { + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_RESETCONTENT,0 , 0); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)" "); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"1"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"2"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"3"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"4"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"5"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"6"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"7"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"8"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"9"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"10"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"11"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"12"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"13"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"14"); + SendDlgItemMessage(hDlg, RMS_SSID1 + i, CB_ADDSTRING,0 , (LPARAM)"15"); + } +*/ + SendDlgItemMessage(hDlg, RMS_SSID1, WM_SETTEXT, 0, (LPARAM)"-"); + SendDlgItemMessage(hDlg, RMS_SSID2, WM_SETTEXT, 0, (LPARAM)"-"); + SendDlgItemMessage(hDlg, RMS_SSID3, WM_SETTEXT, 0, (LPARAM)"-"); + + i = 0; + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(SSID, "%d", s); + else + SSID[0] = 0; + + SendDlgItemMessage(hDlg, RMS_SSID1 + i++, WM_SETTEXT, 0, (LPARAM)SSID); +// SendDlgItemMessage(hDlg, RMS_SSID1 + i++, CB_SETCURSEL, s, 0); + if (i == 3) + break; + } + } + + tm = gmtime((time_t *)&user->TimeLastConnected); + Dateptr = asctime(tm); + strcpy(Date, Dateptr); + + SetDlgItemText(hDlg, LASTCONNECT, Date); + + i = (user->flags >> 28); + sprintf(SSID, "%d", i); + + if (i == 0) + SSID[0] = 0; + + SendDlgItemMessage(hDlg, IDC_APRSSSID, WM_SETTEXT, 0, (LPARAM)SSID); + + return 0; + } + } + + // Typing in new user + + CurrentConfigIndex = -1; + + SetDlgItemText(hDlg, IDC_NAME, ""); + SetDlgItemText(hDlg, IDC_PASSWORD, ""); + SetDlgItemText(hDlg, IDC_CMSPASS, ""); + + SetDlgItemText(hDlg, IDC_QTH, ""); + SetDlgItemText(hDlg, IDC_UZIP, ""); + SetDlgItemText(hDlg, IDC_HOMEBBS, ""); + SetDlgItemInt(hDlg, IDC_LASTLISTED, LatestMsg, FALSE); + + CheckDlgButton(hDlg, IDC_SYSOP, FALSE); + CheckDlgButton(hDlg, IDC_BBSFLAG, FALSE); + CheckDlgButton(hDlg, IDC_PMSFLAG, FALSE); + CheckDlgButton(hDlg, IDC_EXPERT, FALSE); + CheckDlgButton(hDlg, IDC_EXCLUDED, FALSE); + + return 0; +} + +VOID Do_Add_User(HWND hDlg) +{ + struct UserInfo * user; + int n; + + SendDlgItemMessage(hDlg, IDC_USER, WM_GETTEXT, 19, (LPARAM)(LPCTSTR)&CurrentConfigCall); + + if (LookupCall(CurrentConfigCall)) + sprintf(InfoBoxText, "User %s already exists", CurrentConfigCall); + else if ((strlen(CurrentConfigCall) < 3) || (strlen(CurrentConfigCall) > MAXUSERNAMELEN)) + sprintf(InfoBoxText, "User %s is invalid", CurrentConfigCall); + else + { + user = AllocateUserRecord(CurrentConfigCall); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + CurrentConfigIndex=NumberofUsers; + Do_Save_User(hDlg, FALSE); + sprintf(InfoBoxText, "User %s added and info saved", CurrentConfigCall); + } + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + SendDlgItemMessage(hDlg, IDC_USER, CB_RESETCONTENT, 0, 0); + + for (n = 1; n <= NumberofUsers; n++) + { + SendDlgItemMessage(hDlg, IDC_USER, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)UserRecPtr[n]->Call); + } + + return; + +} + + +VOID Do_Delete_User(HWND hDlg) +{ + struct UserInfo * user; + int n; + + + if (CurrentConfigIndex == -1) + { + sprintf(InfoBoxText, "Please select a user to delete"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + user = UserRecPtr[CurrentConfigIndex]; + + if (_stricmp(CurrentConfigCall, user->Call) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - user not deleted"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + for (n = CurrentConfigIndex; n < NumberofUsers; n++) + { + UserRecPtr[n] = UserRecPtr[n+1]; // move down all following entries + } + + NumberofUsers--; + + SendDlgItemMessage(hDlg, IDC_USER, CB_RESETCONTENT, 0, 0); + + for (n = 1; n <= NumberofUsers; n++) + { + SendDlgItemMessage(hDlg, IDC_USER, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)UserRecPtr[n]->Call); + } + + sprintf(InfoBoxText, "User %s deleted", user->Call); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + if (user->flags & F_BBS) // was a BBS? + DeleteBBS(user); + + free(user); + + // Position to same place in list + + SendDlgItemMessage(hDlg, IDC_USER, CB_SETCURSEL, Sel, 0); + Do_User_Sel_Changed(hDlg); + + return; + +} +VOID Do_Save_User(HWND hDlg, BOOL ShowBox) +{ + struct UserInfo * user; + BOOL OK; + char RMSSSID[10]; + unsigned int SSID; + + if (CurrentConfigIndex == -1) + { + sprintf(InfoBoxText, "Please select a user to save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + user = UserRecPtr[CurrentConfigIndex]; + + if (strcmp(CurrentConfigCall, user->Call) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - information not saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + GetDlgItemText(hDlg, IDC_NAME, user->Name, 17); + GetDlgItemText(hDlg, IDC_PASSWORD, user->pass, 12); + GetDlgItemText(hDlg, IDC_QTH, user->Address, 60); + GetDlgItemText(hDlg, IDC_UZIP, user->ZIP, 8); + GetDlgItemText(hDlg, IDC_HOMEBBS, user->HomeBBS, 40); + GetDlgItemText(hDlg, IDC_CMSPASS, user->CMSPass, 15); + + user->lastmsg = GetDlgItemInt(hDlg, IDC_LASTLISTED, &OK, FALSE); + + if (IsDlgButtonChecked(hDlg, IDC_BBSFLAG)) + { + // If BBS Flag has changed, must set up or delete forwarding info + + if ((user->flags & F_BBS) == 0) + { + // New BBS + + if(SetupNewBBS(user)) + { + user->flags |= F_BBS; + user->flags &= ~F_Temp_B2_BBS; // Clear RMS Express User + CheckDlgButton(hDlg, RMS_EXPRESS_USER, (user->flags & F_Temp_B2_BBS)); + } + else + { + // Failed - too many bbs's defined + + sprintf(InfoBoxText, "Cannot set user to be a BBS - you already have 80 BBS's defined"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + user->flags &= ~F_BBS; + CheckDlgButton(hDlg, IDC_BBSFLAG, (user->flags & F_BBS)); + } + } + } + else + { + if (user->flags & F_BBS) + { + //was a BBS + + user->flags &= ~F_BBS; + DeleteBBS(user); + } + } + + if (IsDlgButtonChecked(hDlg, IDC_PMSFLAG)) + user->flags |= F_PMS; else user->flags &= ~F_PMS; + + if (IsDlgButtonChecked(hDlg, IDC_EXPERT)) + user->flags |= F_Expert; else user->flags &= ~F_Expert; + + if (IsDlgButtonChecked(hDlg, IDC_EXCLUDED)) + user->flags |= F_Excluded; else user->flags &= ~F_Excluded; + + if (IsDlgButtonChecked(hDlg, IDC_SYSOP)) + user->flags |= F_SYSOP; else user->flags &= ~F_SYSOP; + + if (IsDlgButtonChecked(hDlg, IDC_EMAIL)) + user->flags |= F_EMAIL; else user->flags &= ~F_EMAIL; + + if (IsDlgButtonChecked(hDlg, IDC_HOLDMAIL)) + user->flags |= F_HOLDMAIL; else user->flags &= ~F_HOLDMAIL; + + if (IsDlgButtonChecked(hDlg, ALLOW_BULLS) == 0) + user->flags |= F_NOBULLS; else user->flags &= ~F_NOBULLS; // Note flag inverted + + if (IsDlgButtonChecked(hDlg, IDC_APRSMFOR)) + user->flags |= F_APRSMFOR; else user->flags &= ~F_APRSMFOR; + + if (IsDlgButtonChecked(hDlg, IDC_NTSMPS)) + user->flags |= F_NTSMPS; else user->flags &= ~F_NTSMPS; + + if (IsDlgButtonChecked(hDlg, IDC_RMSREDIRECT)) + user->flags |= F_RMSREDIRECT; else user->flags &= ~F_RMSREDIRECT; + + if (IsDlgButtonChecked(hDlg, IDC_POLLRMS)) + user->flags |= F_POLLRMS; else user->flags &= ~F_POLLRMS; + + if (IsDlgButtonChecked(hDlg, IDC_SYSOP_IN_LM)) + user->flags |= F_SYSOP_IN_LM; else user->flags &= ~F_SYSOP_IN_LM; + + if (IsDlgButtonChecked(hDlg, RMS_EXPRESS_USER)) + user->flags |= F_Temp_B2_BBS; else user->flags &= ~F_Temp_B2_BBS; + + if (IsDlgButtonChecked(hDlg, NO_WINLINKdotORG)) + user->flags |= F_NOWINLINK; else user->flags &= ~F_NOWINLINK; + +// if (user->flags & F_BBS) +// user->flags &= ~F_Temp_B2_BBS; // Can't be both + + + user->RMSSSIDBits = 0; + + SendDlgItemMessage(hDlg, RMS_SSID1, WM_GETTEXT, 3, (LPARAM)(LPCTSTR)&RMSSSID); + + if (RMSSSID[0] != '-') + { + SSID = atoi(RMSSSID); + user->RMSSSIDBits |= (1 << (SSID)); + } + SendDlgItemMessage(hDlg, RMS_SSID2, WM_GETTEXT, 3, (LPARAM)(LPCTSTR)&RMSSSID); + + if (RMSSSID[0] != '-') + { + SSID = atoi(RMSSSID); + user->RMSSSIDBits |= (1 << (SSID)); + } + + SendDlgItemMessage(hDlg, RMS_SSID3, WM_GETTEXT, 3, (LPARAM)(LPCTSTR)&RMSSSID); + + if (RMSSSID[0] != '-') + { + SSID = atoi(RMSSSID); + user->RMSSSIDBits |= (1 << (SSID)); + } + + SendDlgItemMessage(hDlg, IDC_APRSSSID, WM_GETTEXT, 3, (LPARAM)(LPCTSTR)&RMSSSID); + + SSID = atoi(RMSSSID); + SSID &= 15; + + user->flags &= 0x0fffffff; + user->flags |= (SSID << 28); + + SaveUserDatabase(); + + UpdateWPWithUserInfo(user); + + + if (ShowBox) + { + sprintf(InfoBoxText, "User information saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + } +} + +int compare(const void *arg1, const void *arg2); + +int Do_Msg_Sel_Changed(HWND hDlg) +{ + // Update Msg display with newly selected Msg + + struct MsgInfo * Msg; + char MsgnoText[10]; + int Msgno; + int Sel = SendDlgItemMessage(hDlg, 0, LB_GETCURSEL, 0, 0); + char Size[10]; + + if (Sel != -1) + { + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Sel, (LPARAM)(LPCTSTR)&MsgnoText); + Msgno = atoi(MsgnoText); + } + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + struct UserInfo * USER; + int i = 0, n; + UINT State; + + SetDlgItemText(hDlg, 6001, Msg->from); + SetDlgItemText(hDlg, 6002, Msg->bid); + SetDlgItemText(hDlg, 6003, Msg->to); + SetDlgItemText(hDlg, EMAILFROM, Msg->emailfrom); + SetDlgItemText(hDlg, 6004, Msg->via); + SetDlgItemText(hDlg, 6005, Msg->title); + sprintf(Size, "%d", Msg->length); + + SetDlgItemText(hDlg, 6018, FormatDateAndTime((time_t)Msg->datecreated, FALSE)); + SetDlgItemText(hDlg, 6019, FormatDateAndTime((time_t)Msg->datereceived, FALSE)); + SetDlgItemText(hDlg, 6021, FormatDateAndTime((time_t)Msg->datechanged, FALSE)); + SetDlgItemText(hDlg, 6020, Size); + + if (Msg->type == 'B') + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); + else if (Msg->type == 'P') + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 1, 0); + else + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 2, 0); + + switch(Msg->status) + { + case 'N': Sel = MSGSTATUS_N; break; + case 'Y': Sel = MSGSTATUS_Y; break; + case 'F': Sel = MSGSTATUS_F; break; + case 'K': Sel = MSGSTATUS_K; break; + case 'H': Sel = MSGSTATUS_H; break; + case 'D': Sel = MSGSTATUS_D; break; + case '$': Sel = MSGSTATUS_$; break; + } + + // Get a sorted list of BBS records + + for (n = 1; n <= NumberofUsers; n++) + { + USER = UserRecPtr[n]; + + if ((USER->flags & F_BBS) && USER->BBSNumber) + MsgBBSList[i++] = USER; + } + + qsort((void *)MsgBBSList, i, 4, compare ); + + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_SETCURSEL, Sel, 0); + + for (n = 0; n <= NBBBS; n++) + { + State = BST_INDETERMINATE; + + USER = MsgBBSList[n]; + + if (USER) + { + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + if (check_fwd_bit(Msg->fbbs, USER->BBSNumber)) + State = BST_UNCHECKED; + if (memcmp(Msg->forw, zeros, NBMASK) != 0) + if (check_fwd_bit(Msg->forw, USER->BBSNumber)) + State = BST_CHECKED; + + SetDlgItemText(hDlg, n + 25, USER->Call); + } + else + SetDlgItemText(hDlg, n + 25, ""); + + CheckDlgButton(hDlg, n + 25, State); + } + + return 0; + } + } + + CurrentMsgIndex = -1; + + return 0; +} + +VOID Do_Save_Msg(HWND hDlg) +{ + struct MsgInfo * Msg; + struct UserInfo * user; + + char status[2]; + int i, n, BBSNumber; + BOOL toforward, forwarded; + + if (CurrentMsgIndex == -1) + { + sprintf(InfoBoxText, "Please select a message to save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + Msg = MsgHddrPtr[CurrentMsgIndex]; + + GetDlgItemText(hDlg, 6001, Msg->from, 7); + GetDlgItemText(hDlg, 6002, Msg->bid, 13); + GetDlgItemText(hDlg, 6003, Msg->to, 7); + GetDlgItemText(hDlg, 6004, Msg->via, 41); + GetDlgItemText(hDlg, 6005, Msg->title, 61); + GetDlgItemText(hDlg, EMAILFROM, Msg->emailfrom, 41); + + GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); + Msg->type = status[0]; + + // Check each BBS to for Farwardind State + + for (i = 0; i < NBBBS; i++) + { + n = IsDlgButtonChecked(hDlg, i + 25); + + user = MsgBBSList[i]; + + if (user) + { + BBSNumber = user->BBSNumber; + +// if (BBSNumber == 31) +// n = n; + + toforward = check_fwd_bit(Msg->fbbs, BBSNumber); + forwarded = check_fwd_bit(Msg->forw, BBSNumber); + + if (n == BST_INDETERMINATE) + { + if ((!toforward) && (!forwarded)) + { + // No Change + continue; + } + else + { + clear_fwd_bit(Msg->fbbs, BBSNumber); + if (toforward) + user->ForwardingInfo->MsgCount--; + + clear_fwd_bit(Msg->forw, BBSNumber); + } + } + else if (n == BST_UNCHECKED) + { + if (toforward) + { + // No Change + continue; + } + else + { + set_fwd_bit(Msg->fbbs, BBSNumber); + user->ForwardingInfo->MsgCount++; + clear_fwd_bit(Msg->forw, BBSNumber); + if (FirstMessageIndextoForward > CurrentMsgIndex) + FirstMessageIndextoForward = CurrentMsgIndex; + } + } + else if (n == BST_CHECKED) + { + if (forwarded) + { + // No Change + continue; + } + else + { + set_fwd_bit(Msg->forw, BBSNumber); + clear_fwd_bit(Msg->fbbs, BBSNumber); + if (toforward) + user->ForwardingInfo->MsgCount--; + } + } + } + } + + GetDlgItemText(hDlg, IDC_MSGSTATUS, status, 2); + + if (Msg->status != status[0]) + { + // Need to take action if killing message + + Msg->status = status[0]; + if (status[0] == 'K') + FlagAsKilled(Msg, FALSE); // Clear forwarding bits + } + + sprintf(InfoBoxText, "Message Updated"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + Msg->datechanged=time(NULL); + + SaveMessageDatabase(); + + Do_Msg_Sel_Changed(hDlg); // Refresh +} + +VOID SaveBBSConfig() +{ + BOOL OK1,OK2,OK3,OK4; + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + HKEY hKey=0; + + GetDlgItemText(hwndDisplay, IDC_BBSCall, BBSName, 50); + GetDlgItemText(hwndDisplay, IDC_SYSOPCALL, SYSOPCall, 50); + GetDlgItemText(hwndDisplay, IDC_HRoute, HRoute, 50); + GetDlgItemText(hwndDisplay, IDC_BaseDir, BaseDirRaw, MAX_PATH -1); + EnableUI = IsDlgButtonChecked(hwndDisplay, IDC_ENABLEUI); + RefuseBulls = IsDlgButtonChecked(hwndDisplay, IDC_REFUSEBULLS); + OnlyKnown = IsDlgButtonChecked(hwndDisplay, IDC_KNOWNUSERS); + MailForInterval = GetDlgItemInt(hwndDisplay, MAILFOR_MINS, &OK1, FALSE); + SendSYStoSYSOPCall = IsDlgButtonChecked(hwndDisplay, IDC_SYSTOSYSOPCALL); + SendBBStoSYSOPCall = IsDlgButtonChecked(hwndDisplay, IDC_BBSTOSYSOPCALL); + DontHoldNewUsers = IsDlgButtonChecked(hwndDisplay, IDC_DONTHOLDNEW); + ForwardToMe = IsDlgButtonChecked(hwndDisplay, IDC_FORWARDTOBBS); + DontNeedHomeBBS = IsDlgButtonChecked(hwndDisplay, IDC_NOHOMEBBS); + DontCheckFromCall = IsDlgButtonChecked(hwndDisplay, IDC_DONTCHECKFROM); + AllowAnon = IsDlgButtonChecked(hwndDisplay, IDC_NONAME); + UserCantKillT = !IsDlgButtonChecked(hwndDisplay, IDC_USERRKILLT); // Reverse logic + DefaultNoWINLINK = IsDlgButtonChecked(hwndDisplay, IDC_DEFAULTNOWINLINK); + + BBSApplNum = GetDlgItemInt(hwndDisplay, IDC_BBSAppl, &OK1, FALSE); + MaxStreams = GetDlgItemInt(hwndDisplay, IDC_BBSStreams, &OK2, FALSE); + POP3InPort = GetDlgItemInt(hwndDisplay, IDC_POP3Port, &OK3, FALSE); + SMTPInPort = GetDlgItemInt(hwndDisplay, IDC_SMTPPort, &OK4, FALSE); + NNTPInPort = GetDlgItemInt(hwndDisplay, IDC_NNTPPort, &OK3, FALSE); + + GetDlgItemText(hwndDisplay, IDC_AMPR, AMPRDomain, 50); + SendAMPRDirect= IsDlgButtonChecked(hwndDisplay, IDC_FORWARDAMPR); + RemoteEmail = IsDlgButtonChecked(hwndDisplay, IDC_REMOTEEMAIL); + + strlop(BBSName, '-'); + strlop(SYSOPCall, '-'); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + sprintf(InfoBoxText, "Warning - Program must be restarted for changes to be effective"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + +} + + +VOID SaveISPConfig() +{ + BOOL OK1,OK2,OK3; + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + ISP_Gateway_Enabled = IsDlgButtonChecked(hwndDisplay, IDC_ISP_Gateway_Enabled); + + SMTPAuthNeeded = IsDlgButtonChecked(hwndDisplay, ISP_SMTP_AUTH); + + ISPPOP3Interval = GetDlgItemInt(hwndDisplay, IDC_POP3Timer, &OK1, FALSE); + + GetDlgItemText(hwndDisplay, IDC_MyMailDomain, MyDomain, 50); + + GetDlgItemText(hwndDisplay, IDC_ISPSMTPName, ISPSMTPName, 50); + ISPSMTPPort = GetDlgItemInt(hwndDisplay, IDC_ISPSMTPPort, &OK2, FALSE); + + GetDlgItemText(hwndDisplay, SMTP_EHELO, ISPEHLOName, 50); + + GetDlgItemText(hwndDisplay, IDC_ISPPOP3Name, ISPPOP3Name, 50); + ISPPOP3Port = GetDlgItemInt(hwndDisplay, IDC_ISPPOP3Port, &OK3, FALSE); + + GetDlgItemText(hwndDisplay, IDC_ISPAccountName, ISPAccountName, 50); + GetDlgItemText(hwndDisplay, IDC_ISPAccountPass, ISPAccountPass, 50); + + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + +} + +VOID SaveFWDConfig(HWND hDlg) +{ + int OK, n; + char BBSHA[50]; + + ReaddressLocal = IsDlgButtonChecked(hDlg, IDC_READDRESSLOCAL); + ReaddressReceived = IsDlgButtonChecked(hDlg, IDC_READDRESSRXED); + WarnNoRoute = IsDlgButtonChecked(hDlg, IDC_WARNNOROUTE); + Localtime = IsDlgButtonChecked(hDlg, IDC_USELOCALTIME); + MaxTXSize = GetDlgItemInt(hDlg, IDC_MAXSEND, &OK, FALSE); + MaxRXSize = GetDlgItemInt(hDlg, IDC_MAXRECV, &OK, FALSE); + MaxAge = GetDlgItemInt(hDlg, IDC_MAXAGE, &OK, FALSE); + SendPtoMultiple = IsDlgButtonChecked(hDlg, IDC_MULTIP); + + + // Reinitialise Aliases + + n = 0; + + if (Aliases) + { + while(Aliases[n]) + { + free(Aliases[n]->Dest); + free(Aliases[n]); + n++; + } + + free(Aliases); + Aliases = NULL; + FreeList(AliasText); + } + + AliasText = GetMultiLineDialogParam(hDlg, IDC_ALIAS); + SetupFwdAliases(); + + if (CurrentBBS) + { + struct BBSForwardingInfo * ForwardingInfo = CurrentBBS->ForwardingInfo; + + ForwardingInfo->ATCalls = GetMultiLineDialogParam(hDlg, IDC_ATCALLS); + ForwardingInfo->TOCalls = GetMultiLineDialogParam(hDlg, IDC_TOCALLS); + ForwardingInfo->Haddresses = GetMultiLineDialogParam(hDlg, IDC_HROUTES); + ForwardingInfo->HaddressesP = GetMultiLineDialogParam(hDlg, IDC_HROUTESP); + ForwardingInfo->ConnectScript = GetMultiLineDialogParam(hDlg, IDC_CALL); + ForwardingInfo->FWDTimes = GetMultiLineDialogParam(hDlg, IDC_FWDTIMES); + + + ForwardingInfo->Enabled = IsDlgButtonChecked(hDlg, IDC_FWDENABLE); + ForwardingInfo->ReverseFlag = IsDlgButtonChecked(hDlg, IDC_REVERSE); + ForwardingInfo->AllowB2 = IsDlgButtonChecked(hDlg, IDC_USEB2); + ForwardingInfo->PersonalOnly = IsDlgButtonChecked(hDlg, IDC_PERSONALONLY); + ForwardingInfo->SendNew = IsDlgButtonChecked(hDlg, IDC_SENDNEW); + ForwardingInfo->AllowB1 = IsDlgButtonChecked(hDlg, IDC_USEB1); + ForwardingInfo->SendCTRLZ = IsDlgButtonChecked(hDlg, IDC_CTRLZ); + ForwardingInfo->AllowBlocked = IsDlgButtonChecked(hDlg, IDC_BLOCKED); + ForwardingInfo->AllowCompressed = IsDlgButtonChecked(hDlg, IDC_ALLOWCOMP); + ForwardingInfo->FwdInterval = GetDlgItemInt(hDlg, IDC_FWDINT, &OK, FALSE); + ForwardingInfo->RevFwdInterval = GetDlgItemInt(hDlg, IDC_REVFWDINT, &OK, FALSE); + ForwardingInfo->MaxFBBBlockSize = GetDlgItemInt(hDlg, IDC_MAXBLOCK, &OK, FALSE); + ForwardingInfo->ConTimeout = GetDlgItemInt(hDlg, IDC_CONTIMEOUT,&OK , FALSE); + + GetDlgItemText(hDlg, IDC_BBSHA, BBSHA, 50); + if (ForwardingInfo->BBSHA) + free(ForwardingInfo->BBSHA); + ForwardingInfo->BBSHA = _strdup(BBSHA); + } + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + if (CurrentBBS) + ReinitializeFWDStruct(CurrentBBS); + + sprintf(InfoBoxText, "Forwarding information saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + +} + +VOID CopyFwdConfig(HWND hDlg) +{ + char FromBBS[11] = ""; + struct UserInfo * OldBBS; + + if (CurrentBBS == NULL) + { + sprintf(InfoBoxText, "Please select a BBS to copy to"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + // Get call to copy from + + GetDlgItemText(hDlg, COPYFROMCALL, FromBBS, 10); + + OldBBS = FindBBS(FromBBS); + + if (OldBBS == NULL) + { + sprintf(InfoBoxText, "BBS %s not found", FromBBS); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + // Set current info from OldBBS + + SetForwardingPage(hDlg, OldBBS); // moved to separate routine as also called from copy config + +// sprintf(InfoBoxText, "Forwarding information saved"); +// DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + +} + + +VOID SaveMAINTConfigFromDialog() +{ + BOOL OK1; + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + MaxMsgno = GetDlgItemInt(hwndDisplay, IDC_MAXMSG, &OK1, FALSE); + + if (MaxMsgno > 99000) MaxMsgno = 99000; + + BidLifetime = GetDlgItemInt(hwndDisplay, IDC_BIDLIFETIME, &OK1, FALSE); + LogAge = GetDlgItemInt(hwndDisplay, IDC_LOGLIFETIME, &OK1, FALSE); + UserLifetime = GetDlgItemInt(hwndDisplay, IDC_USERLIFETIME, &OK1, FALSE); + MaintInterval = GetDlgItemInt(hwndDisplay, IDC_MAINTINTERVAL, &OK1, FALSE); + MaintTime = GetDlgItemInt(hwndDisplay, IDC_MAINTTIME, &OK1, FALSE); + PR = GetDlgItemFloat(hwndDisplay, IDM_PR, &OK1, FALSE); + PUR = GetDlgItemFloat(hwndDisplay, IDM_PUR, &OK1, FALSE); + PF = GetDlgItemFloat(hwndDisplay, IDM_PF, &OK1, FALSE); + PNF = GetDlgItemFloat(hwndDisplay, IDM_PNF, &OK1, FALSE); + BF = GetDlgItemInt(hwndDisplay, IDM_BF, &OK1, FALSE); + BNF = GetDlgItemInt(hwndDisplay, IDM_BNF, &OK1, FALSE); + NTSD = GetDlgItemInt(hwndDisplay, IDM_NTSD, &OK1, FALSE); + NTSF = GetDlgItemInt(hwndDisplay, IDM_NTSF, &OK1, FALSE); + NTSU = GetDlgItemInt(hwndDisplay, IDM_NTSU, &OK1, FALSE); + DeletetoRecycleBin = IsDlgButtonChecked(hwndDisplay, IDC_DELETETORECYCLE); + SuppressMaintEmail = IsDlgButtonChecked(hwndDisplay, IDC_MAINTNOMAIL); + SaveRegDuringMaint = IsDlgButtonChecked(hwndDisplay, IDC_MAINTSAVEREG); + OverrideUnsent = IsDlgButtonChecked(hwndDisplay, IDC_OVERRIDEUNSENT); + SendNonDeliveryMsgs = IsDlgButtonChecked(hwndDisplay, IDC_MAINTNONDELIVERY); + + GetDlgItemText(hwndDisplay, IDM_LTFROM, LTFROMString, 2048); + LTFROM = GetOverrideFromString(LTFROMString); + + GetDlgItemText(hwndDisplay, IDM_LTTO, LTTOString, 2048); + LTTO = GetOverrideFromString(LTTOString); + + GetDlgItemText(hwndDisplay, IDM_LTAT, LTATString, 2048); + LTAT = GetOverrideFromString(LTATString); + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = _mkgmtime(tm); + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %d NOW %d Time to HouseKeeping %d", MaintClock, now, MaintClock - now); + } + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + +} + +VOID SaveWelcomeMsgs() +{ + char Value[10000]; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + GetDlgItemText(hwndDisplay, IDM_USERMSG, Value, 10000); + + free(WelcomeMsg); + WelcomeMsg = _strdup(Value); + + GetDlgItemText(hwndDisplay, IDM_NEWUSERMSG, Value, 10000); + + free(NewWelcomeMsg); + NewWelcomeMsg = _strdup(Value); + + GetDlgItemText(hwndDisplay, IDM_EXPERTUSERMSG, Value, 10000); + + free(ExpertWelcomeMsg); + ExpertWelcomeMsg = _strdup(Value); + + GetDlgItemText(hwndDisplay, IDM_SIGNOFF, SignoffMsg, 99); + + if (SignoffMsg[0]) + if (SignoffMsg[strlen(SignoffMsg) - 1] != 13) + strcat(SignoffMsg, "\r"); + + TidyWelcomeMsg(&WelcomeMsg); + TidyWelcomeMsg(&NewWelcomeMsg); + TidyWelcomeMsg(&ExpertWelcomeMsg); + + // redisplay, in case tidy has changed them + + SetDlgItemText(hwndDisplay, IDM_USERMSG, WelcomeMsg); + SetDlgItemText(hwndDisplay, IDM_NEWUSERMSG, NewWelcomeMsg); + SetDlgItemText(hwndDisplay, IDM_EXPERTUSERMSG, ExpertWelcomeMsg); + + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + +VOID SavePrompts() +{ + char Value[10000]; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + GetDlgItemText(hwndDisplay, IDM_USERMSG, Value, 10000); + + free(Prompt); + Prompt = _strdup(Value); + + GetDlgItemText(hwndDisplay, IDM_NEWUSERMSG, Value, 10000); + + free(NewPrompt); + NewPrompt = _strdup(Value); + + GetDlgItemText(hwndDisplay, IDM_EXPERTUSERMSG, Value, 10000); + + free(ExpertPrompt); + ExpertPrompt = _strdup(Value); + + TidyPrompts(); + + // redisplay, in case tidy has changed them + + SetDlgItemText(hwndDisplay, IDM_USERMSG, Prompt); + SetDlgItemText(hwndDisplay, IDM_NEWUSERMSG, NewPrompt); + SetDlgItemText(hwndDisplay, IDM_EXPERTUSERMSG, ExpertPrompt); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + + +VOID SaveWPConfig(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + SendWP = IsDlgButtonChecked(hwndDisplay, IDC_SENDWP); + SendWPType = SendDlgItemMessage(hwndDisplay, IDC_WPTYPE, CB_GETCURSEL, 0, 0); + FilterWPBulls = IsDlgButtonChecked(hwndDisplay, IDC_FILTERWPB); + + SendWPAddrs = GetMultiLineDialogParam(hDlg, IDC_WPTO); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + + +VOID SaveFilters(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + RejFrom = GetMultiLineDialogParam(hDlg, IDC_REJFROM); + RejTo = GetMultiLineDialogParam(hDlg, IDC_REJTO); + RejAt = GetMultiLineDialogParam(hDlg, IDC_REJAT); + RejBID = GetMultiLineDialogParam(hDlg, IDC_REJBID); + + HoldFrom = GetMultiLineDialogParam(hDlg, IDC_HOLDFROM); + HoldTo = GetMultiLineDialogParam(hDlg, IDC_HOLDTO); + HoldAt = GetMultiLineDialogParam(hDlg, IDC_HOLDAT); + HoldBID = GetMultiLineDialogParam(hDlg, IDC_HOLDBID); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + +} + + +VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem) +{ + char Text[32768]; + char Val[32768]; + char * ptr1, * ptr2; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + char ** Value; + + int Len = GetDlgItemText(hDialog, DLGItem, Text, 10000); + + // replace crlf with '|' + + if (Text[strlen(Text)-1] != '\n') // no terminating crlf? + strcat(Text, "\r\n"); + + ptr1 = Text; + ptr2 = Val; + + while (*ptr1) + { + if (*ptr1 == '\r') + { + while (*(ptr1+2) == '\r') // Blank line + ptr1+=2; + + *++ptr1 = '|'; + } + *ptr2++= *ptr1++; + } + + *ptr2++ = 0; + + Value = zalloc(4); // always NULL entry on end even if no values + Value[0] = NULL; + + ptr = Val; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (ptr[0] == 0) // Just had a | (empty string) + break; + + Value = realloc(Value, (Count+2) * sizeof(void *)); + + Value[Count++] = _strdup(ptr); + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + +BOOL GetConfigFromRegistry() +{ + HKEY hKey=0; + int retCode,Type,Vallen, i; + char Size[80]; + char * ptr; + + // Get Config From Registry + + sprintf(BaseDirRaw, "%s/BPQMailChat", GetBPQDirectory()); + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat", 0, KEY_ALL_ACCESS, &hKey); + + if (retCode != ERROR_SUCCESS) + return FALSE; + + { + Vallen=4; + retCode += RegQueryValueEx(hKey,"Streams",0, + (ULONG *)&Type,(UCHAR *)&MaxStreams,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"BBSApplNum",0, + (ULONG *)&Type,(UCHAR *)&BBSApplNum,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "EnableUI", 0, &Type, (UCHAR *)&EnableUI, &Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "MailForInterval", 0, &Type, (UCHAR *)&MailForInterval, &Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "RefuseBulls", 0, &Type, (UCHAR *)&RefuseBulls, &Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"SendSYStoSYSOPCall",0, + (ULONG *)&Type,(UCHAR *)&SendSYStoSYSOPCall,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"SendBBStoSYSOPCall",0, + (ULONG *)&Type,(UCHAR *)&SendBBStoSYSOPCall,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"DontHoldNewUsers",0, + (ULONG *)&Type,(UCHAR *)&DontHoldNewUsers,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"ForwardToMe",0, + (ULONG *)&Type,(UCHAR *)&ForwardToMe,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"AllowAnon",0, + (ULONG *)&Type,(UCHAR *)&AllowAnon,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"DontNeedHomeBBS",0, + (ULONG *)&Type,(UCHAR *)&DontNeedHomeBBS,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"MaxTXSize",0, + (ULONG *)&Type,(UCHAR *)&MaxTXSize,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"MaxRXSize",0, + (ULONG *)&Type,(UCHAR *)&MaxRXSize,(ULONG *)&Vallen); + + AliasText = RegGetMultiStringValue(hKey, "FWD Aliases"); + + Vallen=4; + RegQueryValueEx(hKey, "Readdress Local",0, + (ULONG *)&Type,(UCHAR *)&ReaddressLocal, &Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "Readdress Received",0, + (ULONG *)&Type,(UCHAR *)&ReaddressReceived, &Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "Warn No Route",0, + (ULONG *)&Type,(UCHAR *)&WarnNoRoute, &Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "Localtime",0, + (ULONG *)&Type,(UCHAR *)&Localtime, &Vallen); + + Vallen=100; + retCode += RegQueryValueEx(hKey, "BBSName",0 , &Type, (UCHAR *)&BBSName, &Vallen); + + sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default + + Vallen=100; + retCode += RegQueryValueEx(hKey, "MailForText",0 , &Type, (UCHAR *)&MailForText, &Vallen); + + Vallen=100; + retCode += RegQueryValueEx(hKey,"SYSOPCall",0, + (ULONG *)&Type,(UCHAR *)&SYSOPCall,(ULONG *)&Vallen); + + Vallen=100; + retCode += RegQueryValueEx(hKey,"H-Route",0, + (ULONG *)&Type,(UCHAR *)&HRoute,(ULONG *)&Vallen); + + Vallen=MAX_PATH; + retCode += RegQueryValueEx(hKey,"BaseDir",0, + (ULONG *)&Type,(UCHAR *)&BaseDirRaw,(ULONG *)&Vallen); + + ptr = &BaseDirRaw[strlen(BaseDirRaw) -1]; + + if (*ptr == '\\' || *ptr == '/') + *ptr = 0; + + ExpandEnvironmentStrings(BaseDirRaw, BaseDir, MAX_PATH); + // Get length of Chatnodes String + + Vallen=4; + retCode += RegQueryValueEx(hKey,"SMTPPort",0, + (ULONG *)&Type,(UCHAR *)&SMTPInPort,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"POP3Port",0, + (ULONG *)&Type,(UCHAR *)&POP3InPort,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"NNTPPort",0, + (ULONG *)&Type,(UCHAR *)&NNTPInPort,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"SMTPGatewayEnabled",0, + (ULONG *)&Type,(UCHAR *)&ISP_Gateway_Enabled,(ULONG *)&Vallen); + + Vallen=4; + + Vallen=4; + RegQueryValueEx(hKey,"RemoteEmail",0, + (ULONG *)&Type,(UCHAR *)&RemoteEmail,(ULONG *)&Vallen); + + Vallen=4; + + retCode += RegQueryValueEx(hKey,"POP3 Polling Interval",0, + (ULONG *)&Type,(UCHAR *)&ISPPOP3Interval,(ULONG *)&Vallen); + + Vallen=50; + retCode += RegQueryValueEx(hKey,"MyDomain",0, + (ULONG *)&Type,(UCHAR *)&MyDomain,(ULONG *)&Vallen); + + Vallen=50; + retCode += RegQueryValueEx(hKey,"ISPSMTPName",0, + (ULONG *)&Type,(UCHAR *)&ISPSMTPName,(ULONG *)&Vallen); + + Vallen=50; + retCode += RegQueryValueEx(hKey,"ISPPOP3Name",0, + (ULONG *)&Type,(UCHAR *)&ISPPOP3Name,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"ISPSMTPPort",0, + (ULONG *)&Type,(UCHAR *)&ISPSMTPPort,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"ISPPOP3Port",0, + (ULONG *)&Type,(UCHAR *)&ISPPOP3Port,(ULONG *)&Vallen); + + Vallen=50; + retCode += RegQueryValueEx(hKey,"ISPAccountName",0, + (ULONG *)&Type,(UCHAR *)&ISPAccountName,(ULONG *)&Vallen); + + EncryptedPassLen=50; + retCode += RegQueryValueEx(hKey,"ISPAccountPass",0, + (ULONG *)&Type,(UCHAR *)&EncryptedISPAccountPass,(ULONG *)&EncryptedPassLen); + + DecryptPass(EncryptedISPAccountPass, ISPAccountPass, EncryptedPassLen); + + Vallen=4; + RegQueryValueEx(hKey,"AuthenticateSMTP",0, + (ULONG *)&Type,(UCHAR *)&SMTPAuthNeeded,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"Log_BBS",0, + (ULONG *)&Type,(UCHAR *)&LogBBS,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"Log_TCP",0, + (ULONG *)&Type,(UCHAR *)&LogTCP,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"Log_CHAT",0, + (ULONG *)&Type,(UCHAR *)&LogCHAT,(ULONG *)&Vallen); + + Vallen=80; + RegQueryValueEx(hKey,"MonitorSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + sscanf(Size,"%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom); + + Vallen=80; + RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); + + // Get Welcome Messages + + Vallen=0; + + RegQueryValueEx(hKey,"WelcomeMsg",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + WelcomeMsg = malloc(Vallen); + RegQueryValueEx(hKey,"WelcomeMsg",0, (ULONG *)&Type, WelcomeMsg, (ULONG *)&Vallen); + } + else + WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + RegQueryValueEx(hKey,"NewUserWelcomeMsg",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + NewWelcomeMsg = malloc(Vallen); + RegQueryValueEx(hKey,"NewUserWelcomeMsg",0, (ULONG *)&Type, NewWelcomeMsg, (ULONG *)&Vallen); + } + else + + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + Vallen=0; + + RegQueryValueEx(hKey,"ExpertWelcomeMsg",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + ExpertWelcomeMsg = malloc(Vallen); + RegQueryValueEx(hKey,"ExpertWelcomeMsg",0, (ULONG *)&Type, ExpertWelcomeMsg, (ULONG *)&Vallen); + } + else + ExpertWelcomeMsg = _strdup(""); + + Vallen = 99; + RegQueryValueEx(hKey,"SignoffMsg",0, (ULONG *)&Type, &SignoffMsg[0], (ULONG *)&Vallen); + + // Get Prompts + + Vallen=0; + + RegQueryValueEx(hKey,"Prompt",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + Prompt = malloc(Vallen + 3); + RegQueryValueEx(hKey,"Prompt",0, (ULONG *)&Type, Prompt, (ULONG *)&Vallen); + } + else + { + Prompt = malloc(20); + sprintf(Prompt, "de %s>\r\n", BBSName); + } + + RegQueryValueEx(hKey,"NewUserPrompt",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + NewPrompt = malloc(Vallen + 3); + RegQueryValueEx(hKey,"NewUserPrompt",0, (ULONG *)&Type, NewPrompt, (ULONG *)&Vallen); + } + else + { + NewPrompt = malloc(20); + sprintf(NewPrompt, "de %s>\r\n", BBSName); + } + + RegQueryValueEx(hKey,"ExpertPrompt",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + ExpertPrompt = malloc(Vallen); + RegQueryValueEx(hKey,"ExpertPrompt",0, (ULONG *)&Type, ExpertPrompt, (ULONG *)&Vallen); + } + else + { + ExpertPrompt = malloc(20); + sprintf(ExpertPrompt, "de %s>\r\n", BBSName); + } + + TidyPrompts(); + + RegQueryValueEx(hKey,"NewUserWelcomeMsg",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + NewWelcomeMsg = malloc(Vallen); + RegQueryValueEx(hKey,"NewUserWelcomeMsg",0, (ULONG *)&Type, NewWelcomeMsg, (ULONG *)&Vallen); + } + else + + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + Vallen=0; + + RegQueryValueEx(hKey,"ExpertWelcomeMsg",0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + ExpertWelcomeMsg = malloc(Vallen); + RegQueryValueEx(hKey,"ExpertWelcomeMsg",0, (ULONG *)&Type, ExpertWelcomeMsg, (ULONG *)&Vallen); + } + else + ExpertWelcomeMsg = _strdup(""); + + Vallen=80; + + + RejFrom = RegGetMultiStringValue(hKey, "RejFrom"); + RejTo = RegGetMultiStringValue(hKey, "RejTo"); + RejAt = RegGetMultiStringValue(hKey, "RejAt"); + + HoldFrom = RegGetMultiStringValue(hKey, "HoldFrom"); + HoldTo = RegGetMultiStringValue(hKey, "HoldTo"); + HoldAt = RegGetMultiStringValue(hKey, "HoldAt"); + + // Send WP Params + + Vallen=4; + RegQueryValueEx(hKey, "SendWP", 0, &Type, (UCHAR *)&SendWP, &Vallen); + + Vallen=10; + RegQueryValueEx(hKey,"SendWPTO",0, &Type, &SendWPTO[0],&Vallen); + + Vallen=80; + RegQueryValueEx(hKey,"SendWPVIA",0, &Type,&SendWPVIA[0],&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"SendWPType",0, &Type, (UCHAR *)&SendWPType, &Vallen); + + + if (RegQueryValueEx(hKey,"Version",0, (ULONG *)&Type, (UCHAR *)&Size, (ULONG *)&Vallen) == 0) + sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); + +/* + if ((LastVer[3] != Ver[3]) || (LastVer[2] != Ver[2]) || + (LastVer[1] != Ver[1]) || (LastVer[0] != Ver[0])) + { + // New Version Detected + + if (LastVer[0] == 0) + { + // Pre Version Checking + + MessageBox(NULL, "WARNING - This seems to be the first time you have run this version.\r\n" + "Forwarding has changed significantly. Please read the docs and make the necessary\r\n" + "changes to Forwarding Config. The Software will try to fill in the BBS HA fields from the WP\r\n" + "Database, but check them, and complete the new 'Hierarchical Routes (Flood Bulls)' field.\r\n" + "Network access has been disabled by setting BBS Streams to zero to prevent messages\r\n" + "being lost or incorrecly forwarded. Once you are happy with the forwarding config\r\n" + "you can reset BBS Streams.", + "BPQMailChat", MB_ICONINFORMATION); + + MaxStreams = 0; + + RegSetValueEx(hKey, "Streams", 0, REG_DWORD,(BYTE *)&MaxStreams, 4); + + } + } +*/ + RegCloseKey(hKey); + + for (i=1; i<=32; i++) + { + int retCode; + char Key[100]; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\UIPort%d", i); + + retCode = RegOpenKeyEx (REGTREE, + Key, + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=4; + RegQueryValueEx(hKey,"Enabled",0, + (ULONG *)&Type,(UCHAR *)&UIEnabled[i],(ULONG *)&Vallen); + + UIMF[i] = UIEnabled[i]; // Defaults + UIHDDR[i] = UIEnabled[i]; + + Vallen=4; + RegQueryValueEx(hKey,"SendMF",0, + (ULONG *)&Type,(UCHAR *)&UIMF[i],(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"SendHDDR",0, + (ULONG *)&Type,(UCHAR *)&UIHDDR[i],(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"SendNull",0, + (ULONG *)&Type,(UCHAR *)&UINull[i],(ULONG *)&Vallen); + + Vallen=0; + RegQueryValueEx(hKey,"Digis",0, + (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + UIDigi[i] = malloc(Vallen); + RegQueryValueEx(hKey,"Digis",0, + (ULONG *)&Type, UIDigi[i], (ULONG *)&Vallen); + } + + // retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ,(BYTE *)UIDigi[i], strlen(UIDigi[i])); + + RegCloseKey(hKey); + } + } + + retCode += RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\Housekeeping", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=4; + RegQueryValueEx(hKey,"LastHouseKeepingTime",0, + (ULONG *)&Type,(UCHAR *)&LastHouseKeepingTime,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"LastTrafficTime",0, + (ULONG *)&Type,(UCHAR *)&LastTrafficTime,(ULONG *)&Vallen); + + + Vallen=4; + retCode += RegQueryValueEx(hKey,"MaxMsgno",0, + (ULONG *)&Type,(UCHAR *)&MaxMsgno,(ULONG *)&Vallen); + + if (MaxMsgno > 99000) MaxMsgno = 99000; + + Vallen=4; + RegQueryValueEx(hKey,"LogLifetime",0, + (ULONG *)&Type,(UCHAR *)&LogAge,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"BidLifetime",0, + (ULONG *)&Type,(UCHAR *)&BidLifetime,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"UserLifetime",0, + (ULONG *)&Type,(UCHAR *)&UserLifetime,(ULONG *)&Vallen); + + + Vallen=4; + retCode += RegQueryValueEx(hKey,"MaintInterval",0, + (ULONG *)&Type,(UCHAR *)&MaintInterval,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"MaintTime",0, + (ULONG *)&Type,(UCHAR *)&MaintTime,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"PR",0, + (ULONG *)&Type,(UCHAR *)&PR,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"PUR",0, + (ULONG *)&Type,(UCHAR *)&PUR,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"PF",0, + (ULONG *)&Type,(UCHAR *)&PF,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"PNF",0, + (ULONG *)&Type,(UCHAR *)&PNF,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"BF",0, + (ULONG *)&Type,(UCHAR *)&BF,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey,"BNF",0, + (ULONG *)&Type,(UCHAR *)&BNF,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"NTSD",0, + (ULONG *)&Type,(UCHAR *)&NTSD,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"NTSU",0, + (ULONG *)&Type,(UCHAR *)&NTSU,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"NTSF",0, + (ULONG *)&Type,(UCHAR *)&NTSF,(ULONG *)&Vallen); + +// Vallen=4; +// retCode += RegQueryValueEx(hKey, "AP", 0, +// (ULONG *)&Type,(UCHAR *)&AP,(ULONG *)&Vallen); + +// Vallen=4; +// retCode += RegQueryValueEx(hKey, "AB", 0, +// (ULONG *)&Type,(UCHAR *)&AB,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "DeletetoRecycleBin", 0, + (ULONG *)&Type,(UCHAR *)&DeletetoRecycleBin,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "SuppressMaintEmail", 0, + (ULONG *)&Type,(UCHAR *)&SuppressMaintEmail,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "MaintSaveReg", 0, + (ULONG *)&Type,(UCHAR *)&SaveRegDuringMaint,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "OverrideUnsent", 0, + (ULONG *)&Type,(UCHAR *)&OverrideUnsent,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey, "SendNonDeliveryMsgs", 0, + (ULONG *)&Type,(UCHAR *)&SendNonDeliveryMsgs,(ULONG *)&Vallen); + + LTFROM = RegGetOverrides(hKey, "LTFROM"); + LTTO = RegGetOverrides(hKey, "LTTO"); + LTAT = RegGetOverrides(hKey, "LTAT"); + } + + return TRUE; + } +} + + +INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, n; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (n = 1; n <= NumberofUsers; n++) + { + SendDlgItemMessage(hDlg, IDC_USER, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)UserRecPtr[n]->Call); + } + + return (INT_PTR)TRUE; + + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + + case IDC_USER: + + // User Selection Changed + + Do_User_Sel_Changed(hDlg); + + return TRUE; + + + case IDC_ADDUSER: + + Do_Add_User(hDlg); + return TRUE; + + case IDC_DELETEUSER: + + Do_Delete_User(hDlg); + return TRUE; + + case IDC_SAVEUSER: + + Do_Save_User(hDlg, TRUE); + return TRUE; + + } + break; + } + + return (INT_PTR)FALSE; +} + +INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, n; + char msgno[20]; + struct MsgInfo * Msg; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (n = NumberofMessages; n >= 1; n--) + { + sprintf_s(msgno, sizeof(msgno), "%d", MsgHddrPtr[n]->number); + SendDlgItemMessage(hDlg, 0, LB_ADDSTRING, 0, (LPARAM)msgno); + } + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); + + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "N"); + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "Y"); + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "F"); + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "K"); + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "H"); + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "D"); + SendDlgItemMessage(hDlg, IDC_MSGSTATUS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "$"); + + CheckDlgButton(hDlg,205, BST_INDETERMINATE); + CheckDlgButton(hDlg,206, BST_UNCHECKED); + CheckDlgButton(hDlg,207, BST_CHECKED); + + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case 0: + + // Msg Selection Changed + + Do_Msg_Sel_Changed(hDlg); + + return TRUE; + + case IDC_EDITTEXT: + + if (CurrentMsgIndex == -1) + { + sprintf(InfoBoxText, "Please select a message to Edit"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + if (SendDlgItemMessage(hDlg, 0, LB_GETSELCOUNT, 0, 0) > 1) + { + sprintf(InfoBoxText, "Please select only one message"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITMSGTEXT), hDlg, EditMsgTextDialogProc); + return TRUE; + + case IDC_SAVEMSG: + + if (SendDlgItemMessage(hDlg, 0, LB_GETSELCOUNT, 0, 0) > 1) + { + sprintf(InfoBoxText, "Please select only one message"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + Do_Save_Msg(hDlg); + return TRUE; + + case IDC_EXPORT: + { + struct MsgInfo * Msg; + char FileName[MAX_PATH] = "Export.out"; + OPENFILENAME Ofn; + int Count; + int * Indexes; + int i; + char MsgnoText[10]; + int Msgno; + + if (CurrentMsgIndex == -1) + { + sprintf(InfoBoxText, "Please select a message to Export"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + Count = SendDlgItemMessage(hDlg, 0, LB_GETSELCOUNT, 0, 0); + + Indexes = malloc(Count * sizeof(void *)); + + SendDlgItemMessage(hDlg, 0, LB_GETSELITEMS , Count, (LPARAM)&Indexes[0]); + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT; + Ofn.lpstrTitle = NULL;//; + + if (GetSaveFileName(&Ofn)) + { + FILE * Handle = fopen(FileName, "ab"); + + if (Handle == NULL) + { + sprintf(InfoBoxText, "Failed to open Export File %s", FileName); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + +// SetFilePointer(Handle, 0, 0, FILE_END); + + for (i = 0; i < Count; i++) + { + Msg = MsgHddrPtr[Indexes[i]]; + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); + Msgno = atoi(MsgnoText); + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + ForwardMessagetoFile(Msg, Handle); + break; + } + } + } + fclose(Handle); + } + + free(Indexes); + + return TRUE; + } + + case IDC_SAVETOFILE: + { + struct MsgInfo * Msg; + char * MailBuffer; + char FileName[MAX_PATH] = ""; + int Files = 0; + int BodyLen; + char * ptr; + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + OPENFILENAME Ofn; + char Hddr[1000]; + char FullTo[100]; + + if (CurrentMsgIndex == -1) + { + sprintf(InfoBoxText, "Please select a message to Save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + if (SendDlgItemMessage(hDlg, 0, LB_GETSELCOUNT, 0, 0) > 1) + { + sprintf(InfoBoxText, "Please select only one message"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + + Msg = MsgHddrPtr[CurrentMsgIndex]; + + MailBuffer = ReadMessageFile(Msg->number); + BodyLen = Msg->length; + + sprintf(FileName, "MSG%05d.txt", Msg->number); + + ptr = MailBuffer; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * bptr; + bptr = strstr(ptr, "Body:"); + if (bptr) + { + BodyLen = atoi(bptr + 5); + bptr = strstr(bptr, "\r\n\r\n"); + + if (bptr) + ptr = bptr+4; + } + } + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT; + Ofn.lpstrTitle = NULL;//; + + if (GetSaveFileName(&Ofn)) + { + hFile = CreateFile(FileName, + GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, Hddr, strlen(Hddr), &WriteLen, NULL); + WriteFile(hFile, ptr, BodyLen, &WriteLen, NULL); + CloseHandle(hFile); + } + } + return TRUE; + } + + + + case IDC_PRINTMSG: + { + int Count; + int * Indexes; + + if (CurrentMsgIndex == -1) + { + sprintf(InfoBoxText, "Please select a message to Print"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return TRUE; + } + + Count = SendDlgItemMessage(hDlg, 0, LB_GETSELCOUNT, 0, 0); + + Indexes = malloc(Count * sizeof(void *)); + + SendDlgItemMessage(hDlg, 0, LB_GETSELITEMS , Count, (LPARAM)&Indexes[0]); + + PrintMessages(hDlg, Count, Indexes); + + free(Indexes); + + return TRUE; + } + case FILTER_FROM: + case FILTER_TO: + case FILTER_VIA: + case FILTER_BID: + + if (HIWORD(wParam) == 0x300) + { + GetDlgItemText(hDlg, FILTER_FROM, Filter_FROM, 10); + GetDlgItemText(hDlg, FILTER_TO, Filter_TO, 10); + GetDlgItemText(hDlg, FILTER_VIA, Filter_VIA, 50); + GetDlgItemText(hDlg, FILTER_BID, Filter_BID, 14); + + SendDlgItemMessage(hDlg, 0, LB_RESETCONTENT, 0, 0); + + for (n = NumberofMessages; n >= 1; n--) + { + Msg = MsgHddrPtr[n]; + + if ((!Filter_TO[0] || strstr(Msg->to, Filter_TO)) && + (!Filter_FROM[0] || strstr(Msg->from, Filter_FROM)) && + (!Filter_BID[0] || strstr(Msg->bid, Filter_BID)) && + (!Filter_VIA[0] || strstr(Msg->via, Filter_VIA))) + { + sprintf_s(msgno, sizeof(msgno), "%d", Msg->number); + SendDlgItemMessage(hDlg, 0, LB_ADDSTRING, 0, (LPARAM)msgno); + } + } + } + + return TRUE; + + } + break; + } + + return (INT_PTR)FALSE; +} + +char HRHelpMsg[] = +"Please read the following carefully, as forwarding is handled rather differently from other BBS software\r\n" +"Private Messages, and Bulls that have not reached their target area (eg a Bull sent to ALL@GBR from the\r\n" +"USA) are forwarded to only one define define BBS1 with HR EU and BBS2 with HR GBR.EU, a message for GBR.EU\r\n" +"will be sent to BBS2. Any other EU message (eg FRA.EU) would be sent to BBS2." +"\r\n\r\n" +"Bulls which have reached their target will be sent to ALL BBS's where the BBS HA matches all elements\r\n" +"of the HA of the message\r\n So if a BBS had\r\n" +"GBR.EU It would match messages sent to EU or GBR.EU, but not FRA.EU.\r\n" +"If you want to send only Bulls addressed to a lower level then add the number of levels to ignore after the string\r\n" +"So #23.GBR.EU,2 would match only Bulls for #23, and not GBR or EU\r\r" +"The software assumes an implied WW on the end of all aadresses, but only if there is something in the field\r\n" +"So you need an explicit WW to send to everyone. So a BBS with WW in the HA will get all Bulls, and any Personal\r\n" +"Messages that don't have a more explicit route via another BBS" +; + +INT_PTR CALLBACK HRHelpProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + + SetDlgItemText(hDlg, IDC_HRTEXT, HRHelpMsg); + + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +#include + +int scrolledx; scrolledy; + +INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command; + struct UserInfo * user; + RECT Rect; + SCROLLINFO Sinfo; + int deltax, deltay; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (user = BBSChain; user; user = user->BBSNext) + { + SendDlgItemMessage(hDlg, IDC_BBS, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)user->Call); + } + + SetDlgItemInt(hDlg, IDC_MAXSEND, MaxTXSize, FALSE); + SetDlgItemInt(hDlg, IDC_MAXRECV, MaxRXSize, FALSE); + SetDlgItemInt(hDlg, IDC_MAXAGE, MaxAge, FALSE); + + if (Aliases) + { + char Text[100000] = ""; + int i=0; + + while(Aliases[i]) + { + strcat(Text, Aliases[i]->Dest); + strcat(Text, ":"); + strcat(Text, Aliases[i]->Alias); + strcat(Text, "\r\n"); + i++; + } + SetDlgItemText(hDlg, IDC_ALIAS, Text); + } + + + CheckDlgButton(hDlg, IDC_READDRESSLOCAL, ReaddressLocal); + CheckDlgButton(hDlg, IDC_READDRESSRXED, ReaddressReceived); + CheckDlgButton(hDlg, IDC_WARNNOROUTE, WarnNoRoute); + CheckDlgButton(hDlg, IDC_USELOCALTIME, Localtime); + CheckDlgButton(hDlg, IDC_MULTIP, SendPtoMultiple); + + CurrentBBS = NULL; + + ww = 0; + wh = 0; + + ShowScrollBar(hDlg, SB_BOTH, FALSE); // Hide them till needed + + xmargin = 6; + ymargin = 2 + GetSystemMetrics(SM_CYCAPTION); + scrolledx = scrolledy = 0; + + GetWindowRect(hDlg, &Rect); + ww = Rect.right - Rect.left; + wh = Rect.bottom - Rect.top; + + return (INT_PTR)TRUE; + + case WM_SIZE: + + w = LOWORD(lParam); + h = HIWORD(lParam); + + // If window is smaller than client area enable scroll bars + + if (w >= ww && (h + ymargin) >= wh) + { + ShowScrollBar(hDlg, SB_BOTH, FALSE); // Hide them till needed +// MoveWindow(hwndDisplay, xmargin, ymargin, ww, wh, TRUE); + ScrollWindow(hDlg, scrolledx, scrolledy, 0, 0); + scrolledx = scrolledy = 0; + return TRUE; + } + + ShowScrollBar(hDlg, SB_BOTH, TRUE); + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = ww + xmargin; + Sinfo.nPage = w; + Sinfo.nPos = hpos; + SetScrollInfo(hDlg, SB_HORZ, &Sinfo, TRUE); + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = wh + ymargin; + Sinfo.nPage = h; + Sinfo.nPos = hpos; + SetScrollInfo(hDlg, SB_VERT, &Sinfo, TRUE); + + return TRUE; + + case WM_HSCROLL: + + switch (LOWORD(wParam)) + { + case SB_PAGELEFT: + + goto UpdateHPos; + + case SB_LINELEFT: + + goto UpdateHPos; + + case SB_PAGERIGHT: + + goto UpdateHPos; + + case SB_LINERIGHT: + + hpos++; + goto UpdateHPos; + + case SB_THUMBPOSITION: + + deltax = hpos - HIWORD(wParam); + + ScrollWindow(hDlg, deltax, 0, 0, 0); + scrolledx -= deltax; + + hpos = hpos -= deltax; + +UpdateHPos: + // Need to update Scroll Bar + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = ww + xmargin; + Sinfo.nPage = w; + Sinfo.nPos = hpos; + SetScrollInfo(hDlg, SB_HORZ, &Sinfo, TRUE); + + // Move Client Window + + return TRUE; + } + + return TRUE; + + + case WM_VSCROLL: + + switch (LOWORD(wParam)) + { + case SB_PAGEUP: + + goto UpdateVPos; + + case SB_LINEUP: + + goto UpdateVPos; + + case SB_PAGEDOWN: + + goto UpdateVPos; + + case SB_LINEDOWN: + + goto UpdateVPos; + + case SB_THUMBPOSITION: + + deltay = vpos - HIWORD(wParam); + + ScrollWindow(hDlg,0, deltay, 0, 0); + scrolledy -= deltay; + + vpos = vpos -= deltay; + +UpdateVPos: + // Need to update Scroll Bar + + Sinfo.cbSize = sizeof(SCROLLINFO); + Sinfo.fMask = SIF_ALL; + Sinfo.nMin = 0; + Sinfo.nMax = wh + ymargin; + Sinfo.nPage = h; + Sinfo.nPos = vpos; + SetScrollInfo(hDlg, SB_VERT, &Sinfo, TRUE); + return TRUE; + } + + return TRUE; + + + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDC_BBS: + + // BBS Selection Changed + + Do_BBS_Sel_Changed(hDlg); + + return TRUE; + + case IDC_HRHELP: + + ShellExecute(hDlg,"open", + "http://www.cantab.net/users/john.wiseman/Documents/BPQ Mail and Chat Server Mail Forwarding.htm", + "", NULL, SW_SHOWNORMAL); + + return TRUE; + + case IDC_FWDSAVE: + + SaveFWDConfig(hDlg); + return TRUE; + + case COPYCONFIG: + + CopyFwdConfig(hDlg); + return TRUE; + + + } + break; + } + + return (INT_PTR)FALSE; +} + + + +int CreateDialogLine(HWND hWnd, int i, int row) +{ + char PortNo[60]; + char PortDesc[31]; + + // Only allow UI on ax.25 ports + + struct _EXTPORTDATA * PORTVEC; + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + if (PORTVEC->PORTCONTROL.UICAPABLE == 0) + return FALSE; + + GetPortDescription(i, PortDesc); + sprintf(PortNo, "Port %2d %30s", GetPortNumber(i), PortDesc); + + hCheck[i] = CreateWindow(WC_BUTTON , "", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + 10,row+5,14,14, hWnd, NULL, hInst, NULL); + + Button_SetCheck(hCheck[i], UIEnabled[i]); + + hLabel[i] = CreateWindow(WC_STATIC , PortNo, WS_CHILD | WS_VISIBLE, + 30,row+5,300,22, hWnd, NULL, hInst, NULL); + + SendMessage(hLabel[i], WM_SETFONT,(WPARAM) hFont, 0); + + + hUIBox[i] = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT , "", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_UPPERCASE, + 315,row,200,22, hWnd, NULL, hInst, NULL); + + SendMessage(hUIBox[i], WM_SETFONT,(WPARAM) hFont, 0); + SetWindowText(hUIBox[i], UIDigi[i]); + + hSendMF[i] = CreateWindow(WC_BUTTON , "", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + 550,row+4,14,14, hWnd, NULL, hInst, NULL); + + hSendHDDR[i] = CreateWindow(WC_BUTTON , "", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + 610,row+4,14,14, hWnd, NULL, hInst, NULL); + + hNullCheck[i] = CreateWindow(WC_BUTTON , "", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + 670,row+4,14,14, hWnd, NULL, hInst, NULL); + + Button_SetCheck(hSendMF[i], UIMF[i]); + Button_SetCheck(hSendHDDR[i], UIHDDR[i]); + Button_SetCheck(hNullCheck[i], UINull[i]); + + return TRUE; +} + + + +DoUICheck(int i) +{ + return TRUE; +} +DoUIBox(int i) +{ + return TRUE; +} + +GetUIConfig() +{ + int Num = GetNumberofPorts(); + int i, Len; + + Free_UI(); + + for (i=1; i<=Num; i++) + { + UIEnabled[i] = Button_GetCheck(hCheck[i]); + UINull[i] = Button_GetCheck(hNullCheck[i]); + UIMF[i] = Button_GetCheck(hSendMF[i]); + UIHDDR[i] = Button_GetCheck(hSendHDDR[i]); + + Len = GetWindowTextLength(hUIBox[i]); + + UIDigi[i] = malloc(Len+1); + GetWindowText(hUIBox[i], UIDigi[i], Len+1); + } + + return TRUE; +} + +INT_PTR CALLBACK UIDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, i; + RECT Rect; + int Row = 80; + + switch (message) + { + case WM_INITDIALOG: + + SetDlgItemText(hDlg, IDC_MAILFOR, MailForText); + + for (i = 1; i <= GetNumberofPorts(); i++) + { + if (CreateDialogLine(hDlg, i, Row)) + Row += 30; + + } + + GetWindowRect(hDlg, &Rect); + SetWindowPos(hDlg, HWND_TOP, Rect.left, Rect.top, 800, Row+100, 0); + SetWindowPos(GetDlgItem(hDlg, IDOK), NULL, 300, Row+20, 70, 30, 0); + SetWindowPos(GetDlgItem(hDlg, IDCANCEL), NULL, 400, Row+20, 80, 30, 0); + + + return (INT_PTR)TRUE; + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDOK: + + GetDlgItemText(hDlg, IDC_MAILFOR, MailForText, 99); + GetUIConfig(); + + SaveConfig(ConfigName); + + sprintf(InfoBoxText, "Configuration Saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case 0: + + for (i = 1; i <= 32; i++) + { + if (lParam == (LPARAM)hCheck[i]) + { + DoUICheck(i); + break; + } + else if (lParam == (LPARAM)hUIBox[i]) + { + DoUIBox(i); + return TRUE; + + } + } + } + + break; + } + + return (INT_PTR)FALSE; +} + +INT_PTR CALLBACK EditMsgTextDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct MsgInfo * Msg; + char * MsgBytes; + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + HWND hWndEdit = GetDlgItem(hDlg, IDC_MESSAGE); + + Msg = MsgHddrPtr[CurrentMsgIndex]; + + MsgBytes = ReadMessageFile(Msg->number); + + // See if Multipart + + if (Msg->B2Flags & Attachments) + EnableWindow(GetDlgItem(hDlg, IDC_SAVEATTACHMENTS), TRUE); + + if (MsgBytes) + { + SetDlgItemText(hDlg, IDC_MESSAGE, MsgBytes); + SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + free (MsgBytes); + } + return TRUE; + } + + case WM_SIZING: + { + HWND hWndEdit = GetDlgItem(hDlg, IDC_MESSAGE); + + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndEdit, 5, 50, Width-20, Height - 95, TRUE); + + return TRUE; + } + + case WM_ACTIVATE: + + SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + if (Cmd == IDC_SAVEATTACHMENTS) + { + struct MsgInfo * Msg; + char * MailBuffer; + char FileName[100][MAX_PATH] = {""}; + int FileLen[100]; + int Files = 0; + int BodyLen; + int i; + char * ptr; + + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + + Msg = MsgHddrPtr[CurrentMsgIndex]; + + MailBuffer = ReadMessageFile(Msg->number); + + ptr = MailBuffer; + + while(*ptr != 13) + { + char * ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + memcpy(FileName[Files++], &ptr1[1], (ptr2-ptr1 - 2)); + } + + ptr = ptr2; + ptr++; + } + + ptr += 4; // Over Blank Line and Separator + ptr += BodyLen; // to first file + + for (i = 0; i < Files; i++) + { + OPENFILENAME Ofn; + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName[i]; + Ofn.nMaxFile = sizeof(FileName[i])/ sizeof(*FileName[i]); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT; + Ofn.lpstrTitle = NULL;//; + + if (GetSaveFileName(&Ofn)) + { + hFile = CreateFile(FileName[i], + GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, ptr, FileLen[i], &WriteLen, NULL); + CloseHandle(hFile); + } + } + + ptr += FileLen[i]; + ptr +=2; // Over separator - I don't think there should be one + } + } + + if (Cmd == IDSAVE) + { + struct MsgInfo * Msg; + char * via = NULL; + int MsgLen; + char * MailBuffer; + char MsgFile[MAX_PATH]; + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->B2Flags & Attachments) + { + MessageBox(NULL, "It isn't safe to save messages with attachments", "BPQMail", MB_ICONERROR); + return TRUE; + } + + + MsgLen = SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_GETTEXTLENGTH, 0 ,0); + + if (MsgLen) + { + MailBuffer = malloc(MsgLen+1); + GetDlgItemText(hDlg, IDC_MESSAGE, MailBuffer, MsgLen+1); + } + + Msg->datechanged = time(NULL); + Msg->length = MsgLen; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = CreateFile(MsgFile, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, MailBuffer, Msg->length, &WriteLen, NULL); + CloseHandle(hFile); + } + + free(MailBuffer); + + EndDialog(hDlg, LOWORD(wParam)); + + return TRUE; + } + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} diff --git a/BPQMailrc.h b/BPQMailrc.h new file mode 100644 index 0000000..e13267c --- /dev/null +++ b/BPQMailrc.h @@ -0,0 +1,330 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by BPQMail.rc +// +#define IDC_MYICON 2 +#define IDSENDTOMAP 3 +#define IDSelectFiles 3 +#define IDI_ICON1 101 +#define IDD_CONFIG 102 +#define IDS_APP_TITLE 103 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDD_PROPPAGE_LARGE 107 +#define IDC_BPQMailChat 109 +#define IDM_CONFIG 110 +#define IDM_CONSOLE 120 +#define IDM_MONITOR 121 +#define IDR_MAINFRAME 128 +#define IDD_USEREDIT 200 +#define IDD_FORWARDING 201 +#define IDD_MSGEDIT 202 +#define IDC_NODES 501 +#define IDC_USERS 502 +#define IDC_LINKS 503 +#define IDC_SYSOPMSGS 504 +#define IDC_NODES2 505 +#define IDC_FWDINT 505 +#define IDC_MAXSEND 506 +#define IDC_UTC 506 +#define IDC_MAXRECV 507 +#define IDC_LOCAL 507 +#define IDC_MAXBLOCK 508 +#define IDC_MSGS 508 +#define IDC_BBSHA 509 +#define IDC_HELD 509 +#define IDC_SMTP 510 +#define IDC_REVFWDINT 510 +#define IDC_MAXAGE 511 +#define IDC_FWDINT2 512 +#define IDC_CONTIMEOUT 512 +#define IDC_USERS2 513 +#define IDC_MSGSEM 514 +#define IDC_ALLOCSEM 515 +#define IDC_CONSEM 517 +#define COPYFROMCALL 518 +#define IDC_BBSCall 1001 +#define IDC_BaseDir 1002 +#define IDC_BBSAppl 1003 +#define IDC_BBSStreams 1004 +#define IDC_POP3Port 1005 +#define IDC_REMOVED 1006 +#define IDC_SMTPPort 1006 +#define IDC_KILLED 1007 +#define IDC_HRoute 1007 +#define IDC_LIVE 1008 +#define IDC_SYSOPCALL 1008 +#define IDC_TOTAL 1009 +#define IDC_REMOTEEMAIL 1009 +#define IDC_BIDSREMOVED 1010 +#define IDC_UIPORTS 1010 +#define IDC_NNTPPort 1010 +#define IDC_BIDSLEFT 1011 +#define IDC_AMPR 1011 +#define IDC_ENABLEUI 1012 +#define IDC_USEB2 1013 +#define IDC_REFUSEBULLS 1013 +#define IDC_CHATSEM 1014 +#define IDC_PERSONALONLY 1014 +#define MAILFOR_MINS 1014 +#define IDC_SENDNEW 1015 +#define IDC_FORWARDAMPR 1015 +#define IDC_UICONFIG 1016 +#define IDC_USELOCALTIME 1016 +#define IDC_DELETETORECYCLE 1017 +#define IDC_CTRLZ 1017 +#define IDC_KNOWNUSERS 1017 +#define IDC_MAINTNOMAIL 1018 +#define IDC_EDIT1 1019 +#define IDC_MSGTO 1020 +#define IDC_WPVIA 1020 +#define IDC_MSGTITLE 1021 +#define IDC_MAINTSAVEREG 1021 +#define IDC_MAINTTRAFFIC 1021 +#define IDC_MSGBID 1022 +#define IDC_MAINTNONDELIVERY 1022 +#define IDSEND 1023 +#define IDC_MAINTSAVEREG2 1023 +#define IDCANCELMSG 1024 +#define BPQBASE 1024 +#define IDC_ATTACHMENTS 1024 +#define IDC_ALIAS 1025 +#define IDC_HRTEXT 1027 +#define IDC_HROUTESP 1029 +#define IDC_LASTLISTED 1030 +#define IDC_MESSAGE 1031 +#define IDSAVE 1032 +#define IDM_USERMSG 1034 +#define IDM_CHATUSERMSG 1035 +#define IDM_NEWUSERMSG 1036 +#define IDM_MSGSAVE 1037 +#define IDM_EXPERTUSERMSG 1038 +#define IDC_MAPPOSITION 1039 +#define IDM_SIGNOFF 1039 +#define IDC_HOVER 1040 +#define BPQMTX 1040 +#define IDC_CLICK 1041 +#define BPQMCOM 1041 +#define IDC_MAPHELP 1042 +#define BPQCOPYMON 1042 +#define IDC_POPUPTEXT 1043 +#define BPQCOPYOUT 1043 +#define IDC_SAVEATTACHMENTS 1044 +#define BPQCLEARMON 1044 +#define CONN_OUT 1045 +#define BPQCLEAROUT 1045 +#define MSGS_OUT 1046 +#define BPQBELLS 1046 +#define MSGS_IN 1047 +#define BPQCHAT 1047 +#define REJECTS_IN 1048 +#define BPQHELP 1048 +#define REJECTS_OUT 1049 +#define BPQStripLF 1049 +#define BYTES_OUT 1050 +#define BPQLogOutput 1050 +#define BYTES_IN 1051 +#define BPQLogMonitor 1051 +#define LASTCONNECT 1052 +#define BPQSendDisconnected 1052 +#define CONN_IN 1053 +#define BPQFLASHONBELL 1053 +#define MONBBS 1060 +#define MONCHAT 1061 +#define MONTCP 1062 +#define IDC_CHATCALLS 1064 +#define IDC_CHATCOLOURS 1065 +#define RMS_EXPRESS_USER 1066 +#define NO_WINLINKdotORG 1067 +#define ALLOW_BULLS 1068 +#define RMS_SSID1 1070 +#define RMS_SSID2 1071 +#define RMS_SSID3 1072 +#define IDC_SYSTOSYSOPCALL 1073 +#define IDC_DONTHOLDNEW 1074 +#define IDC_FORWARDTOBBS 1075 +#define IDC_BBSTOSYSOPCALL 1076 +#define IDC_NOHOMEBBS 1077 +#define IDC_NONAME 1078 +#define IDC_USERRKILLT 1079 +#define IDC_FILTERSAVE 1080 +#define IDC_OVERRIDEUNSENT 1081 +#define IDC_REJBID 1081 +#define IDC_MAILFOR 1082 +#define IDC_HOLDBID 1082 +#define IDC_SENDWP 1083 +#define IDC_FILTERWPB 1084 +#define IDC_WPTYPE 1085 +#define IDC_WPTO 1086 +#define IDC_WPSAVE 1087 +#define IDC_PRINTMSG 1088 +#define IDC_EXPORT 1089 +#define IDM_PROMPTSAVE 1090 +#define IDC_RMSBULL 1090 +#define BBSUSERCHAT 1091 +#define IDC_BBSSAVE 1500 +#define ENDUSERCHAT 1501 +#define IDM_DISCONNECT 2000 +#define IDC_ChatAppl 2001 +#define IDC_ChatNodes 2002 +#define IDC_DONTCHECKFROM 2010 +#define IDM_LOGGING 2100 +#define IDC_CHATSAVE 2100 +#define IDC_ISP_Gateway_Enabled 3000 +#define IDC_MyMailDomain 3001 +#define IDC_ISPSMTPName 3002 +#define IDC_ISPSMTPPort 3003 +#define IDC_ISPPOP3Name 3004 +#define IDC_ISPPOP3Port 3005 +#define IDC_ISPAccountName 3006 +#define IDC_ISPAccountPass 3007 +#define IDC_POP3Timer 3008 +#define ISP_SMTP_AUTH 3009 +#define SMTP_EHELO 3010 +#define IDC_ISPSAVE 3100 +#define IDC_FWDENABLE 4000 +#define IDC_BBS 4001 +#define IDC_CALL 4002 +#define IDC_TOCALLS 4003 +#define IDC_ATCALLS 4004 +#define IDC_TOCALLS2 4004 +#define IDC_HROUTES 4005 +#define IDC_TOCALLS3 4005 +#define IDC_REVERSE 4006 +#define IDC_TOCALLS4 4006 +#define IDC_FWDTIMES 4008 +#define IDC_FWDSAVE 4100 +#define IDC_HRHELP 4101 +#define COPYCONFIG 4102 +#define IDC_WP 5000 +#define IDC_USER 5000 +#define IDC_WPNAME 5001 +#define IDC_NAME 5001 +#define IDC_ZIP1 5002 +#define IDC_PASSWORD 5002 +#define IDC_QTH1 5003 +#define IDC_ZIP 5003 +#define IDC_QTH 5003 +#define IDC_HOMEBBS1 5004 +#define IDC_HOMEBBS 5004 +#define IDC_HOMEBBS2 5005 +#define IDC_BBSFLAG 5005 +#define IDC_QTH2 5006 +#define IDC_PMSFLAG 5006 +#define IDC_ZIP2 5007 +#define IDC_SYSOP 5007 +#define IDC_LASTSEEN 5008 +#define IDC_EXPERT 5008 +#define IDC_LASTMODIFIED 5009 +#define IDC_EXCLUDED 5009 +#define IDC_TYPE 5010 +#define IDC_EMAIL 5010 +#define IDC_CHANGED 5011 +#define IDC_HOLDMAIL 5011 +#define IDC_SEEN 5012 +#define IDC_POLLRMS 5012 +#define IDC_SYSOP_IN_LM 5013 +#define IDC_UZIP 5014 +#define IDC_SYSOP_IN_LMx 5015 +#define IDC_UZIP2 5015 +#define IDC_CMSPASS 5015 +#define IDC_APRSSSID 5016 +#define IDD_USERADDED_BOX 5051 +#define IDD_UserEditInfo 5051 +#define IDC_NTSMPS 5060 +#define IDC_APRSMFOR 5061 +#define IDC_RMSREDIRECT 5062 +#define IDC_ADDUSER 5100 +#define IDC_DELETEUSER 5101 +#define IDC_SAVEUSER 5102 +#define FILTER_FROM 6006 +#define FILTER_TO 6007 +#define FILTER_VIA 6008 +#define EMAILFROM 6009 +#define FILTER_BID 6010 +#define IDC_MSGTYPE 6101 +#define IDC_MSGSTATUS 6102 +#define IDC_SAVEMSG 6103 +#define IDC_EDITTEXT 6104 +#define IDC_SAVETOFILE 6105 +#define IDM_FORWARD_ALL 7000 +#define IDC_HOLDFROM 7074 +#define IDC_HOLDTO 7075 +#define IDC_HOLDAT 7076 +#define IDC_REJFROM 7077 +#define IDC_REJTO 7078 +#define IDC_REJAT 7079 +#define IDM_HOUSEKEEPING 9000 +#define IDM_PR 9001 +#define IDM_PUR 9002 +#define IDM_PF 9003 +#define IDM_PNF 9004 +#define IDM_BF 9005 +#define IDM_BNF 9006 +#define IDM_NTSF 9007 +#define IDM_NTSU 9008 +#define IDM_LTFROM 9009 +#define IDM_LTTO 9010 +#define IDM_LTAT 9011 +#define IDM_MAINTSAVE 9012 +#define IDM_NTSD 9013 +#define IDC_MAXMSG 9021 +#define IDC_BIDLIFETIME 9022 +#define IDC_MAINTINTERVAL 9023 +#define IDC_MAINTTIME 9024 +#define IDC_LOGLIFETIME 9025 +#define IDC_USERLIFETIME 9026 +#define IDC_USEB1 9876 +#define IDC_READDRESSLOCAL 9877 +#define IDC_READDRESSRXED 9878 +#define IDC_ALLOWCOMP 9879 +#define IDC_BLOCKED 9880 +#define IDC_WARNNOROUTE 9881 +#define IDD_MAINTRESULTS 30001 +#define IDD_EDITWP 30002 +#define IDD_UICONFIG 30003 +#define IDD_MSGFROMCLIPBOARD 30004 +#define IDD_HRHELP 30005 +#define IDD_EDITMSGTEXT 30006 +#define IDD_UPDATECHATMAP 30007 +#define IDD_CHATCOLCONFIG 30008 +#define IDR_MENU1 30010 +#define IDD_RMSBULLDLG 30011 +#define IDM_USERS 40001 +#define IDM_FWD 40002 +#define IDM_MESSAGES 40003 +#define IDM_WRAPTEXT 40004 +#define IDM_WARNINPUT 40005 +#define IDM_Flash 40006 +#define IDM_CLOSEWINDOW 40007 +#define IDM_WP 40008 +#define IDC_SAVEWP 40009 +#define IDC_DELETEWP 40010 +#define IDM_DEBUG 40011 +#define IDM_LOGBBS 40012 +#define IDM_LOGTCP 40013 +#define IDM_LOGCHAT 40014 +#define ID_ACTIONS_SENDMSGFROMCLIPBOARD 40015 +#define IDM_MCMONITOR 40016 +#define ID_ACTIONS_UPDATECHATMAPINFO 40017 +#define IDM_EDITCHATCOLOURS 40018 +#define BPQSAVEREG 40019 +#define RESCANMSGS 40020 +#define ID_HELP_ONLINEHELP 40021 +#define ID_ACTIONS_SENDMESSAGE 40022 +#define IDM_IMPORT 40023 +#define ID_MULTICAST 40024 +#define IDC_DEFAULTNOWINLINK 41001 +#define IDC_MULTIP 41002 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 30012 +#define _APS_NEXT_COMMAND_VALUE 40027 +#define _APS_NEXT_CONTROL_VALUE 1093 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/BPQMailrc.hm b/BPQMailrc.hm new file mode 100644 index 0000000..cb0be11 --- /dev/null +++ b/BPQMailrc.hm @@ -0,0 +1,6 @@ +// Microsoft Developer Studio generated Help ID include file. +// Used by BPQMail.rc +// +#define HFILTER_BID 0x80ca177a +#define HFILTER_VIA 0x80ca1778 +#define HIDC_POLLRMS 0x80c81394 diff --git a/BPQNRR.c b/BPQNRR.c new file mode 100644 index 0000000..4b4ad75 --- /dev/null +++ b/BPQNRR.c @@ -0,0 +1,193 @@ +/* +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 +*/ + +// +// Netrom Record ROute Suport Code for BPQ32 Switch +// + +// All code runs from the BPQ32 Received or Timer Routines under Semaphore. +// As most data areas are dynamically allocated, they will not survive a Timer Process Swap. +// Shared data can be used for Config Info. + + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include +//#include "vmm.h" + + +#include "CHeaders.h" + + +extern int SENDNETFRAME(); +extern VOID Q_ADD(); + +VOID __cdecl Debugprintf(const char * format, ...); + +TRANSPORTENTRY * NRRSession; + +/* +datagrams (and other things) to be transported in Netrom L3 frames. +When the frametype is 0x00, the "circuit index" and "circuit id" (first 2 +bytes of the transport header) take on a different meaning, something like +"protocol family" and "protocol id". IP over netrom uses 0x0C for both +bytes, TheNet uses 0x00 for both bytes when making L3RTT measurements, and +Xnet uses family 0x00, protocol id 0x01 for Netrom Record Route. I believe +there are authors using other values too. Unfortunately there is no +co-ordinating authority for these numbers, so authors just pick an unused +one. +*/ + +VOID NRRecordRoute(UCHAR * Buff, int Len) +{ + // NRR frame for us. If We originated it, report outcome, else put our call on end, and send back + + L3MESSAGEBUFFER * Msg = (L3MESSAGEBUFFER *)Buff; + struct DEST_LIST * DEST; + char Temp[7]; + int NRRLen = Len - (21 + MSGHDDRLEN); + UCHAR Flags; + char call[10]; + int calllen; + char * Save = Buff; + + if (memcmp(&Msg->L4DATA, MYCALL, 7) == 0) + { + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[MSGHDDRLEN]; + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "NRR Response:"); + + Buff += 21 + MSGHDDRLEN; + Len -= (21 + MSGHDDRLEN); + + while (Len > 0) + { + calllen = ConvFromAX25(Buff, call); + call[calllen] = 0; + ptr1 += sprintf(ptr1, " %s", call); + if ((Buff[7] & 0x80) == 0x80) // Check turnround bit + *ptr1++ = '*'; + + Buff+=8; + Len -= 8; + } + + // Add ours on end for neatness + + calllen = ConvFromAX25(MYCALL, call); + call[calllen] = 0; + ptr1 += sprintf(ptr1, " %s", call); + + *ptr1++ = 0x0d; // CR + + Len = (int)(ptr1 - BUFFER); + + Msg = (struct _MESSAGE *)BUFFER; + + Msg->LENGTH = Len; + + Msg->CHAIN = NULL; + + C_Q_ADD(&NRRSession->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(NRRSession); + + ReleaseBuffer(Save); + + return; + } + + // Add our call on end, and increase count + + Flags = Buff[Len - 1]; + + Flags--; + + if (Flags && NRRLen < 228) // Dont update if full + { + Flags |= 0x80; // Set End of route bit + + Msg->L3PID = NRPID; + + memcpy(&Msg->L4DATA[NRRLen], MYCALL, 7); + Msg->L4DATA[NRRLen+7] = Flags; + NRRLen += 8; + } + + // We should send it back via our bast route, or recorded route could be wrong + + memcpy(Temp, Msg->L3DEST, 7); + memcpy(Msg->L3DEST, Msg->L3SRCE, 7); + memcpy(Msg->L3SRCE, Temp, 7); + + if (FindDestination(Msg->L3DEST, &DEST) == 0) + { + ReleaseBuffer(Msg); // CANT FIND DESTINATION + return; + } + + Msg->LENGTH = NRRLen + 21 + MSGHDDRLEN; + + Debugprintf("NRR TX Len %d Flags %d NRRLen %d", Msg->LENGTH, Flags, NRRLen); + + C_Q_ADD(&DEST->DEST_Q, Msg); +} + + +VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session) +{ + L3MESSAGEBUFFER * Msg = GetBuff(); + int Stream = 1; + + if (Msg == NULL) + return; + + NRRSession = Session; // Save Session Pointer for reply + + Msg->Port = 0; + Msg->L3PID = NRPID; + + memcpy(Msg->L3DEST, DEST->DEST_CALL, 7); + memcpy(Msg->L3SRCE, MYCALL, 7); + + Msg->L3TTL = L3LIVES; + Msg->L4ID = 1; + Msg->L4INDEX = 0; + Msg->L4FLAGS = 0; + + memcpy(Msg->L4DATA, MYCALL, 7); + Msg->L4DATA[7] = Stream + 28; + + Msg->LENGTH = 8 + 21 + MSGHDDRLEN; + + C_Q_ADD(&DEST->DEST_Q, Msg); +} diff --git a/BPQRemotePTT.c b/BPQRemotePTT.c new file mode 100644 index 0000000..2d1a74b --- /dev/null +++ b/BPQRemotePTT.c @@ -0,0 +1,988 @@ + +// Program to Convert RTS changes on Virtual COM Port to hamlib PTT commands + + +// Version 1. 0. 0. 1 December 2020 + +// Version 1. 0. 2. 1 April 2018 + + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#include +#include +#include +#include +#include + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + +#include "BPQRemotePTTRes.h" + +#define BPQICON 400 + +WSADATA WsaData; // receives data from WSAStartup + +#define WSA_READ WM_USER + 1 + +HINSTANCE hInst; +char AppName[] = "BPQRemotePTT"; +char Title[80] = "BPQRemotePTT"; + +TCHAR szTitle[]="GPSMuxPC"; // The title bar text +TCHAR szWindowClass[]="GPSMAINWINDOW"; // the main window class name + + +// Foward declarations of functions included in this code module: + +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); +VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID WINAPI txCompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID CATThread(); +VOID runTimer(); + +int TimerHandle = 0; + +LOGFONT LFTTYFONT ; + +HFONT hFont; + +struct sockaddr_in sinx; +struct sockaddr rx; + +int addrlen = sizeof(struct sockaddr_in); + +int HAMLIBPORT; // Port Number for HAMLIB (rigctld) Emulator +char * HAMLIBHOST[128]; + +BOOL MinimizetoTray=FALSE; + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[255]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + + OutputDebugString(Mess); + + return; +} + + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +char * RigPort = NULL; +char * RigSpeed = NULL; +HANDLE RigHandle = 0; +int RigType = 0; // Flag for possible RTS/DTR + + +char BPQHostIP[128]; + +char PTTCATPort[4][16]; +HANDLE PTTCATHandle[4]; +short HamLibPort[4]; +SOCKET HamLibSock[4]; // rigctld socket + +HWND comWnd[4]; +HWND portWnd[4]; +HWND stateWnd[4]; +int stateCtl[4]; + +struct sockaddr_in remoteDest[4]; + +int RealMux[4]; // BPQ Virtual or Real + +int EndPTTCATThread = 0; + +char ConfigName[256] = "BPQRemotePTT.cfg"; + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} +VOID GetConfig() +{ + config_setting_t *group; + config_t cfg; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if(!config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "Config read error line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group == NULL) + { + config_destroy(&cfg); + return; + } + + + GetStringValue(group, "BPQHostIP", BPQHostIP); + + GetStringValue(group, "COM1", PTTCATPort[0]); + GetStringValue(group, "COM2", PTTCATPort[1]); + GetStringValue(group, "COM3", PTTCATPort[2]); + GetStringValue(group, "COM4", PTTCATPort[3]); + + HamLibPort[0] = GetIntValue(group, "HamLibPort1"); + HamLibPort[1] = GetIntValue(group, "HamLibPort2"); + HamLibPort[2] = GetIntValue(group, "HamLibPort3"); + HamLibPort[3] = GetIntValue(group, "HamLibPort4"); + + config_destroy(&cfg); +} + +VOID SaveConfig() +{ + config_setting_t *root, *group; + config_t cfg; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveStringValue(group, "BPQHostIP", BPQHostIP); + + SaveStringValue(group, "COM1", PTTCATPort[0]); + SaveStringValue(group, "COM2", PTTCATPort[1]); + SaveStringValue(group, "COM3", PTTCATPort[2]); + SaveStringValue(group, "COM4", PTTCATPort[3]); + + SaveIntValue(group, "HamLibPort1", HamLibPort[0]); + SaveIntValue(group, "HamLibPort2", HamLibPort[1]); + SaveIntValue(group, "HamLibPort3", HamLibPort[2]); + SaveIntValue(group, "HamLibPort4", HamLibPort[3]); + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + + + + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + + if (lpCmdLine[0]) + { + // Port Name and Speed for Remote CAT + + RigPort = _strdup(lpCmdLine); + RigSpeed = strlop(RigPort, ':'); + } + + MyRegisterClass(hInstance); + + GetConfig(); + + if (!InitInstance(hInstance, nCmdShow)) + return (FALSE); + + // Main message loop: + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + KillTimer(NULL, TimerHandle); + + return (msg.wParam); +} + +// + +// +// FUNCTION: InitApplication(HANDLE) +// +// PURPOSE: Initializes window data and registers window class +// +// COMMENTS: +// +// In this function, we initialize a window class by filling out a data +// structure of type WNDCLASS and calling either RegisterClass or +// the internal MyRegisterClass. +// + +#define BGCOLOUR RGB(236,233,216) +HBRUSH bgBrush; +HBRUSH RedBrush; +HBRUSH GreenBrush; + +HWND hWnd; + +BOOL InitApplication(HINSTANCE hInstance) +{ + return TRUE; +} + +// +// FUNCTION: InitInstance(HANDLE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HFONT FAR PASCAL MyCreateFont( void ) +{ + CHOOSEFONT cf; + LOGFONT lf; + HFONT hfont; + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = (HDC)NULL; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = SCREEN_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. + + hfont = CreateFontIndirect(cf.lpLogFont); + return (hfont); +} + +VOID Rig_PTT(int n, BOOL PTTState) +{ + + char Msg[16]; + int Len = sprintf(Msg, "T %d\n", PTTState); + + if (HamLibSock[n]) + { + send(HamLibSock[n], Msg, Len, 0); + + if (PTTState) + SetDlgItemText(hWnd, stateCtl[n], "PTT"); + else + SetDlgItemText(hWnd, stateCtl[n], ""); + } +} + + + +VOID PTTCATThread() +{ + DWORD dwLength = 0; + int Length, ret, i; + UCHAR * ptr1; + UCHAR * ptr2; + UCHAR c; + UCHAR Block[4][80]; + UCHAR CurrentState[4] = {0}; +#define RTS 2 +#define DTR 4 + HANDLE Event; + DWORD EvtMask[4]; + OVERLAPPED Overlapped[4]; + char Port[32]; + int PIndex = 0; + int HIndex = 0; + int rc; + + EndPTTCATThread = FALSE; + + while (PIndex < 4 && PTTCATPort[PIndex][0]) + { + RealMux[HIndex] = 0; + + sprintf(Port, "\\\\.\\pipe\\BPQ%s", PTTCATPort[PIndex]); + + PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) + { + int Err = GetLastError(); +// Consoleprintf("PTTMUX port BPQCOM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); + + // See if real com port + + sprintf(Port, "\\\\.\\\\%s", PTTCATPort[PIndex]); + + PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + RealMux[HIndex] = 1; + + if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) + { + int Err = GetLastError(); + PTTCATHandle[HIndex] = 0; + Debugprintf("PTTMUX port COM%s Open failed code %d", PTTCATPort[PIndex], Err); + } + else + { + rc = SetCommMask(PTTCATHandle[HIndex], EV_CTS | EV_DSR); // Request notifications + HIndex++; + } + } + else + HIndex++; + + PIndex++; + + } + + if (PIndex == 0) + return; // No ports + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + for (i = 0; i < HIndex; i ++) + { + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + if (RealMux[i]) + { + // Request Interface change notifications + + rc = WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); + rc = GetLastError(); + + } + else + { + // Prime a read on each PTTCATHandle + + ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + + while (EndPTTCATThread == FALSE) + { + +WaitAgain: + + ret = WaitForSingleObject(Event, 1000); + + if (ret == WAIT_TIMEOUT) + { + if (EndPTTCATThread) + { + for (i = 0; i < HIndex; i ++) + { + CancelIo(PTTCATHandle[i]); + CloseHandle(PTTCATHandle[i]); + PTTCATHandle[i] = INVALID_HANDLE_VALUE; + } + CloseHandle(Event); + return; + } + goto WaitAgain; + } + + ResetEvent(Event); + + // See which request(s) have completed + + for (i = 0; i < HIndex; i ++) + { + ret = GetOverlappedResult(PTTCATHandle[i], &Overlapped[i], &Length, FALSE); + + if (ret) + { + if (RealMux[i]) + { + // Request Interface change notifications + + DWORD Mask; + + GetCommModemStatus(PTTCATHandle[i], &Mask); + + if (Mask & MS_CTS_ON) + Rig_PTT(i, TRUE); + else + Rig_PTT(i, FALSE); + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); + + } + else + { + + ptr1 = Block[i]; + ptr2 = Block[i]; + + while (Length > 0) + { + c = *(ptr1++); + + Length--; + + if (c == 0xff) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + Length--; + } + else + { + // This is connection / RTS/DTR statua from other end + // Convert to CAT Command + + if (c == CurrentState[i]) + continue; + + if (c & RTS) + Rig_PTT(i, TRUE); + else + Rig_PTT(i, FALSE); + + CurrentState[i] = c; + continue; + } + } + } + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + } + } + EndPTTCATThread = FALSE; +} + +char ClassName[]="BPQMAIL"; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASS wc; + + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); +// BlueBrush = CreateSolidBrush(RGB(0,0,255)); + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.lpfnWndProc = WndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = NULL; //LoadIcon( hInstance, MAKEINTRESOURCE(IDI_GPSMUXPC)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL;//"MENU_1"; + wc.lpszClassName = szWindowClass; + +// RegisterClass(&wc); + + // wc.lpfnWndProc = TraceWndProc; + // wc.lpszClassName = TraceClassName; + +// RegisterClass(&wc); + +// wc.lpfnWndProc = ConfigWndProc; +// wc.lpszClassName = ConfigClassName; + + return (RegisterClass(&wc)); + +} + + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + u_long param=1; + BOOL bcopt=TRUE; + int ret; + WNDCLASS wc = {0}; + int n = 0; + + + hInst = hInstance; // Store instance handle in our global variable + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + ret=GetLastError(); + return FALSE; + } + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = OEM_CHARSET ; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ; + lstrcpy( LFTTYFONT.lfFaceName, "Fixedsys" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; +// hFont = MyCreateFont(); + + SetWindowText(hWnd,Title); + + comWnd[0] = GetDlgItem(hWnd, IDC_COM1); + comWnd[1] = GetDlgItem(hWnd, IDC_COM2); + comWnd[2] = GetDlgItem(hWnd, IDC_COM3); + comWnd[3] = GetDlgItem(hWnd, IDC_COM4); + + portWnd[0] = GetDlgItem(hWnd, IDC_HAMLIBPORT1); + portWnd[1] = GetDlgItem(hWnd, IDC_HAMLIBPORT2); + portWnd[2] = GetDlgItem(hWnd, IDC_HAMLIBPORT3); + portWnd[3] = GetDlgItem(hWnd, IDC_HAMLIBPORT4); + + stateWnd[0] = GetDlgItem(hWnd, IDC_STATE1); + stateWnd[1] = GetDlgItem(hWnd, IDC_STATE2); + stateWnd[2] = GetDlgItem(hWnd, IDC_STATE3); + stateWnd[3] = GetDlgItem(hWnd, IDC_STATE4); + + + stateCtl[0] = IDC_STATE1; + stateCtl[1] = IDC_STATE2; + stateCtl[2] = IDC_STATE3; + stateCtl[3] = IDC_STATE4; + + ShowWindow(hWnd, nCmdShow); + + SetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP); + + SetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0]); + SetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1]); + SetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2]); + SetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3]); + + SetDlgItemInt(hWnd, IDC_HAMLIBPORT1, HamLibPort[0], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT2, HamLibPort[1], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT3, HamLibPort[2], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT4, HamLibPort[3], 0); + +// ioctlsocket (sock, FIONBIO, ¶m); + + if (RigPort) + { + int Speed = 9600; + + if (RigSpeed) + Speed = atoi(RigSpeed); + } + + TimerHandle = SetTimer(hWnd, 1, 10000, NULL); + + runTimer(); // Open Hamlib connections + + _beginthread(PTTCATThread, 0); + + return TRUE; +} + +VOID ConnecttoHAMLIB(int n); + +VOID runTimer() +{ + int n; + + for (n = 0; n < 4; n++) + { + if (HamLibPort[n] && HamLibSock[n] == 0) + { + // try to connect + + ConnecttoHAMLIB(n); + + } + } +} +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + + switch (message) + { + case WM_TIMER: + + runTimer(); + break; + + case WM_CTLCOLOREDIT: + { + HDC hdc = (HDC) wParam; // handle to display context + HWND hwnd = (HWND) lParam; // handle to control window + int n; + + for (n = 0; n < 4; n++) + { + if (hwnd == comWnd[n] && PTTCATPort[n][0]) + if (PTTCATHandle[n]) + return (INT_PTR)GreenBrush; + else + return (INT_PTR)RedBrush; + + if (hwnd == portWnd[n] && HamLibPort[n]) + if (HamLibSock[n]) + return (INT_PTR)GreenBrush; + else + return (INT_PTR)RedBrush; + } + + //return (INT_PTR)RedBrush; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + if (MinimizetoTray) + + return ShowWindow(hWnd, SW_HIDE); + else + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + break; + + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_CLOSE: + + PostQuitMessage(0); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + int OK; + + case IDC_TEST1: + + Rig_PTT(0, 1); + Sleep(1500); + Rig_PTT(0, 0); + break; + + case IDC_TEST2: + + Rig_PTT(1, 1); + Sleep(1500); + Rig_PTT(1, 0); + break; + + case IDC_TEST3: + + Rig_PTT(2, 1); + Sleep(1500); + Rig_PTT(2, 0); + break; + + case IDC_TEST4: + + Rig_PTT(3, 1); + Sleep(1500); + Rig_PTT(3, 0); + break; + + case IDOK: + + GetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP, 127); + + GetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0], 15); + GetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1], 15); + GetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2], 15); + GetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3], 15); + + HamLibPort[0] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT1, &OK, 0); + HamLibPort[1] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT2, &OK, 0); + HamLibPort[2] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT3, &OK, 0); + HamLibPort[3] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT4, &OK, 0); + + SaveConfig(); + + // EndPTTCATThread = 1; + + // Sleep(1100); + + // _beginthread(PTTCATThread, 0); + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + } + + return (DefWindowProc(hWnd, message, wParam, lParam)); + +} + +void HAMLIBProcessMessage(int n) +{ + char RXBuffer[256]; + + int InputLen = recv(HamLibSock[n], RXBuffer, 256, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (HamLibSock[n]) + closesocket(HamLibSock[n]); + + HamLibSock[n] = 0; + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + return; + } +} + + +VOID HAMLIBThread(int n); + +VOID ConnecttoHAMLIB(int n) +{ + _beginthread(HAMLIBThread, 0, n); + return ; +} + +VOID HAMLIBThread(int n) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (HamLibSock[n]) + closesocket(HamLibSock[n]); + + // Param is IPADDR:PORT. Only Allow numeric addresses + + if (HamLibPort[n] == 0) + return; + + remoteDest[n].sin_family = AF_INET; + remoteDest[n].sin_addr.s_addr = inet_addr(BPQHostIP); + remoteDest[n].sin_port = htons(HamLibPort[n]); + + HamLibSock[n] = 0; + HamLibSock[n] = socket(AF_INET,SOCK_STREAM,0); + + if (HamLibSock[n] == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); + Debugprintf(Msg); + + return; + } + + setsockopt(HamLibSock[n], SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(HamLibSock[n], IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(HamLibSock[n],(LPSOCKADDR)&remoteDest[n],sizeof(remoteDest[n])) == 0) + { + // + // Connected successful + // + } + else + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for HAMLIB socket %d - error code = %d\r\n", n, err); + Debugprintf(Msg); + + closesocket(HamLibSock[n]); + + HamLibSock[n] = 0; + + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + return; + } + + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + ret = GetLastError(); + + while (HamLibSock[n]) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(HamLibSock[n],&readfs); + FD_SET(HamLibSock[n],&errorfs); + + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + ret = select((int)HamLibSock[n] + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(HamLibSock[n], &readfs)) + { + HAMLIBProcessMessage(n); + } + + if (FD_ISSET(HamLibSock[n], &errorfs)) + { +Lost: + sprintf(Msg, "HAMLIB Connection lost for Port %d\r\n", n); + Debugprintf(Msg); + + closesocket(HamLibSock[n]); + HamLibSock[n] = 0; + RedrawWindow(hWnd, NULL, NULL, 0); + + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "HAMLIB Thread Terminated Port %d\r\n", n); + Debugprintf(Msg); +} diff --git a/BPQRemotePTT.cfg b/BPQRemotePTT.cfg new file mode 100644 index 0000000..846bf91 --- /dev/null +++ b/BPQRemotePTT.cfg @@ -0,0 +1,12 @@ +main : +{ + BPQHostIP = "192.168.1.64"; + COM1 = "COM43"; + COM2 = ""; + COM3 = ""; + COM4 = ""; + HamLibPort1 = 4534; + HamLibPort2 = 0; + HamLibPort3 = 0; + HamLibPort4 = 0; +}; diff --git a/BPQRemotePTT.rc b/BPQRemotePTT.rc new file mode 100644 index 0000000..3c942d6 --- /dev/null +++ b/BPQRemotePTT.rc @@ -0,0 +1,173 @@ +//Microsoft Developer Studio generated resource script. +// +#include "BPQRemotePTTRes.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +BPQICON ICON DISCARDABLE "..\\BPQIcon.ICO" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,3,2 + PRODUCTVERSION 1,0,3,2 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "Program to Convert RTS changes on Virtual COM Port to hamlib PTT commands\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "BPQRemotePTT\0" + VALUE "FileVersion", "1. 0. 3. 2\0" + VALUE "InternalName", "bpq32\0" + VALUE "LegalCopyright", "Copyright © 2020 G8BPQ\0" + VALUE "OriginalFilename", "\\BPQRemotePTT.exe\0" + VALUE "ProductName", "BPQRemotePTT\0" + VALUE "ProductVersion", "1. 0. 1. 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + +#endif // !_MAC + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "BPQRemotePTTRes.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +GPSMAINWINDOW DIALOG FIXED IMPURE 100, 100, 335, 162 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "GPS Mux Version 1.0.0" +CLASS "GPSMAINWINDOW" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,150,116,50,14 + EDITTEXT IDC_BPQHOST,71,7,72,14,ES_AUTOHSCROLL + LTEXT "BPQ Host IP",IDC_STATIC,9,7,55,11 + EDITTEXT IDC_COM1,71,27,46,14,ES_AUTOHSCROLL + LTEXT "COM Port",IDC_STATIC,9,29,55,14 + EDITTEXT IDC_HAMLIBPORT1,186,27,46,14,ES_AUTOHSCROLL + LTEXT "HAMLIB Port",IDC_STATIC,124,28,55,14 + LTEXT "",IDC_STATE1,243,28,24,14 + PUSHBUTTON "Test",IDC_TEST1,277,27,50,14 + EDITTEXT IDC_COM2,71,47,46,14,ES_AUTOHSCROLL + LTEXT "COM Port",IDC_STATIC,9,48,55,14 + EDITTEXT IDC_HAMLIBPORT2,185,47,46,14,ES_AUTOHSCROLL + LTEXT "HAMLIB Port",IDC_STATIC,123,47,55,14 + LTEXT "",IDC_STATE2,243,47,24,14 + PUSHBUTTON "Test",IDC_TEST2,277,47,50,14 + EDITTEXT IDC_COM3,71,66,46,14,ES_AUTOHSCROLL + LTEXT "COM Port",IDC_STATIC,9,69,55,14 + EDITTEXT IDC_HAMLIBPORT3,186,66,46,14,ES_AUTOHSCROLL + LTEXT "HAMLIB Port",IDC_STATIC,124,67,55,14 + LTEXT "",IDC_STATE3,244,66,24,14 + PUSHBUTTON "Test",IDC_TEST3,277,66,50,14 + EDITTEXT IDC_COM4,71,86,46,14,ES_AUTOHSCROLL + LTEXT "COM Port",IDC_STATIC,9,88,55,14 + EDITTEXT IDC_HAMLIBPORT4,186,86,46,14,ES_AUTOHSCROLL + LTEXT "HAMLIB Port",IDC_STATIC,124,87,55,14 + LTEXT "",IDC_STATE4,243,87,24,14 + PUSHBUTTON "Test",IDC_TEST4,277,86,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "GPSMAINWINDOW", DIALOG + BEGIN + BOTTOMMARGIN, 144 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/BPQRemotePTT.vcproj b/BPQRemotePTT.vcproj new file mode 100644 index 0000000..f47dfa3 --- /dev/null +++ b/BPQRemotePTT.vcproj @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user b/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40182c4 --- /dev/null +++ b/BPQRemotePTT.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQRemotePTTRes.h b/BPQRemotePTTRes.h new file mode 100644 index 0000000..34d1f57 --- /dev/null +++ b/BPQRemotePTTRes.h @@ -0,0 +1,32 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by BPQRemotePTT.rc +// +#define IDC_BPQHOST 1000 +#define IDC_COM1 1001 +#define IDC_HAMLIBPORT1 1002 +#define IDC_COM2 1003 +#define IDC_STATE1 1004 +#define IDC_TEST1 1005 +#define IDC_HAMLIBPORT2 1006 +#define IDC_STATE2 1007 +#define IDC_TEST2 1008 +#define IDC_COM3 1009 +#define IDC_HAMLIBPORT3 1010 +#define IDC_STATE3 1011 +#define IDC_TEST3 1012 +#define IDC_COM4 1013 +#define IDC_HAMLIBPORT4 1014 +#define IDC_STATE4 1015 +#define IDC_TEST4 1016 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1006 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/BPQTermMDI.c b/BPQTermMDI.c new file mode 100644 index 0000000..9f80062 --- /dev/null +++ b/BPQTermMDI.c @@ -0,0 +1,4830 @@ + +// Version 2.0.1 November 2007 + +// Change resizing algorithm + + +// Version 2.0.2 January 2008 + +// Restore Checked state of Bells and AutoConnect Flags +// Call CheckTimer on startup (for new Initialisation Scheme for perl) + +// Version 2.0.3 July 2008 + +// Display lines received without a terminaing CR + + +// Version 2.0.4 November 2008 + +// Add option to remove a Line Feed following a CR +// Add Logging Option + +// Version 2.0.5 January 2009 + +// Add Start Minimized Option + +// Version 2.0.6 June 2009 + +// Add Option to send *** Disconnnected on disconnect +// Add line wrap code +// Add option not to monitor NODES broadcasts + +// Version 2.0.7 October 2009 + +// Add input buffer scrollback. +// Fix monitoring when PORTNUM specified + +// Version 2.0.8 December 2009 + +// Fix use of numeric keypad 2 and 8 (were treated as up and down) + +// Version 2.0.9 March 2010 + +// Add colour for monitor and BPQ Chat + +// Version 2.0.0 October 2010 + +// Add Chat Terminal Mode (sends keepalives) + +// Version 2.1.0 August 2011 + +// Add Copy/Paste capability to output window. +// Add Font Selection +// Get Registry Tree from BPQ32.dll +// Add Command to reset Monitor/Output window split + +// Version 2.1.1 October 2011 + +// Wrap overlong lines + +// Version 2.1.2 Jan 2012 + +// Call CloseBPQ32 on exit +// Fix ClearOutputWindow +// Save MonitorNodes flag + +#define _CRT_SECURE_NO_DEPRECATE + +#include "compatbits.h" +#include "AsmStrucs.h" +#include +#include +#include +#define DllImport __declspec(dllimport) + +#include "bpq32.h" // BPQ32 API Defines +#define BPQTermMDI + +#ifndef MDIKERNEL + +#include "Versions.h" +#include "GetVersion.h" + +#define BGCOLOUR RGB(236,233,216) + +#endif + +HBRUSH bgBrush; + +#include "BpqTermMDI.h" + + +extern BOOL FrameMaximized; + +char RTFHeader[4000]; + +int RTFHddrLen; + +struct ConsoleInfo * ConsHeader = NULL; + +struct ConsoleInfo MonWindow; + +struct ConsoleInfo InitHeader; // Dummy for before window is created + +HKEY FAR WINAPI GetRegistryKey(); +char * FAR WINAPI GetRegistryKeyText(); + +VOID __cdecl Debugprintf(const char * format, ...); + +#ifndef MDIKERNEL + +HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf_s(Mess, sizeof(Mess), format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + return; +} +#else +extern HKEY REGTREE; +extern int OffsetH, OffsetW; + +#endif + +HINSTANCE hInst; + +char ClassName[] = "BPQMAINWINDOW"; +char Title[80]; + +// Foward declarations of functions included in this code module: + +ATOM MyRegisterClass(CONST WNDCLASS*); +BOOL InitApplication(HINSTANCE); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK FrameWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +extern RECT FRect; // Frame + +int NewLine(); + +int ProcessBuff(char * readbuff,int len); + +int EnableConnectMenu(HWND hWnd); +int EnableDisconnectMenu(HWND hWnd); +int DisableConnectMenu(HWND hWnd); +int DisableDisconnectMenu(HWND hWnd); +int ToggleRestoreWindows(); +int ToggleAppl(HWND hWnd, int Item, int mask); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int ToggleFlags(HWND hWnd, int Item, int mask); +int CopyScreentoBuffer(char * buff); +int DoMonData(int Stream); +int TogglePort(HWND hWnd, int Item, int mask); +int ToggleMTX(HWND hWnd); +int ToggleMCOM(HWND hWnd); +int ToggleParam(HMENU hMenu, BOOL * Param, int Item); +int ToggleChat(HWND hWnd); +void MoveWindows(struct ConsoleInfo * Cinfo); +void CopyListToClipboard(HWND hWnd); +void CopyRichTextToClipboard(HWND hWnd); +BOOL OpenMonitorLogfile(); +void WriteMonitorLine(char * Msg, int MsgLen); +int WritetoOutputWindow(struct ConsoleInfo * Cinfo, char * Msg, int len, BOOL Refresh); +VOID DoRefresh(struct ConsoleInfo * Cinfo); +INT_PTR CALLBACK FontConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +BOOL CreateMonitorWindow(char * MonSize); +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized); +int ToggleMON_UI_ONLY(HWND hWnd); + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(64,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) +}; + + + +LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; + + +extern CMDX COMMANDS[]; +extern int APPL1; + +static HMENU trayMenu; + +int xsize,ysize; // Screen size at startup + +double Split=0.5; +int SplitPos=300; +#define SplitBarHeight 5 +#define InputBoxHeight 25 +RECT MonRect; +RECT OutputRect; +RECT SplitRect; + +int OutputBoxHeight; + +int maxlinelen = 80; +int CharWidth = 8; + +int ClientHeight, ClientWidth; + + +#define MAXLINES 1000 +#define LINELEN 200 + +char RTFHeader[4000]; + +int RTFHddrLen; + + +char FontName[100] = "FixedSys"; +int FontSize = 20; +int FontWidth = 8; +int CodePage = 1252; +int CharSet = 0; + + +int CurrentHost = 0; +int CfgNo = 0; + +SOCKET sock; + +BOOL MonData = FALSE; + +static char Key[80]; +int portmask=1; +int mtxparam=1; +int mcomparam=1; +int monUI=0; + +char kbbuf[160]; +int kbptr=0; +char readbuff[100000]; // for stupid bbs programs + +int ptr=0; + +int Stream, Stream2; // Stream2 for SYSOP Chat session +int len,count; + + +static UINT APPLMASK = 0x80000000; +int applflags = 2; // Message to Uset and Application +int Sessno = 0; + +int PartLinePtr=0; +int PartLineIndex=0; // Listbox index of (last) incomplete line + +BOOL Bells = FALSE; +BOOL StripLF = FALSE; +BOOL LogMonitor = FALSE; +BOOL LogOutput = FALSE; +BOOL SendDisconnected = TRUE; +BOOL MonitorNODES = TRUE; +BOOL MonitorColour = TRUE; +BOOL ChatMode = FALSE; +BOOL RestoreWindows = TRUE; +BOOL MonitorAPRS = FALSE; +BOOL WarnWrap = TRUE; +BOOL WrapInput = TRUE; +BOOL FlashOnBell = FALSE; + + + + +HANDLE MonHandle=INVALID_HANDLE_VALUE; + +HCURSOR DragCursor; +HCURSOR Cursor; + +AUTOCONNECT=TRUE; + +LOGFONT LF; + +HFONT hFont ; + +BOOL MinimizetoTray; + +VOID CALLBACK tTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +TIMERPROC tTimerFunc = (TIMERPROC) tTimerProc; + +int tTimerHandle = 0; + +BOOL WINE; + + +// HMENU hTermMenu; // handle of menu +extern HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +extern HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +extern HMENU hWndMenu, hMainFrameMenu, hBaseMenu; +extern HWND ClientWnd, FrameWnd; +extern HANDLE hInstance; + +extern byte MCOM; +extern char MTX; +extern ULONG MMASK; +extern byte MUIONLY; + +HMENU hPopMenu1; + +#define AUTO -1 +#define CP437 437 +#define CP1251 1251 +#define CP1252 1252 + +int RXMode = AUTO; //CP1252; +int TXMode = CP_UTF8; + +int APRSWriteLog(char * msg); + +VOID MonitorAPRSIS(char * Msg, int MsgLen, BOOL TX) +{ + char Line[300]; + char Copy[300]; + + int Len; + struct tm * TM; + time_t NOW; + + if (MonWindow.hConsole == NULL || MonitorAPRS == 0) + return; + + if (MsgLen > 250) + return; + + NOW = _time32(NULL); + TM = gmtime(&NOW); + + // Mustn't change Msg + + memcpy(Copy, Msg, MsgLen); + Copy[MsgLen] = 0; + + if (strchr(Copy, 13)) + Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); + else + Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s\r", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); + + if (TX) + MonWindow.CurrentColour = 0; + else + MonWindow.CurrentColour = 64; + + WritetoOutputWindow(&MonWindow, Line, Len, FALSE); + MonWindow.NeedRefresh = TRUE; + + APRSWriteLog(Line); +} + + +VOID CALLBACK tTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ + // entered every 1 sec + + struct ConsoleInfo * Cinfo = &MonWindow; + + if (Cinfo->NeedRefresh) + { + Cinfo->NeedRefresh = FALSE; + DoRefresh(Cinfo); + } + + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->CONNECTED && ChatMode) + { + Cinfo->SlowTimer++; + + if (Cinfo->SlowTimer > 500) // About 9 mins + { + Cinfo->SlowTimer = 0; + SendMsg(Cinfo->BPQStream, "\0", 1); + } + } + } +} + +// +// FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) +// +// PURPOSE: Entry point for the application. +// +// COMMENTS: +// +// This function initializes the application and processes the +// message loop. +// + +VOID SetupRTFHddr() +{ + int i, n; + char RTFColours[3000]; + char Temp[1000]; + + // Set up RTF Header, including Colours String; + + memcpy(RTFColours, "{\\colortbl ;", 12); + n = 12; + + for (i = 1; i < 100; i++) + { + COLORREF Colour = Colours[i]; + n += sprintf(&RTFColours[n], "\\red%d\\green%d\\blue%d;", GetRValue(Colour), GetGValue(Colour),GetBValue(Colour)); + } + + RTFColours[n++] = '}'; + RTFColours[n] = 0; + +// strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset204 ;}}"); + sprintf(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1\\cpg%d\\fcharset%d %s;}}", CodePage, CharSet, FontName); + strcat(RTFHeader, RTFColours); + sprintf(Temp, "\\viewkind4\\uc1\\pard\\f0\\fs%d", FontSize); + strcat(RTFHeader, Temp); + + RTFHddrLen = strlen(RTFHeader); +} + +#ifdef MDIKERNEL + +extern int SessHandle; + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ + int i, n, tempmask=0xffff; + char msg[50]; + int retCode,Type,Vallen; + HKEY hKey=0; + char Size[80]; + int Sessions = 0; + struct PORTCONTROL * PORT; + + KillTimer(NULL,SessHandle); + + tTimerHandle = SetTimer(NULL, 0, 1000, tTimerFunc); + + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE = TRUE; + Debugprintf("Running under WINE"); + } + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + SetupRTFHddr(); + + trayMenu = CreatePopupMenu(); + AppendMenu(trayMenu, MF_STRING, RTFCOPY, "Copy"); + + Stream = FindFreeStream(); // For Monitoring /Inbound + + if (Stream == 255) + { + MessageBox(NULL,"No free streams available",NULL,MB_OK); + return; + } + + BPQSetHandle(Stream, FrameWnd); + + // Get config from Registry + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\BPQTermMDI"); + + retCode = RegOpenKeyEx (REGTREE, + Key, + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=4; + retCode = RegQueryValueEx(hKey,"Sessions",0, + (ULONG *)&Type,(UCHAR *)&Sessions,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MTX",0, + (ULONG *)&Type,(UCHAR *)&mtxparam,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MCOM",0, + (ULONG *)&Type,(UCHAR *)&mcomparam,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MONUIONLY",0, + (ULONG *)&Type,(UCHAR *)&monUI,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"PortMask",0, + (ULONG *)&Type,(UCHAR *)&tempmask,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"ChatMode",0, + (ULONG *)&Type,(UCHAR *)&ChatMode, (ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MONColour",0, + (ULONG *)&Type,(UCHAR *)&MonitorColour,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MonitorNODES",0, + (ULONG *)&Type,(UCHAR *)&MonitorNODES,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MonitorAPRS",0, + (ULONG *)&Type,(UCHAR *)&MonitorAPRS,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"Bells",0, + (ULONG *)&Type,(UCHAR *)&Bells,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"StripLF",0, + (ULONG *)&Type,(UCHAR *)&StripLF,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "SendDisconnected",0, + (ULONG *)&Type,(UCHAR *)&SendDisconnected,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"WarnWrap",0, + (ULONG *)&Type,(UCHAR *)&WarnWrap,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"WrapInput",0, + (ULONG *)&Type,(UCHAR *)&WrapInput,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"FlashOnBell",0, + (ULONG *)&Type,(UCHAR *)&FlashOnBell,(ULONG *)&Vallen); + + + + Vallen=99; + retCode = RegQueryValueEx(hKey, "FontName", 0, &Type, FontName, &Vallen); + + if (FontName[0] == 0) + strcpy(FontName, "FixedSys"); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "CharSet", 0, &Type, (UCHAR *)&CharSet, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "CodePage", 0, &Type, (UCHAR *)&CodePage, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "FontSize", 0, &Type, (UCHAR *)&FontSize, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "FontWidth", 0, &Type, (UCHAR *)&FontWidth, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "RestoreWindows", 0, &Type, (UCHAR *)&RestoreWindows, &Vallen); + + Vallen=80; + RegQueryValueEx(hKey, "MonSize", 0 , &Type, &Size[0], &Vallen); + } + +// hPopMenu1 = GetSubMenu(hMonCfgMenu, 1); + + portmask = tempmask; + + for (n=1; n <= GetNumberofPorts(); n++) + { + PORT = GetPortTableEntryFromSlot(n); + + i = PORT->PORTNUMBER; + + sprintf(msg,"Port %d %s ",i, PORT->PORTDESCRIPTION); + + if (tempmask & (1<<(i-1))) + AppendMenu(hMonCfgMenu,MF_STRING | MF_CHECKED,BPQBASE + i,msg); + else + AppendMenu(hMonCfgMenu,MF_STRING | MF_UNCHECKED,BPQBASE + i,msg); + } + + if (mtxparam & 1) + CheckMenuItem(hMonCfgMenu,BPQMTX,MF_CHECKED); + else + CheckMenuItem(hMonCfgMenu,BPQMTX,MF_UNCHECKED); + + if (mcomparam & 1) + CheckMenuItem(hMonCfgMenu,BPQMCOM,MF_CHECKED); + else + CheckMenuItem(hMonCfgMenu,BPQMCOM,MF_UNCHECKED); + + if (monUI & 1) + CheckMenuItem(hMonCfgMenu,MON_UI_ONLY,MF_CHECKED); + else + CheckMenuItem(hMonCfgMenu,MON_UI_ONLY,MF_UNCHECKED); + + if (RestoreWindows) + { + CheckMenuItem(hMonCfgMenu,ID_WINDOWS_RESTORE,MF_CHECKED); + CheckMenuItem(hTermCfgMenu,ID_WINDOWS_RESTORE,MF_CHECKED); + } + else + { + CheckMenuItem(hMonCfgMenu, ID_WINDOWS_RESTORE, MF_UNCHECKED); + CheckMenuItem(hTermCfgMenu, ID_WINDOWS_RESTORE, MF_UNCHECKED); + } + + if (Bells & 1) + CheckMenuItem(hTermCfgMenu,BPQBELLS, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQBELLS, MF_UNCHECKED); + + if (SendDisconnected & 1) + CheckMenuItem(hTermCfgMenu,BPQSendDisconnected, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQSendDisconnected, MF_UNCHECKED); + + if (StripLF & 1) + CheckMenuItem(hTermCfgMenu,BPQStripLF, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQStripLF, MF_UNCHECKED); + + CheckMenuItem(hTermCfgMenu, ID_WARNWRAP, (WarnWrap) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hTermCfgMenu, ID_WRAP, (WrapInput) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hTermCfgMenu, ID_FLASHONBELL, (FlashOnBell) ? MF_CHECKED : MF_UNCHECKED); + + + CheckMenuItem(hMonCfgMenu,BPQMNODES, (MonitorNODES) ? MF_CHECKED : MF_UNCHECKED); + + CheckMenuItem(hMonCfgMenu,MONITORAPRS, (MonitorAPRS) ? MF_CHECKED : MF_UNCHECKED); + + + DrawMenuBar(FrameWnd); + + if (portmask) applflags |= 0x80; + + MCOM = mcomparam; + MTX = mtxparam; + MMASK = portmask; + MUIONLY = monUI; + + if (COMMANDS[APPL1 + 31].String[0] == '*') // No Host App + { + SetAppl(Stream, 0x80, 0); + } + else + { + SetAppl(Stream, applflags, APPLMASK); + + // Allocate another incoming stream + + Stream2 = FindFreeStream();; + + BPQSetHandle(Stream2, FrameWnd); + SetAppl(Stream2, 2, APPLMASK); + } + +// CreateMonitorWindow(Size); + + if (RestoreWindows) + { + struct ConsoleInfo * Cinfo; + int i; + + for (i = 1; i <= Sessions; i++) + { + Cinfo = CreateChildWindow(0, TRUE); + } + } +} + +SaveHostSessions() +{ + int Sessions = 0; + struct ConsoleInfo * Cinfo; + int i, disp; + char Item[80]; + BOOL NeedPause = FALSE; + HKEY hKey; + + // Save config + + RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + RegSetValueEx(hKey,"ChatMode",0,REG_DWORD,(BYTE *)&ChatMode,4); + RegSetValueEx(hKey,"PortMask",0,REG_DWORD,(BYTE *)&portmask,4); + RegSetValueEx(hKey,"Bells",0,REG_DWORD,(BYTE *)&Bells,4); + RegSetValueEx(hKey,"StripLF",0,REG_DWORD,(BYTE *)&StripLF,4); + RegSetValueEx(hKey,"SendDisconnected",0,REG_DWORD,(BYTE *)&SendDisconnected,4); + RegSetValueEx(hKey,"RestoreWindows",0,REG_DWORD,(BYTE *)&RestoreWindows,4); + RegSetValueEx(hKey,"MTX",0,REG_DWORD,(BYTE *)&mtxparam,4); + RegSetValueEx(hKey,"MCOM",0,REG_DWORD,(BYTE *)&mcomparam,4); + RegSetValueEx(hKey,"MONUIONLY",0,REG_DWORD,(BYTE *)&monUI,4); + RegSetValueEx(hKey,"MONColour",0,REG_DWORD,(BYTE *)&MonitorColour,4); + RegSetValueEx(hKey,"MonitorNODES",0,REG_DWORD,(BYTE *)&MonitorNODES,4); + RegSetValueEx(hKey,"MonitorAPRS",0,REG_DWORD,(BYTE *)&MonitorAPRS,4); + RegSetValueEx(hKey,"WarnWrap",0,REG_DWORD,(BYTE *)&WarnWrap,4); + RegSetValueEx(hKey,"WrapInput",0,REG_DWORD,(BYTE *)&WrapInput,4); + RegSetValueEx(hKey,"FlashOnBell",0,REG_DWORD,(BYTE *)&FlashOnBell,4); + + // Close any sessions + + i = 0; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + i++; + + sprintf(Item, "SessSize%d", i); + SaveMDIWindowPos(Cinfo->hConsole, "BPQTermMDI", Item, Cinfo->Minimized); + } + + RegSetValueEx(hKey,"Sessions",0,REG_DWORD,(BYTE *)&i,4); + + RegCloseKey(hKey); + + SaveMDIWindowPos(MonWindow.hConsole, "BPQTermMDI", "MonSize", MonWindow.Minimized); +} + +CloseHostSessions() +{ + struct ConsoleInfo * Cinfo; + int i; + BOOL NeedPause = FALSE; + + i = 0; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + i++; + + if (Cinfo->CONNECTED) + { + SessionControl(Cinfo->BPQStream, 2, 0); + NeedPause = TRUE; + } + } + + if (NeedPause) + Sleep(1500); +} + +#else + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + + sscanf(lpCmdLine,"%d %x",&Sessno, &applflags); + + if (!InitInstance(hInstance, nCmdShow)) + return (FALSE); + + // Main message loop: + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + KillTimer(NULL, tTimerHandle); + + CloseBPQ32(); // Close Ext Drivers if last bpq32 process + + return (msg.wParam); +} + +// + +// +// FUNCTION: InitApplication(HANDLE) +// +// PURPOSE: Initializes window data and registers window class +// +// COMMENTS: +// +// In this function, we initialize a window class by filling out a data +// structure of type WNDCLASS and calling either RegisterClass or +// the internal MyRegisterClass. +// + +HMENU hTermCfgMenu, hMonMenu, hPopMenu1, hPopMenu2, hPopMenu3; // handle of menu + +HINSTANCE hInstance = NULL; +TCHAR gszSigmaFrameClassName[] = TEXT("SigmaMdiFrame"); +TCHAR gszAppName[] = TEXT("Sigma"); +HWND ghMDIClientArea = NULL; //This stores the MDI client area window handle + +HWND FrameWnd = NULL; // This will hold the main frame window handle +HMENU ghMainFrameMenu = NULL; + +HANDLE ghSystemInfoMutex = NULL; +HMENU ghSysInfoMenu = NULL; + + + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + INITCOMMONCONTROLSEX dICC; + BOOL bInitCommonControlLib; + WNDCLASSEX wndclassMainFrame; + int i, n, tempmask=0xffff; + char msg[20]; + int retCode,Type,Vallen; + HKEY hKey=0; + char Size[80]; + int Sessions = 0; + + hInstance = hInstance; + hInst = hInstance; // Store instance handle in our global variable + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE = TRUE; + Debugprintf("Running under WINE"); + } + + REGTREE = GetRegistryKey(); + + MinimizetoTray=GetMinimizetoTrayFlag(); + + dICC.dwSize = sizeof(INITCOMMONCONTROLSEX); + dICC.dwICC = ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES; + + // Initialize common control library + + bInitCommonControlLib = InitCommonControlsEx(&dICC); + + if(!bInitCommonControlLib) + { + MessageBox(NULL, TEXT("Unable to load Common Controls!"), gszAppName, MB_ICONERROR); + } + + LoadLibrary("riched20.dll"); + + SetupRTFHddr(); + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW; + 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 = gszSigmaFrameClassName; + wndclassMainFrame.hIconSm = NULL; + + //Register MainFrame window + if(!RegisterClassEx(&wndclassMainFrame)) + { + DWORD derror = GetLastError(); + MessageBox(NULL, TEXT("This program requires Windows NT!"), gszAppName, MB_ICONERROR); + return 0; + } + + ghMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + hTermCfgMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + + //Create the main MDI frame window + + FrameWnd = CreateWindow(gszSigmaFrameClassName, + gszAppName, + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, // allows system choose an x position + CW_USEDEFAULT, // allows system choose a y position + CW_USEDEFAULT, // width, CW_USEDEFAULT allows system to choose height and width + CW_USEDEFAULT, // height, CW_USEDEFAULT ignores heights as this is set by setting + // CW_USEDEFAULT in width above. + NULL, // handle to parent window + ghMainFrameMenu, // 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 + + nCmdShow = SW_SHOW; // To show the window + + ShowWindow(FrameWnd, nCmdShow); + UpdateWindow(FrameWnd); + + trayMenu = CreatePopupMenu(); + AppendMenu(trayMenu, MF_STRING, RTFCOPY, "Copy"); + + // Create font for input line + + LF.lfHeight = 12; + LF.lfWidth = 8; + LF.lfOutPrecision = OUT_DEFAULT_PRECIS; + LF.lfClipPrecision = CLIP_DEFAULT_PRECIS; + LF.lfQuality = DEFAULT_QUALITY; + LF.lfPitchAndFamily = FIXED_PITCH; + strcpy(LF.lfFaceName, "FIXEDSYS"); + + hFont = CreateFontIndirect(&LF); + + CheckTimer(); + + tTimerHandle = SetTimer(NULL, 0, 10000, lptTimerFunc); + + Stream=FindFreeStream(); + + if (Stream == 255) + { + MessageBox(NULL,"No free streams available",NULL,MB_OK); + return (FALSE); + } + + // + // Register message for posting by BPQDLL + // + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + // + // Enable Async notification + // + + BPQSetHandle(Stream, FrameWnd); + + GetVersionInfo(NULL); + + sprintf(Title,"BPQTerminal Version %s", VersionString); + + SetWindowText(FrameWnd, Title); + + // Get config from Registry + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\BPQTermMDI"); + + retCode = RegOpenKeyEx (REGTREE, + Key, + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=4; + retCode = RegQueryValueEx(hKey,"Sessions",0, + (ULONG *)&Type,(UCHAR *)&Sessions,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MTX",0, + (ULONG *)&Type,(UCHAR *)&mtxparam,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MCOM",0, + (ULONG *)&Type,(UCHAR *)&mcomparam,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"PortMask",0, + (ULONG *)&Type,(UCHAR *)&tempmask,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"ChatMode",0, + (ULONG *)&Type,(UCHAR *)&ChatMode, (ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MONColour",0, + (ULONG *)&Type,(UCHAR *)&MonitorColour,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MonitorNODES",0, + (ULONG *)&Type,(UCHAR *)&MonitorNODES,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"Bells",0, + (ULONG *)&Type,(UCHAR *)&Bells,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"StripLF",0, + (ULONG *)&Type,(UCHAR *)&StripLF,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "SendDisconnected",0, + (ULONG *)&Type,(UCHAR *)&SendDisconnected,(ULONG *)&Vallen); + + Vallen=80; + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + sscanf(Size,"%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom); + + Vallen=99; + retCode = RegQueryValueEx(hKey, "FontName", 0, &Type, FontName, &Vallen); + + if (FontName[0] == 0) + strcpy(FontName, "FixedSys"); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "CharSet", 0, &Type, (UCHAR *)&CharSet, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "CodePage", 0, &Type, (UCHAR *)&CodePage, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "FontSize", 0, &Type, (UCHAR *)&FontSize, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "FontWidth", 0, &Type, (UCHAR *)&FontWidth, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "RestoreWindows", 0, &Type, (UCHAR *)&RestoreWindows, &Vallen); + + } + +// OutputData.CharWidth = FontWidth; + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(FrameWnd, &Rect); + } + + MoveWindow(FrameWnd, Rect.left, Rect.top, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + hPopMenu1 = GetSubMenu(hMonMenu, 1); + + for (n=1; n <= GetNumberofPorts(); n++) + { + i = GetPortNumber(n); + + sprintf(msg,"Port %d",i); + + if (tempmask & (1<<(i-1))) + { + AppendMenu(hPopMenu1,MF_STRING | MF_CHECKED,BPQBASE + i,msg); + portmask |= (1<<(i-1)); + } + else + AppendMenu(hPopMenu1,MF_STRING | MF_UNCHECKED,BPQBASE + i,msg); + } + + if (mtxparam & 1) + CheckMenuItem(hMonMenu,BPQMTX,MF_CHECKED); + else + CheckMenuItem(hMonMenu,BPQMTX,MF_UNCHECKED); + + if (mcomparam & 1) + CheckMenuItem(hMonMenu,BPQMCOM,MF_CHECKED); + else + CheckMenuItem(hMonMenu,BPQMCOM,MF_UNCHECKED); + + if (RestoreWindows) + { + CheckMenuItem(hMonMenu,ID_WINDOWS_RESTORE,MF_CHECKED); + CheckMenuItem(hTermCfgMenu,ID_WINDOWS_RESTORE,MF_CHECKED); + } + else + { + CheckMenuItem(hMonMenu, ID_WINDOWS_RESTORE, MF_UNCHECKED); + CheckMenuItem(hTermCfgMenu, ID_WINDOWS_RESTORE, MF_UNCHECKED); + } + + if (Bells & 1) + CheckMenuItem(hTermCfgMenu,BPQBELLS, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQBELLS, MF_UNCHECKED); + + if (SendDisconnected & 1) + CheckMenuItem(hTermCfgMenu,BPQSendDisconnected, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQSendDisconnected, MF_UNCHECKED); + + if (StripLF & 1) + CheckMenuItem(hTermCfgMenu,BPQStripLF, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQStripLF, MF_UNCHECKED); + + CheckMenuItem(hMonMenu,BPQMNODES, (MonitorNODES) ? MF_CHECKED : MF_UNCHECKED); + + CheckMenuItem(hMonMenu,MONCOLOUR, (MonitorColour) ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(FrameWnd); + + if (portmask) applflags |= 0x80; + + SetAppl(Stream, applflags, APPLMASK); + + SetTraceOptionsEx(portmask,mtxparam,mcomparam, monUI); + + if (MinimizetoTray) + { + // Set up Tray ICON + + sprintf(Title, "BPQTermMDI"); + AddTrayMenuItem(FrameWnd, Title); + } + + if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, nCmdShow); + else + ShowWindow(FrameWnd, nCmdShow); + + UpdateWindow(FrameWnd); + + // Allocate another incoming stream + + Stream2 = FindFreeStream(); + + if (Stream2 < 255) + { + BPQSetHandle(Stream2, FrameWnd); + SetAppl(Stream2, 2, APPLMASK); + } + + Vallen=80; + RegQueryValueEx(hKey, "MonSize", 0 , &Type, &Size[0], &Vallen); + + CreateMonitorWindow(Size); + + if (RestoreWindows) + { + int OffsetH, OffsetW; + struct ConsoleInfo * Cinfo; + int i; + char Item[20]; + + GetWindowRect(FrameWnd, &Rect); + OffsetH = Rect.bottom - Rect.top; + OffsetW = Rect.right - Rect.left; + GetClientRect(FrameWnd, &Rect); + OffsetH -= Rect.bottom; + OffsetW -= Rect.right; + + for (i = 1; i <= Sessions; i++) + { + Cinfo = CreateChildWindow(0); + if (Cinfo) + { + sprintf(Item, "SessSize%d", i); + + Vallen=80; + RegQueryValueEx(hKey, Item, 0 , &Type, &Size[0], &Vallen); + + sscanf(Size,"%d,%d,%d,%d", &Cinfo->Left, &Cinfo->Height, &Cinfo->Top, &Cinfo->Width); + + if (Cinfo->Height < 30) + { + //Was probably minimized. Set to a sensible size and minimize it + + MoveWindow(Cinfo->hConsole, i * 50 , i * 50, (Rect.right * 2)/3, (Rect.bottom * 2 /3), TRUE); + ShowWindow(Cinfo->hConsole, SW_SHOWMINIMIZED); + } + else + { + MoveWindow(Cinfo->hConsole, Cinfo->Left - OffsetW/2 + 4, Cinfo->Top - OffsetH, Cinfo->Width, Cinfo->Height, TRUE); + MoveWindows(Cinfo); + } + } + } + + } + RegCloseKey(hKey); + + return TRUE; +} + +#endif + +/* +BOOL InitInstancex(HINSTANCE hInstance, int nCmdShow) +{ + int i, n, tempmask=0xffff; + char msg[20]; + int retCode,Type,Vallen; + HKEY hKey=0; + char Size[80]; + TEXTMETRIC tm; + HDC dc; + struct RTFTerm * OPData; + + hInst = hInstance; // Store instance handle in our global variable + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE = TRUE; + Debugprintf("Running under WINE"); + } + + REGTREE = GetRegistryKey(); + + MinimizetoTray=GetMinimizetoTrayFlag(); + + // Create a dialog box as the main window + + hWnd=CreateDialog(hInst,ClassName,0,NULL); + + if (!hWnd) + return (FALSE); + + MainWnd=hWnd; + + // Retrieve the handlse to the edit controls. + + hwndInput = GetDlgItem(hWnd, 118); +// hwndOutput = GetDlgItem(hWnd, 117); + hwndSplit = GetDlgItem(hWnd, 119); + hwndMon = GetDlgItem(hWnd, 116); + + // Set our own WndProcs for the controls. + + wpOrigInputProc = (WNDPROC) SetWindowLong(hwndInput, GWL_WNDPROC, (LONG) InputProc); + wpOrigMonProc = (WNDPROC)SetWindowLong(hwndMon, GWL_WNDPROC, (LONG)MonProc); + + // Get config from Registry + + sprintf(Key,"SOFTWARE\\G8BPQ\\BPQ32\\BPQTerminal\\Session%d",Sessno); + + retCode = RegOpenKeyEx (REGTREE, + Key, + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=4; + retCode = RegQueryValueEx(hKey,"APPLMASK",0, + (ULONG *)&Type,(UCHAR *)&APPLMASK,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MTX",0, + (ULONG *)&Type,(UCHAR *)&mtxparam,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MCOM",0, + (ULONG *)&Type,(UCHAR *)&mcomparam,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"PortMask",0, + (ULONG *)&Type,(UCHAR *)&tempmask,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"AutoConnect",0, + (ULONG *)&Type,(UCHAR *)&AUTOCONNECT,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MONColour",0, + (ULONG *)&Type,(UCHAR *)&MonitorColour,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"MonitorNODES",0, + (ULONG *)&Type,(UCHAR *)&MonitorNODES,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"Bells",0, + (ULONG *)&Type,(UCHAR *)&Bells,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey,"StripLF",0, + (ULONG *)&Type,(UCHAR *)&StripLF,(ULONG *)&Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "SendDisconnected",0, + (ULONG *)&Type,(UCHAR *)&StripLF,(ULONG *)&Vallen); + + + + Vallen=8; + retCode = RegQueryValueEx(hKey,"Split",0, + (ULONG *)&Type,(UCHAR *)&Split,(ULONG *)&Vallen); + + Vallen=80; + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + sscanf(Size,"%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom); + + Vallen=99; + retCode = RegQueryValueEx(hKey, "FontName", 0, &Type, FontName, &Vallen); + + if (FontName[0] == 0) + strcpy(FontName, "FixedSys"); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "CharSet", 0, &Type, (UCHAR *)&CharSet, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "CodePage", 0, &Type, (UCHAR *)&CodePage, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "FontSize", 0, &Type, (UCHAR *)&FontSize, &Vallen); + + Vallen=4; + retCode = RegQueryValueEx(hKey, "FontWidth", 0, &Type, (UCHAR *)&FontWidth, &Vallen); + + RegCloseKey(hKey); + } + + OutputData.CharWidth = FontWidth; + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hWnd, &Rect); + } + + Height = Rect.bottom-Rect.top; + Width = Rect.right-Rect.left; + +#pragma warning(push) +#pragma warning(disable:4244) + SplitPos=Height*Split; +#pragma warning(pop) + + SetupRTFHddr(); + + // Create a Rich Text Control + + OPData = &OutputData; + + OPData->SendHeader = TRUE; + OPData->Finished = TRUE; + OPData->CurrentColour = 1; + + LoadLibrary("riched20.dll"); + + hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 6,145,290,130, MainWnd, NULL, hInstance, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + trayMenu = CreatePopupMenu(); + + AppendMenu(trayMenu,MF_STRING,40000,"Copy"); + + MoveWindow(hWnd,Rect.left,Rect.top, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindows(); + + Stream=FindFreeStream(); + + if (Stream == 255) + { + MessageBox(NULL,"No free streams available",NULL,MB_OK); + return (FALSE); + } + + // + // Register message for posting by BPQDLL + // + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + // + // Enable Async notification + // + + BPQSetHandle(Stream, hWnd); + + GetVersionInfo(NULL); + + sprintf(Title,"BPQTerminal Version %s - using stream %d", VersionString, Stream); + + SetWindowText(hWnd,Title); + + +// Sets Application Flags and Mask for stream. (BPQHOST function 1) +// 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. +// +// Top bit of flags controls monitoring + + +// if (APPLMASK & 0x01) CheckMenuItem(hMenu,BPQAPPL1,MF_CHECKED); + if (APPLMASK & 0x02) CheckMenuItem(hTermCfgMenu,BPQCHAT,MF_CHECKED); +// if (APPLMASK & 0x04) CheckMenuItem(hMenu,BPQAPPL3,MF_CHECKED); +// if (APPLMASK & 0x08) CheckMenuItem(hMenu,BPQAPPL4,MF_CHECKED); +// if (APPLMASK & 0x10) CheckMenuItem(hMenu,BPQAPPL5,MF_CHECKED); +// if (APPLMASK & 0x20) CheckMenuItem(hMenu,BPQAPPL6,MF_CHECKED); +// if (APPLMASK & 0x40) CheckMenuItem(hMenu,BPQAPPL7,MF_CHECKED); +// if (APPLMASK & 0x80) CheckMenuItem(hMenu,BPQAPPL8,MF_CHECKED); + +// CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION +// MSG_TO_USER EQU 10B ; SEND 'CONNECTED' TO USER +// MSG_TO_APPL EQU 100B ; SEND 'CONECTED' TO APPL +// 0x40 = Send Keepalives + +// if (applflags & 0x01) CheckMenuItem(hMenu,BPQFLAGS1,MF_CHECKED); +// if (applflags & 0x02) CheckMenuItem(hMenu,BPQFLAGS2,MF_CHECKED); +// if (applflags & 0x04) CheckMenuItem(hMenu,BPQFLAGS3,MF_CHECKED); +// if (applflags & 0x40) CheckMenuItem(hMenu,BPQFLAGS4,MF_CHECKED); + + + hPopMenu1=GetSubMenu(hMonMenu,1); + + for (n=1;n <= GetNumberofPorts();n++) + { + i = GetPortNumber(n); + + sprintf(msg,"Port %d",i); + + if (tempmask & (1<<(i-1))) + { + AppendMenu(hPopMenu1,MF_STRING | MF_CHECKED,BPQBASE + i,msg); + portmask |= (1<<(i-1)); + } + else + AppendMenu(hPopMenu1,MF_STRING | MF_UNCHECKED,BPQBASE + i,msg); + } + + if (mtxparam & 1) + CheckMenuItem(hMonMenu,BPQMTX,MF_CHECKED); + else + CheckMenuItem(hMonMenu,BPQMTX,MF_UNCHECKED); + + if (mcomparam & 1) + CheckMenuItem(hMonMenu,BPQMCOM,MF_CHECKED); + else + CheckMenuItem(hMonMenu,BPQMCOM,MF_UNCHECKED); + + if (AUTOCONNECT) + CheckMenuItem(hTermCfgMenu,BPQAUTOCONNECT,MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQAUTOCONNECT,MF_UNCHECKED); + + if (Bells & 1) + CheckMenuItem(hTermCfgMenu,BPQBELLS, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQBELLS, MF_UNCHECKED); + + if (SendDisconnected & 1) + CheckMenuItem(hTermCfgMenu,BPQSendDisconnected, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQSendDisconnected, MF_UNCHECKED); + + if (StripLF & 1) + CheckMenuItem(hTermCfgMenu,BPQStripLF, MF_CHECKED); + else + CheckMenuItem(hTermCfgMenu,BPQStripLF, MF_UNCHECKED); + + CheckMenuItem(hMonMenu,BPQMNODES, (MonitorNODES) ? MF_CHECKED : MF_UNCHECKED); + + CheckMenuItem(hMonMenu,MONCOLOUR, (MonitorColour) ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + if (portmask) applflags |= 0x80; + + SetAppl(Stream, applflags, APPLMASK); + + SetTraceOptionsEx(portmask,mtxparam,mcomparam, monUI); + + + + DragCursor = LoadCursor(hInstance, "IDC_DragSize"); + Cursor = LoadCursor(NULL, IDC_ARROW); + + GetCallsign(Stream, callsign); + + if (MinimizetoTray) + { + // Set up Tray ICON + + sprintf(Title,"BPQTerminal Stream %d",Stream); + AddTrayMenuItem(hWnd, Title); + } + + if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) + if (MinimizetoTray) + ShowWindow(hWnd, SW_HIDE); + else + ShowWindow(hWnd, nCmdShow); + else + ShowWindow(hWnd, nCmdShow); + + UpdateWindow(hWnd); + + CheckTimer(); + + TimerHandle = SetTimer(NULL, 0, 10000, lpTimerFunc); + + dc = GetDC(hwndOutput); + GetTextMetrics(dc, &tm); + + return (TRUE); +} +*/ +// +// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) +// +// PURPOSE: Processes messages for the main window. +// + + +LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct ConsoleInfo * Cinfo = &MonWindow; + LPRECT lprc; + int wmId, wmEvent; + int i; + RECT Rect; + + switch(message) + { + case WM_CREATE: + break; + + case WM_VSCROLL: + break; + + case WM_NOTIFY: + { + const MSGFILTER * pF = (MSGFILTER *)lParam; + POINT pos; + CHARRANGE Range; + + if(pF->nmhdr.hwndFrom == Cinfo->hwndOutput) + { + if(pF->msg == WM_VSCROLL) + { +// int Command = LOWORD(pF->wParam); +// int Pos = HIWORD(pF->wParam); + +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + DoRefresh(Cinfo); + break; + } + + if(pF->msg == WM_KEYUP) + { + if (pF->wParam == VK_PRIOR || pF->wParam == VK_NEXT) + { +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + DoRefresh(Cinfo); + } + } + + if(pF->msg == WM_RBUTTONDOWN) + { + // Only allow popup if something is selected + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + if (Range.cpMin == Range.cpMax) + return TRUE; + + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return TRUE; + } + } + break; + } + case WM_MDIACTIVATE: + + if (lParam == (LPARAM) hWnd) + { + RECT Rect; + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hMonCfgMenu, "Config"); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hMonEdtMenu, "Edit"); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hMonHlpMenu, "Help"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM)hWndMenu); + + // Check Window is visible + + GetWindowRect(FrameWnd, &FRect); + + if (GetWindowRect(hWnd, &Rect)) + { + if (Rect.top > FRect.bottom || Rect.left > FRect.right) + MoveWindow(hWnd, FRect.top + 100, FRect.left+ 100, 300, 200, 1); + } + } + else + { + RemoveMenu(hBaseMenu, 3, MF_BYPOSITION); + RemoveMenu(hBaseMenu, 2, MF_BYPOSITION); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Cinfo->Height = lprc->bottom-lprc->top; + Cinfo->Width = lprc->right-lprc->left; + Cinfo->Top = lprc->top; + Cinfo->Left = lprc->left; + + MoveWindows(Cinfo); + + break; + + case WM_SIZE: + + MoveWindows(Cinfo); + + // Drop through to Move + + + case WM_MOVE: + + GetWindowRect(hWnd, &Rect); + + Cinfo->Height = Rect.bottom-Rect.top; + Cinfo->Width = Rect.right-Rect.left; + Cinfo->Top = Rect.top; + Cinfo->Left = Rect.left; + + // Male relative to Frame + + GetWindowRect(FrameWnd, &Rect); + + Cinfo->Top -= Rect.top ; + Cinfo->Left -= Rect.left; + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + + if (wmId > BPQBASE && wmId < BPQBASE + 33) + { + TogglePort(hWnd, wmId, 0x1 << (wmId - (BPQBASE + 1))); + break; + } + + switch (wmId) + { + case StopALLMon: + + for (i=1; i <= GetNumberofPorts();i++) + { + CheckMenuItem(hMonCfgMenu, BPQBASE + GetPortNumber(i), MF_UNCHECKED); + } + portmask = 0; + applflags &= 0x7f; + + SetAppl(Stream,applflags,APPLMASK); + + break; + + case RTFCOPY: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(Cinfo->hConsole)) + { + len = SendMessage(Cinfo->hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + SetFocus(Cinfo->hwndInput); + + break; + } + + case BPQMTX: + + ToggleMTX(hWnd); + break; + + case BPQMCOM: + + ToggleMCOM(hWnd); + break; + + case MON_UI_ONLY: + + ToggleMON_UI_ONLY(hWnd); + break; + + case BPQMNODES: + + ToggleParam(hMonCfgMenu, &MonitorNODES, BPQMNODES); + break; + + case MONITORAPRS: + + ToggleParam(hMonCfgMenu, &MonitorAPRS, MONITORAPRS); + break; + + case MONCOLOUR: + + ToggleParam(hMonCfgMenu, &MonitorColour, MONCOLOUR); + break; + + case BPQLogMonitor: + + ToggleParam(hMonCfgMenu, &LogMonitor, BPQLogMonitor); + break; + + + case BPQCLEARMON: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + + break; + + case BPQCOPYMON: + + CopyRichTextToClipboard(Cinfo->hwndOutput); + break; + + case ID_WINDOWS_RESTORE: + + ToggleRestoreWindows(); + break; + + } + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + Cinfo->Minimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + DoRefresh(Cinfo); + + break; + + case SC_MINIMIZE: + + Cinfo->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return DefMDIChildProc(hWnd, message, wParam, lParam); //Frame window calls DefFrameProc rather than DefWindowProc +} + +struct ConsoleInfo * FontCinfo; + + +LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct ConsoleInfo * Cinfo; + struct ConsoleInfo * last = NULL; + + LPRECT lprc; + int wmId, wmEvent; + int i; + RECT Rect; +// MINMAXINFO * mmi; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hConsole == hWnd) + break; + } + + if (Cinfo == NULL) + Cinfo = &InitHeader; + + + switch(message) + { + +/* case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 800; + mmi->ptMaxSize.y = 600; + mmi->ptMaxTrackSize.x = 800; + mmi->ptMaxTrackSize.y = 600; + + return DefMDIChildProc(hWnd, message, wParam, lParam); +*/ + + case WM_CTLCOLOREDIT: + + if (Cinfo->Scrolled) + { + HDC hdcStatic = (HDC)wParam; + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)GetStockObject(LTGRAY_BRUSH); + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_NOTIFY: + { + const MSGFILTER * pF = (MSGFILTER *)lParam; + POINT pos; + CHARRANGE Range; + + if(pF->nmhdr.hwndFrom == Cinfo->hwndOutput) + { + if(pF->msg == WM_VSCROLL) + { +// int Command = LOWORD(pF->wParam); +// int Pos = HIWORD(pF->wParam); + +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + DoRefresh(Cinfo); + break; + } + + if(pF->msg == WM_KEYUP) + { + if (pF->wParam == VK_PRIOR || pF->wParam == VK_NEXT) + { +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + DoRefresh(Cinfo); + } + } + + if(pF->msg == WM_RBUTTONDOWN) + { + // Only allow popup if something is selected + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + if (Range.cpMin == Range.cpMax) + return TRUE; + + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return TRUE; + } + } + break; + } + + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + Cinfo->Active = TRUE; + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hTermActMenu, "Actions"); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hTermCfgMenu, "Config"); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hTermEdtMenu, "Edit"); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hTermHlpMenu, "Help"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM)hWndMenu); + + if (Cinfo->CONNECTED) + { + EnableDisconnectMenu(hWnd); + DisableConnectMenu(hWnd); + } + else + { + DisableDisconnectMenu(hWnd); + EnableConnectMenu(hWnd); + } + SetFocus(Cinfo->hwndInput); + } + else + { + // Deactivate + + Cinfo->Active = FALSE; + RemoveMenu(hBaseMenu, 4, MF_BYPOSITION); + RemoveMenu(hBaseMenu, 3, MF_BYPOSITION); + RemoveMenu(hBaseMenu, 2, MF_BYPOSITION); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Cinfo->Height = lprc->bottom-lprc->top; + Cinfo->Width = lprc->right-lprc->left; + Cinfo->Top = lprc->top; + Cinfo->Left = lprc->left; + + MoveWindows(Cinfo); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + MoveWindows(Cinfo); + + // Drop through to Move + + + case WM_MOVE: + + GetWindowRect(hWnd, &Rect); + + Cinfo->Height = Rect.bottom-Rect.top; + Cinfo->Width = Rect.right-Rect.left; + Cinfo->Top = Rect.top; + Cinfo->Left = Rect.left; + + // Male relative to Frame + + GetWindowRect(FrameWnd, &Rect); + + Cinfo->Top -= Rect.top ; + Cinfo->Left -= Rect.left; + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case RTFCOPY: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(Cinfo->hConsole)) + { + len = SendMessage(Cinfo->hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + + SetFocus(Cinfo->hwndInput); + break; + + } + + case BPQCONNECT: + + SessionControl(Cinfo->BPQStream, 1, 0); + break; + + case BPQDISCONNECT: + + SessionControl(Cinfo->BPQStream, 2, 0); + break; + + + case BPQHELP: + + ShellExecute(hWnd,"open", + "http://www.cantab.net/users/john.wiseman/Documents/BPQTerminal.htm", + "", NULL, SW_SHOWNORMAL); + break; + + case ID_WINDOWS_RESTORE: + + ToggleRestoreWindows(); + break; + + case ID_SETUP_FONT: + + FontCinfo = Cinfo; + DialogBox(hInst, MAKEINTRESOURCE(IDD_FONT), hWnd, FontConfigWndProc); + i = GetLastError(); + break; + + case BPQCLEAROUT: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + + break; + + + case BPQCOPYOUT: + + CopyRichTextToClipboard(Cinfo->hwndOutput); + break; + + case BPQBELLS: + + ToggleParam(hTermCfgMenu, &Bells, BPQBELLS); + break; + + case BPQStripLF: + + ToggleParam(hTermCfgMenu, &StripLF, BPQStripLF); + break; + + case BPQLogOutput: + + ToggleParam(hTermCfgMenu, &LogOutput, BPQLogOutput); + break; + + case CHATTERM: + + ToggleParam(hTermCfgMenu, &ChatMode, CHATTERM); + break; + + case BPQSendDisconnected: + + ToggleParam(hTermCfgMenu, &SendDisconnected, BPQSendDisconnected); + break; + + case ID_WARNWRAP: + + ToggleParam(hTermCfgMenu, &WarnWrap, ID_WARNWRAP); + break; + + case ID_WRAP: + + ToggleParam(hTermCfgMenu, &WrapInput, ID_WRAP); + break; + + case ID_FLASHONBELL: + + ToggleParam(hTermCfgMenu, &FlashOnBell, ID_FLASHONBELL); + break; + } + + break; + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + Cinfo->Minimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + DoRefresh(Cinfo); + + break; + + case SC_MINIMIZE: + + Cinfo->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_CLOSE: + break; // Go on to call DefMDIChildProc + + case WM_DESTROY: + + // Close session and release stream + +#define GWL_WNDPROC (-4) + + SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, (LONG) Cinfo->wpOrigInputProc); + + SessionControl(Cinfo->BPQStream, 2, 0); + + if (Cinfo->Incoming == FALSE) + DeallocateStream(Cinfo->BPQStream); + + // Free Scrollback + + for (i = 0; i < MAXSTACK ; i++) + { + if (Cinfo->KbdStack[i]) + { + free(Cinfo->KbdStack[i]); + Cinfo->KbdStack[i] = NULL; + } + } + + Sleep(500); + + if (Cinfo->readbuff) + free(Cinfo->readbuff); + + // Remove from List + + + for (Cinfo = ConsHeader; Cinfo; last = Cinfo, Cinfo = Cinfo->next) + { + if (Cinfo->hConsole == hWnd) + { + if (last) + { + last->next = Cinfo->next; + free(Cinfo); + return 0; + } + else + { + // First in list + + ConsHeader = Cinfo->next; + free(Cinfo); + return 0; + } + } + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); +} + +#ifndef MDIKERNEL + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + UCHAR Buffer[1000]; + UCHAR * buf = Buffer; + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + HWND m_hwndSystemInformation = 0; + struct ConsoleInfo * Cinfo; + int i, disp; + HKEY hKey=0; + char Size[80], Item[20]; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) { + + case WM_CREATE: + // On creation of main frame, create the MDI client area + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ghMDIClientArea = 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: + switch(LOWORD(wParam)) + { + case ID_NEWWINDOW: + + CreateChildWindow(0); + + break; + + case ID_HELP_ABOUT: + MessageBox(FrameWnd, TEXT("Sigma by Sharath C V"), TEXT("Sigma"), MB_OK); + break; + case ID_FILE_EXIT: + PostQuitMessage(0); + break; + case ID_WINDOWS_CASCADE: + SendMessage(ghMDIClientArea, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ghMDIClientArea, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + + // Handle MDI Window commands + + default: + { + if(LOWORD(wParam) >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ghMDIClientArea, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ghMDIClientArea, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + + break; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + if (MinimizetoTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefFrameProc(hWnd, ghMDIClientArea, message, wParam, lParam)); + + } + + case WM_DESTROY: + + GetWindowRect(hWnd, &Rect); // For save soutine + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + // Save config + + RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + RegSetValueEx(hKey,"ChatMode",0,REG_DWORD,(BYTE *)&ChatMode,4); + RegSetValueEx(hKey,"PortMask",0,REG_DWORD,(BYTE *)&portmask,4); + RegSetValueEx(hKey,"Bells",0,REG_DWORD,(BYTE *)&Bells,4); + RegSetValueEx(hKey,"StripLF",0,REG_DWORD,(BYTE *)&StripLF,4); + RegSetValueEx(hKey,"SendDisconnected",0,REG_DWORD,(BYTE *)&SendDisconnected,4); + RegSetValueEx(hKey,"RestoreWindows",0,REG_DWORD,(BYTE *)&RestoreWindows,4); + RegSetValueEx(hKey,"MTX",0,REG_DWORD,(BYTE *)&mtxparam,4); + RegSetValueEx(hKey,"MCOM",0,REG_DWORD,(BYTE *)&mcomparam,4); + RegSetValueEx(hKey,"MONColour",0,REG_DWORD,(BYTE *)&MonitorColour,4); + RegSetValueEx(hKey,"MonitorNODES",0,REG_DWORD,(BYTE *)&MonitorNODES,4); + + sprintf(Size,"%d,%d,%d,%d",Rect.left,Rect.right,Rect.top,Rect.bottom); + RegSetValueEx(hKey,"Size",0,REG_SZ,(BYTE *)&Size, strlen(Size)); + + // Close any sessions + + i = 0; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + i++; + + sprintf(Size, "%d,%d,%d,%d", Cinfo->Left, Cinfo->Height, Cinfo->Top, Cinfo->Width); + sprintf(Item, "SessSize%d", i); + RegSetValueEx(hKey, Item, 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + SessionControl(Cinfo->BPQStream, 2, 0); + DeallocateStream(Cinfo->BPQStream); + } + + RegSetValueEx(hKey,"Sessions",0,REG_DWORD,(BYTE *)&i,4); + + sprintf(Size, "%d,%d,%d,%d", MonWindow.Left, MonWindow.Height, MonWindow.Top, MonWindow.Width); + RegSetValueEx(hKey, "MonSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + RegCloseKey(hKey); + + break; + + default: + return (DefFrameProc(hWnd, ghMDIClientArea, message, wParam, lParam)); + + } + return (0); +} + +#endif + +/* +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + UCHAR Buffer[1000]; + UCHAR * buf = Buffer; + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + 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 WM_CTLCOLOREDIT: + + if (OutputData.Scrolled) + { + HDC hdcStatic = (HDC)wParam; + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)GetStockObject(LTGRAY_BRUSH); + } + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + // lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + return TRUE; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + SendMessage(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM) Buffer); + + GetTextMetrics(lpdis->hDC, &tm); + + if (lpdis->hwndItem == hwndOutput) + { + CharWidth = tm.tmAveCharWidth; + maxlinelen = ClientWidth/CharWidth - 1; + } + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + if (Buffer[0] == 0x1b) + { + SetTextColor(lpdis->hDC, Colours[Buffer[1] - 10]); + buf += 2; + } +// SetBkColor(lpdis->hDC, 0); + + TextOut(lpdis->hDC, + 6, + y, + buf, + strlen(buf)); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + return TRUE; + + case WM_ACTIVATE: + + //fActive = LOWORD(wParam); // activation flag + //fMinimized = (BOOL) HIWORD(wParam); // minimized flag + // hwnd = (HWND) lParam; // window handle + + SetFocus(hwndInput); + break; + + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + if (wmId > BPQBASE && wmId < BPQBASE + 32) + { + TogglePort(hWnd,wmId,0x1 << (wmId - (BPQBASE + 1))); + break; + } + + switch (wmId) { + + case 40000: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(MainWnd)) + { + len = SendMessage(hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + SetFocus(hwndInput); + return TRUE; + } + + case ID_SETUP_FONT: + + // DialogBox(hInst, MAKEINTRESOURCE(IDD_FONT), hWnd, FontConfigWndProc); + break; + + + case BPQMTX: + + ToggleMTX(hWnd); + break; + + case BPQMCOM: + + ToggleMCOM(hWnd); + break; + + case BPQCONNECT: + + SessionControl(Stream, 1, 0); + break; + + case BPQDISCONNECT: + + SessionControl(Stream, 2, 0); + break; + + + case ID_ACTION_RESETWINDOWSPLIT: + + Split = 0.5; + SplitPos=Height*Split; + MoveWindows(); + + break; + + case BPQAUTOCONNECT: + + ToggleAutoConnect(hWnd); + break; + + case BPQAPPL1: + + ToggleAppl(hWnd,BPQAPPL1,0x1); + break; + + case BPQAPPL2: + + ToggleAppl(hWnd,BPQAPPL2,0x2); + break; + + case BPQAPPL3: + + ToggleAppl(hWnd,BPQAPPL3,0x4); + break; + + case BPQAPPL4: + + ToggleAppl(hWnd,BPQAPPL4,0x8); + break; + + case BPQAPPL5: + + ToggleAppl(hWnd,BPQAPPL5,0x10); + break; + + case BPQAPPL6: + + ToggleAppl(hWnd,BPQAPPL6,0x20); + break; + + case BPQAPPL7: + + ToggleAppl(hWnd,BPQAPPL7,0x40); + break; + + case BPQAPPL8: + + ToggleAppl(hWnd,BPQAPPL8,0x80); + break; + + case BPQFLAGS1: + + ToggleFlags(hWnd,BPQFLAGS1,0x01); + break; + + case BPQFLAGS2: + + ToggleFlags(hWnd,BPQFLAGS2,0x02); + break; + + case BPQFLAGS3: + + ToggleFlags(hWnd,BPQFLAGS3,0x04); + break; + + case BPQFLAGS4: + + ToggleFlags(hWnd,BPQFLAGS4,0x40); + break; + + case BPQBELLS: + + ToggleParam(hTermCfgMenu, &Bells, BPQBELLS); + break; + + case BPQStripLF: + + ToggleParam(hTermCfgMenu, &StripLF, BPQStripLF); + break; + + case BPQLogOutput: + + ToggleParam(hTermCfgMenu, &LogOutput, BPQLogOutput); + break; + + case CHATTERM: + + ToggleParam(hTermCfgMenu, &ChatMode, CHATTERM); + break; + + + case BPQSendDisconnected: + + ToggleParam(hTermCfgMenu, &SendDisconnected, BPQSendDisconnected); + break; + + case BPQMNODES: + + ToggleParam(hTermCfgMenu, &MonitorNODES, BPQMNODES); + break; + + case MONCOLOUR: + + ToggleParam(hTermCfgMenu, &MonitorColour, MONCOLOUR); + break; + + case BPQLogMonitor: + + ToggleParam(hTermCfgMenu, &LogMonitor, BPQLogMonitor); + break; + + case BPQCHAT: + + ToggleChat(hWnd); + break; + + case BPQCLEARMON: + + SendMessage(hwndMon,LB_RESETCONTENT, 0, 0); + break; + + case BPQCLEAROUT: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + + break; + + case BPQCOPYMON: + + CopyListToClipboard(hwndMon); + break; + + case BPQCOPYOUT: + + CopyRichTextToClipboard(hwndOutput); + break; + + case BPQHELP: + + ShellExecute(hWnd,"open", + "http://www.cantab.net/users/john.wiseman/Documents/BPQTerminal.htm", + "", NULL, SW_SHOWNORMAL); + break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + if (MinimizetoTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + SplitPos=Height*Split; + + MoveWindows(); + + return TRUE; + + case WM_SIZE: + + MoveWindows(); + return TRUE; + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &Rect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + SessionControl(Stream, 2, 0); + DeallocateStream(Stream); + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + +*/ + +//static char * KbdStack[20]; + +//int StackIndex=0; + +LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int i; + unsigned int TextLen; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hwndInput == hwnd) + break; + } + + if (Cinfo == NULL) + Cinfo = &InitHeader; + + + if (uMsg == WM_KEYUP) + { + unsigned int i; +// Debugprintf("5%x", LOBYTE(HIWORD(lParam))); + + if (LOBYTE(HIWORD(lParam)) == 0x48 && wParam == 0x26) + { + // Scroll up + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput, WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + Cinfo->StackIndex++; + if (Cinfo->StackIndex == 20) + Cinfo->StackIndex = 19; + + return TRUE; + } + + if (LOBYTE(HIWORD(lParam)) == 0x50 && wParam == 0x28) + { + // Scroll up + + Cinfo->StackIndex--; + if (Cinfo->StackIndex < 0) + Cinfo->StackIndex = 0; + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + return TRUE; + } + } + + + if (uMsg == WM_CHAR) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (TextLen > INPUTLEN-10) Beep(220, 150); + + if(WarnWrap || WrapInput) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (WarnWrap) + if (TextLen == Cinfo->WarnLen) Beep(220, 150); + + if (WrapInput) + if ((wParam == 0x20) && (TextLen > Cinfo->WrapLen)) + wParam = 13; // Replace space with Enter + + } + + if (wParam == 13) + { + // if not connected, and autoconnect is + // enabled, connect now + + if (!Cinfo->CONNECTED) + { + if (Cinfo->Incoming) // Incoming call window + MessageBox(NULL, "Session is for Incoming Calls", "BPQTerm", MB_OK); + else + SessionControl(Cinfo->BPQStream, 1, 0); + } + + Cinfo->kbptr=SendMessage(Cinfo->hwndInput, WM_GETTEXT, INPUTLEN-1, + (LPARAM) (LPCSTR)Cinfo->kbbuf); + + Cinfo->StackIndex = 0; + + // Stack it + + if (Cinfo->KbdStack[19]) + free(Cinfo->KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + Cinfo->KbdStack[i+1] = Cinfo->KbdStack[i]; + } + + Cinfo->KbdStack[0] = _strdup(Cinfo->kbbuf); + + Cinfo->kbbuf[Cinfo->kbptr]=13; + + // Echo + + Cinfo->CurrentColour = 64; + WritetoOutputWindow(Cinfo, Cinfo->kbbuf, Cinfo->kbptr+1, TRUE); + Cinfo->CurrentColour = 1; + + if (Cinfo->Scrolled) + { + POINT Point; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(Cinfo->hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Scrolled = FALSE; + } + + + DoRefresh(Cinfo); +// ProcessLine(Cinfo, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + SendMsg(Cinfo->BPQStream, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + if (wParam == 0x1a) // Ctrl/Z + { + + Cinfo->kbbuf[0]=0x1a; + Cinfo->kbbuf[1]=13; + +// ProcessLine(Cinfo, &Cinfo->kbbuf[0], 2); + SendMsg(Cinfo->BPQStream, &Cinfo->kbbuf[0], 2); + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + + } + + return CallWindowProc(Cinfo->wpOrigInputProc, hwnd, uMsg, wParam, lParam); +} + + + + +/*LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + char DisplayLine[200] = "\x1b\xb"; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hwndInput == hwnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + + if (uMsg == WM_CTLCOLOREDIT) + { + HBRUSH Brush = CreateSolidBrush(RGB(0, 255, 0)); + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)Brush; + } + + + if (uMsg == WM_KEYUP) + { + unsigned int i; + +// Debugprintf("5%x", LOBYTE(HIWORD(lParam))); + + if (LOBYTE(HIWORD(lParam)) == 0x48 && wParam == 0x26) + { + // Scroll up + + if (KbdStack[StackIndex] == NULL) + return TRUE; + + SendMessage(hwndInput, WM_SETTEXT,0,(LPARAM)(LPCSTR) KbdStack[StackIndex]); + + for (i = 0; i < strlen(KbdStack[StackIndex]); i++) + { + SendMessage(hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + StackIndex++; + if (StackIndex == 20) + StackIndex = 19; + + return TRUE; + } + + if (LOBYTE(HIWORD(lParam)) == 0x50 && wParam == 0x28) + { + // Scroll up + + StackIndex--; + if (StackIndex < 0) + StackIndex = 0; + + if (KbdStack[StackIndex] == NULL) + return TRUE; + + SendMessage(hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) KbdStack[StackIndex]); + + for (i = 0; i < strlen(KbdStack[StackIndex]); i++) + { + SendMessage(hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + return TRUE; + } + } + + + + if (uMsg == WM_CHAR) + { + if (wParam == 13) + { + int i; + + // + // if not connected, and autoconnect is + // enabled, connect now + + if (!CONNECTED && AUTOCONNECT) + SessionControl(Stream, 1, 0); + + kbptr=SendMessage(Cinfo->hwndInput,WM_GETTEXT,159,(LPARAM) (LPCSTR) kbbuf); + + // Stack it + + StackIndex = 0; + + if (KbdStack[19]) + free(KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + KbdStack[i+1] = KbdStack[i]; + } + + KbdStack[0] = _strdup(kbbuf); + + kbbuf[kbptr]=13; + + SlowTimer = 0; + + // Echo, with set Black escape + + memcpy(&DisplayLine[2], kbbuf, kbptr+1); + + if (OutputData.Scrolled) + { + POINT Point; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + OutputData.Scrolled = FALSE; + } + + WritetoOutputWindow(&OutputData, DisplayLine, kbptr+3); + DoRefresh(&OutputData); + + + // Replace null with CR, and send to Node + + SendMsg(Stream, &kbbuf[0], kbptr+1); + + SendMessage(hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + if (wParam == 0x1a) // Ctrl/Z + { + // + // if not connected, and autoconnect is + // enabled, connect now + + if (!CONNECTED && AUTOCONNECT) + SessionControl(Stream, 1, 0); + + kbbuf[0]=0x1a; + kbbuf[1]=13; + + SendMsg(Stream, &kbbuf[0], 2); + + SendMessage(hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + + } + + return CallWindowProc(Cinfo->wpOrigInputProc, hwnd, uMsg, wParam, lParam); +} +*/ + + + +DoStateChange(int Stream) +{ + int port, sesstype, paclen, maxframe, l4window, len; + int state, change; + struct ConsoleInfo * Cinfo = NULL; + char callsign[11] = ""; + char Msg[80]; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->BPQStream == Stream) + break; + } + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + if (Cinfo == NULL) + { + // Incoming connect. Create a window for it + + Cinfo = CreateChildWindow(Stream, FALSE); + Cinfo->Incoming = TRUE; + Cinfo->Minimized = FALSE; + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + } + + Cinfo->CONNECTED = TRUE; + Cinfo->SlowTimer = 0; + + GetConnectionInfo(Stream, callsign, + &port, &sesstype, &paclen, + &maxframe, &l4window); + + if (Cinfo->Incoming == TRUE) + { + char conMsg[] = "Send ^D to disconnect\r"; + + SendMsg(Stream, conMsg, strlen(conMsg)); + + len = sprintf(Msg, "*** Incoming Call from %s\r", callsign); + WritetoOutputWindow(Cinfo, Msg, len, TRUE); + + PlaySound("IncomingCall", hInstance, SND_RESOURCE | SND_ASYNC); + + ShowWindow(FrameWnd, SW_SHOWNA); + + if (FrameMaximized == TRUE) + PostMessage(FrameWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(FrameWnd, WM_SYSCOMMAND, SW_SHOWNA, 0); + + } + + sprintf(Title,"Stream %d - Connected to %s", Stream, callsign); + SetWindowText(Cinfo->hConsole, Title); + DisableConnectMenu(FrameWnd); + EnableDisconnectMenu(FrameWnd); + + } + else + { + if (Cinfo == NULL) + return 0; + + Cinfo->CONNECTED=FALSE; + sprintf(Title,"Stream %d - Disconnected", Stream); + SetWindowText(Cinfo->hConsole,Title); + DisableDisconnectMenu(FrameWnd); + EnableConnectMenu(FrameWnd); + + if (SendDisconnected) + { + WritetoOutputWindow(Cinfo, "*** Disconnected\r", 17, TRUE); + } + } + } + + return (0); + +} + +DoReceivedData(int Stream) +{ + char Msg[300]; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->BPQStream == Stream) + break; + } + + if (Cinfo == NULL) + return 0; + + + if (RXCount(Cinfo->BPQStream) > 0) + { + do { + + GetMsg(Cinfo->BPQStream, &Msg[0],&len,&count); + + if (len == 1 && Msg[0] == 0) + continue; + + if (GetApplNum(Cinfo->BPQStream) == 32) // Host Appl + { + // Check for ^D to disconnect + + if (_memicmp(Msg, "^d\r", 3) == 0) + { + SessionControl(Cinfo->BPQStream, 2, 0); + } + } + + WritetoOutputWindow(Cinfo, Msg, len, FALSE); + + Cinfo->SlowTimer = 0; + + if (Cinfo->Active == FALSE) + FlashWindow(Cinfo->hConsole, TRUE); +// Beep(440,250); + + } while (count > 0); + + DoRefresh(Cinfo); + + } + return (0); +} + +VOID wcstoRTF(char * out, WCHAR * in) +{ + WCHAR * ptr1 = in; + char * ptr2 = out; + int val = *ptr1++; + + while (val) + { + // May be Code Page or Unicode + { + if (val > 255 || val < -255 ) + ptr2 += sprintf(ptr2, "\\u%d ", val); + else if (val > 127 || val < -128) + ptr2 += sprintf(ptr2, "\\'%02X", val); + else + *(ptr2++) = val; + } + val = *ptr1++; + } + *ptr2 = 0; +} + +DWORD CALLBACK EditStreamCallback(struct ConsoleInfo * Cinfo, LPBYTE lpBuff, LONG cb, PLONG pcb) +{ + int ReqLen = cb; + int i; + int Line; + int NewLen; + int err; + + char LineB[12048]; + WCHAR LineW[12048]; + +// if (cb != 4092) +// return 0; + + if (Cinfo->SendHeader) + { + // Return header + + memcpy(lpBuff, RTFHeader, RTFHddrLen); + *pcb = RTFHddrLen; + Cinfo->SendHeader = FALSE; + Cinfo->Finished = FALSE; + Cinfo->Index = 0; + return 0; + } + + if (Cinfo->Finished) + { + *pcb = 0; + return 0; + } + +/* + if (BufferLen > cb) + { + memcpy(lpBuff, &Buffer[Offset], cb); + BufferLen -= cb; + Offset += cb; + *pcb = cb; + return 0; + } + + memcpy(lpBuff, &Buffer[Offset], BufferLen); + + *pcb = BufferLen; +*/ + + // Return 10 line at a time + + for (i = 0; i < 10; i++); + { + Line = Cinfo->Index++ + Cinfo->CurrentLine - MAXLINES; + + if (Line <0) + Line = Line + MAXLINES; + + sprintf(lpBuff, "\\cf%d ", Cinfo->Colourvalue[Line]); + + // Handle UTF-8 + + NewLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, Cinfo->OutputScreen[Line], -1, LineW, 2000); + + err = GetLastError(); + + if (err == ERROR_NO_UNICODE_TRANSLATION) + { + // Input isn't UTF 8. Juat use default 8 bit set# + + strcat(lpBuff, Cinfo->OutputScreen[Line]); + } + else + { + if (LineW[0] != 0) + { + wcstoRTF(LineB, LineW); + strcat(lpBuff, LineB); + } + } + } + strcat(lpBuff, "\\line"); + + if (Cinfo->Index == MAXLINES) + { + Cinfo->Finished = TRUE; + strcat(lpBuff, "}"); + i = 10; + } + + *pcb = strlen(lpBuff); + return 0; +} + + +VOID DoRefresh(struct ConsoleInfo * Cinfo) +{ + EDITSTREAM es = {0}; + int Min, Max, Pos; + POINT Point; + SCROLLINFO ScrollInfo; + int LoopTrap = 0; + HWND hwndOutput = Cinfo->hwndOutput; + + if(WINE) + Cinfo->Thumb = 30000; + else + Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + Pos = Cinfo->Thumb + Cinfo->ClientHeight; + + if ((Cinfo->Thumb + Cinfo->ClientHeight) > Cinfo->RTFHeight - 10) // Don't bother writing to screen if scrolled back + { + es.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback; + es.dwCookie = (DWORD_PTR)Cinfo; + Cinfo->SendHeader = TRUE; + SendMessage(hwndOutput, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); + ScrollInfo.cbSize = sizeof(ScrollInfo); + ScrollInfo.fMask = SIF_ALL; + + GetScrollInfo(hwndOutput, SB_VERT, &ScrollInfo); + +// Debugprintf("Pos %d Max %d Min %d nMax %d ClientH %d", Pos, Min, Max, ScrollInfo.nMax, Cinfo->ClientHeight); + + if (Cinfo->FirstTime == FALSE) + { + // RTF Controls don't immediately scroll to end - don't know why. + + Cinfo->FirstTime = TRUE; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + while (LoopTrap++ < 20) + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + Cinfo->RTFHeight = Max; + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + } + + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + + if (Cinfo->Thumb > (Point.y - 10)) // Don't Scroll if user has scrolled back + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + + if (Cinfo->Scrolled) + { + Cinfo->Scrolled = FALSE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } + return; + } + + if (!Cinfo->Scrolled) + { + Cinfo->Scrolled = TRUE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } +} + +/*VOID DoRefresh(struct RTFTerm * OPData) +{ + EDITSTREAM es = {0}; + int Min, Max, Pos; + POINT Point; + SCROLLINFO ScrollInfo; + int LoopTrap = 0; + + if(WINE) + OPData->Thumb = 30000; + else + OPData->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + + Pos = OPData->Thumb + OutputBoxHeight; + + if (Pos > OPData->RTFHeight - 10) // Don't bother writing to screen if scrolled back + { + es.pfnCallback = EditStreamCallback; + es.dwCookie = (DWORD_PTR)OPData; + OPData->SendHeader = TRUE; + SendMessage(hwndOutput, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } +// else +// Debugprintf("Pos %d RTFHeight %d - Not refreshing", Pos, OPData->RTFHeight); + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); + ScrollInfo.cbSize = sizeof(ScrollInfo); + ScrollInfo.fMask = SIF_ALL; + + GetScrollInfo(hwndOutput, SB_VERT, &ScrollInfo); + +// Debugprintf("Thumb %d Pos %d Min %d Max %d nMax %d ClientH %d RTFHeight %d", +// OPData->Thumb, Pos, Min, Max, ScrollInfo.nMax, OutputBoxHeight, OPData->RTFHeight); + + if (OPData->FirstTime == FALSE) + { + // RTF Controls don't immediately scroll to end - don't know why. + + OPData->FirstTime = TRUE; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + while (LoopTrap++ < 20) + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + OPData->RTFHeight = Max; + Point.x = 0; + Point.y = OPData->RTFHeight - ScrollInfo.nPage; + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + OPData->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + } + + Point.x = 0; + Point.y = OPData->RTFHeight - ScrollInfo.nPage; + + if (OPData->Thumb > (Point.y - 10)) // Don't Scroll if user has scrolled back + { +// Debugprintf("Scrolling to %d", Point.y); + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + if (OPData->Scrolled) + { + OPData->Scrolled = FALSE; + InvalidateRect(hwndInput, NULL, TRUE); + } + return; + } + +// Debugprintf("Thumb = %d Point.y = %d - Not Scrolling", OPData->Thumb, Point.y); + + if (!OPData->Scrolled) + { + OPData->Scrolled = TRUE; + InvalidateRect(hwndInput, NULL, TRUE); + } +} + +*/ +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, char * Line) +{ + int Len = strlen(Line); + char * ptr1 = Line; + char * ptr2; + int l, Index; + char LineCopy[LINELEN * 2]; + + if (strlen(Line) > 200) + { + Line[199] = 0; + } + + if (Line[0] == 0x1b && Len > 1) + { + // Save Colour Char + + Cinfo->CurrentColour = Line[1] - 10; + ptr1 +=2; + Len -= 2; + } + + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], ptr1); + + // Look for chars we need to escape (\ { }) + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '\\'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ++ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy Including found char + Index += l; + LineCopy[Index++] = '\\'; + Len++; + ptr1 = ptr2; + ptr2 = strchr(ptr1, '\\'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '{'); + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '{'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '{'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '}'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '}'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '}'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + + Cinfo->Colourvalue[Cinfo->CurrentLine] = Cinfo->CurrentColour; + Cinfo->LineLen[Cinfo->CurrentLine++] = Len; + if (Cinfo->CurrentLine >= MAXLINES) Cinfo->CurrentLine = 0; +} + + + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, char * Msg, int len); + +int WritetoOutputWindow(struct ConsoleInfo * Cinfo, char * Msg, int len, BOOL Refresh) +{ + WritetoConsoleWindowSupport(Cinfo, Msg, len); + + if (Cinfo->Minimized) + return 0; + + if (Refresh) + DoRefresh(Cinfo); + else + Cinfo->NeedRefresh = TRUE; + + return 0; +} + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, char * Msg, int len) +{ + char * ptr1, * ptr2; + + if (len + Cinfo->PartLinePtr > Cinfo->readbufflen) + { + Cinfo->readbufflen += len + Cinfo->PartLinePtr; + Cinfo->readbuff = realloc(Cinfo->readbuff, Cinfo->readbufflen + 100); + } + + if (Cinfo->PartLinePtr != 0) + { + Cinfo->CurrentLine--; // Overwrite part line in buffer + if (Cinfo->CurrentLine < 0) + Cinfo->CurrentLine = MAXLINES - 1; + + + if (Msg[0] == 0x1b && len > 1) + { + Msg += 2; // Remove Colour Escape + len -= 2; + } + } + + memcpy(&Cinfo->readbuff[Cinfo->PartLinePtr], Msg, len); + + len=len+Cinfo->PartLinePtr; + + ptr1=&Cinfo->readbuff[0]; + Cinfo->readbuff[len]=0; + + if (Bells) + { + do { + + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + + if (FlashOnBell) + FlashWindow(Cinfo->hConsole, TRUE); + else + Beep(440,250); + } + + } while (ptr2); + } + +lineloop: + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + Cinfo->PartLinePtr=len; + memmove(Cinfo->readbuff,ptr1,len); + AddLinetoWindow(Cinfo, ptr1); +// InvalidateRect(Cinfo->hwndOutput, NULL, FALSE); + + return (0); + } + + *(ptr2++)=0; + + // If len is greater that screen with, fold + + if (Cinfo == &MonWindow) + { + if (LogMonitor) + WriteMonitorLine(ptr1, ptr2 - ptr1); + } + else + if (LogOutput) + WriteMonitorLine(ptr1, ptr2 - ptr1); + + if ((ptr2 - ptr1) > Cinfo->maxlinelen) + { + char * ptr3; + char * saveptr1 = ptr1; + int linelen = ptr2 - ptr1; + int foldlen; + char save; + + foldloop: + + ptr3 = ptr1 + Cinfo->maxlinelen; + + while(*ptr3!= 0x20 && ptr3 > ptr1) + { + ptr3--; + } + + foldlen = ptr3 - ptr1 ; + + if (foldlen == 0) + { + // No space before, so split at width + + foldlen = Cinfo->maxlinelen; + ptr3 = ptr1 + Cinfo->maxlinelen; + + } + else + { + ptr3++ ; // Omit space + linelen--; + } + save = ptr1[foldlen]; + ptr1[foldlen] = 0; + AddLinetoWindow(Cinfo, ptr1); + ptr1[foldlen] = save; + linelen -= foldlen; + ptr1 = ptr3; + + if (linelen > Cinfo->maxlinelen) + goto foldloop; + + AddLinetoWindow(Cinfo, ptr1); + + ptr1 = saveptr1; + } + else + AddLinetoWindow(Cinfo, ptr1); + + Cinfo->PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && Cinfo->StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + goto lineloop; + } + + + return (0); +} + + + +/* +VOID AddLinetoWindow(struct RTFTerm * OPData, char * Line) +{ + int Len = strlen(Line); + char * ptr1 = Line; + char * ptr2; + int l, Index; + char LineCopy[LINELEN * 2]; + + if (Line[0] == 0x1b && Len > 1) + { + // Save Colour Char + + OPData->CurrentColour = Line[1] - 10; + ptr1 +=2; + Len -= 2; + } + + strcpy(OPData->OutputScreen[OPData->CurrentLine], ptr1); + + // Look for chars we need to escape (\ { }) + + ptr1 = OPData->OutputScreen[OPData->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '\\'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ++ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy Including found char + Index += l; + LineCopy[Index++] = '\\'; + Len++; + ptr1 = ptr2; + ptr2 = strchr(ptr1, '\\'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(OPData->OutputScreen[OPData->CurrentLine], LineCopy); + } + + ptr1 = OPData->OutputScreen[OPData->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '{'); + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '{'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '{'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(OPData->OutputScreen[OPData->CurrentLine], LineCopy); + } + + ptr1 = OPData->OutputScreen[OPData->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '}'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '}'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '}'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(OPData->OutputScreen[OPData->CurrentLine], LineCopy); + } + + + OPData->Colourvalue[OPData->CurrentLine] = OPData->CurrentColour; + OPData->LineLen[OPData->CurrentLine++] = Len; + if (OPData->CurrentLine >= MAXLINES) OPData->CurrentLine = 0; +} + + +VOID WritetoOutputWindow(struct RTFTerm * OPData, char * Msg, int len) +{ + char * ptr1, * ptr2; + + if (PartLinePtr > LINELEN) + Msg[len++] = 13; // Force a newline + + if (PartLinePtr != 0) + { + OPData->CurrentLine--; // Overwrite part line in buffer + + if (Msg[0] == 0x1b && len > 1) + { + Msg += 2; // Remove Colour Escape + len -= 2; + } + } + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len += PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + if (Bells) + { + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + Beep(440,250); + } + } while (ptr2); + } + +lineloop: + + if (len > 0) + { + // copy text to buffer a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr = len; + memmove(readbuff,ptr1,len); + AddLinetoWindow(Cinfo, ptr1); + return; + } + + *(ptr2++)=0; + + if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + // If len is greater that screen with, fold + + if ((ptr2 - ptr1) > maxlinelen) + { + char * ptr3; + char * saveptr1 = ptr1; + int linelen = ptr2 - ptr1; + int foldlen; + char save; + + foldloop: + + ptr3 = ptr1 + maxlinelen; + + while(*ptr3!= 0x20 && ptr3 > ptr1) + { + ptr3--; + } + foldlen = ptr3 - ptr1 ; + + if (foldlen == 0) + { + // No space before, so split at width + + foldlen = maxlinelen; + ptr3 = ptr1 + maxlinelen; + + } + else + { + ptr3++ ; // Omit space + linelen--; + } + save = ptr1[foldlen]; + ptr1[foldlen] = 0; + + AddLinetoWindow(Cinfo, ptr1); + + ptr1[foldlen] = save; + linelen -= foldlen; + ptr1 = ptr3; + + if (linelen > maxlinelen) + goto foldloop; + + AddLinetoWindow(Cinfo, ptr1); + ptr1 = saveptr1; + + } + else + AddLinetoWindow(Cinfo, ptr1); + + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + goto lineloop; + } +} + + +*/ +DoMonData(int Stream) +{ + char * ptr1, * ptr2; + int stamp; + int len; + unsigned char buffer[1024] = "\x1b\xb", monbuff[512]; + struct ConsoleInfo * Cinfo; + + // Monitor data uses the first stream + + Cinfo = &MonWindow; + + if (MONCount(Stream) > 0) + { + do { + + stamp=GetRaw(Stream, monbuff,&len,&count); + + if (MonitorColour) + { + if (monbuff[4] & 0x80) // TX + buffer[1] = 91; + else + buffer[1] = 17; + } + + // See if a NODES + + if (!MonitorNODES && monbuff[21] == 3 && monbuff[22] == 0xcf && monbuff[23] == 0xff) + len = 0; + else + { + len=DecodeFrame(monbuff,&buffer[2],stamp); + len +=2; + } + + ptr1=&buffer[0]; + + lineloop: + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + memmove(buffer,ptr1,len); + + return (0); + + } + else + { + ptr2++; + + WritetoOutputWindow(Cinfo, ptr1, ptr2 - ptr1, FALSE); + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + goto lineloop; + } + } + + } while (count > 0); + + Cinfo->NeedRefresh = TRUE; + } + return (0); +} + + + + + +int DisableConnectMenu(HWND hWnd) +{ + HMENU hMenu; // handle of menu + + hMenu=GetMenu(FrameWnd); + + EnableMenuItem(hMenu,BPQCONNECT,MF_GRAYED); + + return (0); +} +int DisableDisconnectMenu(HWND hWnd) +{ + HMENU hMenu; // handle of menu + + hMenu=GetMenu(FrameWnd); + + EnableMenuItem(hMenu,BPQDISCONNECT,MF_GRAYED); + return (0); +} + +int EnableConnectMenu(HWND hWnd) +{ + HMENU hMenu; // handle of menu + + hMenu=GetMenu(FrameWnd); + + EnableMenuItem(hMenu,BPQCONNECT,MF_ENABLED); + return (0); +} + +int EnableDisconnectMenu(HWND hWnd) +{ + HMENU hMenu; // handle of menu + + hMenu=GetMenu(FrameWnd); + + EnableMenuItem(hMenu,BPQDISCONNECT,MF_ENABLED); + + return (0); +} + +int ToggleRestoreWindows() +{ + RestoreWindows = !RestoreWindows; + + if (RestoreWindows) + { + CheckMenuItem(hMonCfgMenu,ID_WINDOWS_RESTORE,MF_CHECKED); + CheckMenuItem(hTermCfgMenu,ID_WINDOWS_RESTORE,MF_CHECKED); + } + else + { + CheckMenuItem(hMonCfgMenu, ID_WINDOWS_RESTORE, MF_UNCHECKED); + CheckMenuItem(hTermCfgMenu, ID_WINDOWS_RESTORE, MF_UNCHECKED); + } + return (0); + +} +/* +int ToggleAppl(HWND hWnd, int Item, int mask) +{ + HMENU hMenu; // handle of menu + + hMenu=GetMenu(hWnd); + + APPLMASK = APPLMASK ^ mask; + + if (APPLMASK & mask) + + CheckMenuItem(hMenu,Item,MF_CHECKED); + + else + + CheckMenuItem(hMenu,Item,MF_UNCHECKED); + + SetAppl(Stream,applflags,APPLMASK); + + return (0); + +} + +int ToggleFlags(HWND hWnd, int Item, int mask) +{ + HMENU hMenu; // handle of menu + + hMenu=GetMenu(hWnd); + + applflags ^= mask; + + if (applflags & mask) + + CheckMenuItem(hMenu,Item,MF_CHECKED); + + else + + CheckMenuItem(hMenu,Item,MF_UNCHECKED); + + return (0); + +} + +*/ + +static CopyScreentoBuffer(char * buff) +{ + return (0); +} + +int TogglePort(HWND hWnd, int Item, int mask) +{ + portmask ^= mask; + + if (portmask & mask) + CheckMenuItem(hMonCfgMenu,Item,MF_CHECKED); + else + CheckMenuItem(hMonCfgMenu,Item,MF_UNCHECKED); + + if (portmask) + applflags |= 0x80; + else + applflags &= 0x7f; + + SetAppl(Stream,applflags,APPLMASK); + + SetTraceOptionsEx(portmask,mtxparam,mcomparam, monUI); + + return (0); + +} +int ToggleMTX(HWND hWnd) +{ + mtxparam = mtxparam ^ 1; + + if (mtxparam & 1) + + CheckMenuItem(hMonCfgMenu,BPQMTX,MF_CHECKED); + + else + + CheckMenuItem(hMonCfgMenu,BPQMTX,MF_UNCHECKED); + + SetTraceOptionsEx(portmask,mtxparam,mcomparam, monUI); + + return (0); + +} +int ToggleMCOM(HWND hWnd) +{ + mcomparam = mcomparam ^ 1; + + if (mcomparam & 1) + + CheckMenuItem(hMonCfgMenu,BPQMCOM,MF_CHECKED); + + else + + CheckMenuItem(hMonCfgMenu,BPQMCOM,MF_UNCHECKED); + + SetTraceOptionsEx(portmask,mtxparam,mcomparam, monUI); + + return (0); + +} +int ToggleMON_UI_ONLY(HWND hWnd) +{ + monUI = monUI ^ 1; + + if (monUI & 1) + + CheckMenuItem(hMonCfgMenu,MON_UI_ONLY,MF_CHECKED); + + else + + CheckMenuItem(hMonCfgMenu,MON_UI_ONLY,MF_UNCHECKED); + + SetTraceOptionsEx(portmask,mtxparam,mcomparam, monUI); + + return (0); + +} +int ToggleParam(HMENU hMenu, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu, Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +int ToggleChat(HWND hWnd) +{ + APPLMASK = APPLMASK ^ 02; + + if (APPLMASK & 02) + + CheckMenuItem(hTermCfgMenu,BPQCHAT,MF_CHECKED); + + else + + CheckMenuItem(hTermCfgMenu,BPQCHAT,MF_UNCHECKED); + + SetAppl(Stream,applflags,APPLMASK); + + return (0); + +} +/* +void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + if (hWnd == 0) + return; + + GetWindowRect(hWnd, &rcMain); + GetClientRect(hWnd, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (ClientWidth == 0) // Minimized + return; + + OutputBoxHeight = ClientHeight - SplitPos - InputBoxHeight - SplitBarHeight - SplitBarHeight; + + MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, SplitPos+SplitBarHeight, ClientWidth-4, OutputBoxHeight, TRUE); + MoveWindow(hwndInput,2, ClientHeight-InputBoxHeight-2, ClientWidth-4, InputBoxHeight, TRUE); + MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); + + GetClientRect(hwndOutput, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (ClientWidth > 16) + maxlinelen = ClientWidth/OutputData.CharWidth - 1; + + InvalidateRect(hWnd, NULL, TRUE); +} + +*/ +static void MoveWindows(struct ConsoleInfo * Cinfo) +{ + RECT rcClient= {0,0,0,0}; + int ClientWidth; + + GetClientRect(Cinfo->hConsole, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (Cinfo->hwndInput) + { + MoveWindow(Cinfo->hwndOutput,2, 2, ClientWidth-4, Cinfo->ClientHeight-InputBoxHeight-4, TRUE); + MoveWindow(Cinfo->hwndInput,2, Cinfo->ClientHeight-InputBoxHeight-2, ClientWidth-4, InputBoxHeight, TRUE); + } + else + MoveWindow(Cinfo->hwndOutput,2, 2, ClientWidth-4, Cinfo->ClientHeight-4, TRUE); + + GetClientRect(Cinfo->hwndOutput, &rcClient); + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + Cinfo->WarnLen = ClientWidth/8 - 1; + Cinfo->WrapLen = Cinfo->WarnLen; + Cinfo->maxlinelen = Cinfo->WarnLen; + +} + +void CopyRichTextToClipboard(HWND hWnd) +{ + int len=0; + HGLOBAL hMem; + char * ptr; + + // Copy Rich Text to Clipboard + + len = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); + + hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(FrameWnd)) + { + len = SendMessage(hWnd, WM_GETTEXT , len, (LPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); +} + +BOOL OpenMonitorLogfile() +{ + UCHAR * BPQDirectory=GetBPQDirectory(); + UCHAR FN[MAX_PATH]; + + if (BPQDirectory[0] == 0) + sprintf(FN,"BPQTerm.log"); + else + sprintf(FN,"%s\\BPQTerm.log", BPQDirectory); + + MonHandle = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(MonHandle, 0, 0, FILE_END); + + return (MonHandle != INVALID_HANDLE_VALUE); +} + +void WriteMonitorLine(char * Msg, int MsgLen) +{ + int cnt; + char CRLF[2] = {0x0d,0x0a}; + + if (MonHandle == INVALID_HANDLE_VALUE) OpenMonitorLogfile(); + + if (MonHandle == INVALID_HANDLE_VALUE) return; + + WriteFile(MonHandle ,Msg , MsgLen, &cnt, NULL); + WriteFile(MonHandle ,CRLF , 2, &cnt, NULL); +} + +INT_PTR CALLBACK FontConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) + +{ + POINT Point; + int Min, Max; + int retCode, disp; + HKEY hKey=0; + + + switch (message) + { + case WM_INITDIALOG: + + SetDlgItemText(hDlg, IDC_FONTNAME, FontName); + SetDlgItemInt(hDlg, IDC_CHARSET, CharSet, FALSE); + SetDlgItemInt(hDlg, IDC_CODEPAGE, CodePage, FALSE); + SetDlgItemInt(hDlg, IDC_FONTSIZE, FontSize, FALSE); + SetDlgItemInt(hDlg, IDC_FONTWIDTH, FontWidth, FALSE); + + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + + GetDlgItemText(hDlg, IDC_FONTNAME, FontName, 99); + CharSet = GetDlgItemInt(hDlg, IDC_CHARSET, NULL, FALSE); + CodePage = GetDlgItemInt(hDlg, IDC_CODEPAGE, NULL, FALSE); + FontSize = GetDlgItemInt(hDlg, IDC_FONTSIZE, NULL, FALSE); + FontWidth = GetDlgItemInt(hDlg, IDC_FONTWIDTH, NULL, FALSE); + +// SaveStringValue("FontName", FontName); +// SaveIntValue("CharSet", CharSet); +// SaveIntValue("CodePage", CodePage); +// SaveIntValue("FontSize", FontSize); +// SaveIntValue("FontWidth", FontWidth); + + + // Save Config + + retCode = RegCreateKeyEx(REGTREE, + Key, + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"FontWidth",0,REG_DWORD,(BYTE *)&FontWidth,4); + retCode = RegSetValueEx(hKey,"FontSize",0,REG_DWORD,(BYTE *)&FontSize,4); + retCode = RegSetValueEx(hKey,"CodePage",0,REG_DWORD,(BYTE *)&CodePage,4); + retCode = RegSetValueEx(hKey,"CharSet",0,REG_DWORD,(BYTE *)&CharSet,4); + retCode = RegSetValueEx(hKey,"FontName",0,REG_SZ,(BYTE *)&FontName, strlen(FontName)); + + RegCloseKey(hKey); + } + + FontCinfo->CharWidth = FontWidth; + FontCinfo->FirstTime = FALSE; + + SetupRTFHddr(); + + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(FontCinfo->hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + FontCinfo->Scrolled = FALSE; + + GetScrollRange(FontCinfo->hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + FontCinfo->RTFHeight = Max; + + DoRefresh(FontCinfo); + EndDialog(hDlg, LOWORD(wParam)); + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + } + + return FALSE; + +} + + +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit) +{ + WNDCLASS wc; + HWND ChildWnd = 0; + struct ConsoleInfo * Cinfo; + struct ConsoleInfo * last = NULL; + int i = 1; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]=""; + char Item[80]; + RECT Rect= {100, 100, 800, 600}; + + char Title[80]; + + if (Stream == 0) + Stream = FindFreeStream(); + + if (Stream == 255) + { + MessageBox(NULL,"No free streams available",NULL,MB_OK); + return (FALSE); + } + + // Find end of session chain + + for (Cinfo = ConsHeader; Cinfo; last = Cinfo, i++, Cinfo = Cinfo->next); + + Cinfo = malloc(sizeof(struct ConsoleInfo)); + + memset(Cinfo, 0, sizeof(struct ConsoleInfo)); + + if (last) + last->next = Cinfo; + else + ConsHeader = Cinfo; + + Cinfo->BPQStream = Stream; + BPQSetHandle(Cinfo->BPQStream, FrameWnd); + +// BPQMsg = RegisterWindowMessage(BPQWinMsg); + + sprintf(Title, "Stream %d", Cinfo->BPQStream); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.lpfnWndProc = (WNDPROC)ChildWndProc; + wc.cbClsExtra = 0; + 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 = "ChildWnd"; + + if(!RegisterClass(&wc)) + { + // return if RegisterClassEx fails + DWORD dw_LastError = GetLastError(); + if(ERROR_CLASS_ALREADY_EXISTS != dw_LastError) + { + // return if the error is other than "error class already exists" + + Debugprintf("Reg Class Failed %d", dw_LastError); +// return NULL; + } + } + + ChildWnd = CreateMDIWindow("ChildWnd", Title, 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + Cinfo->hConsole = ChildWnd; + + // return if its not possible to create the child window + + if(NULL == ChildWnd) + { + DWORD dw_LastError = GetLastError(); + Debugprintf("Create Child Failed %d", dw_LastError); + return NULL; + } + + Cinfo->SendHeader = TRUE; + Cinfo->Finished = TRUE; + Cinfo->CurrentColour = 1; + + Cinfo->WarnLen = Cinfo->WrapLen = Cinfo->maxlinelen = 100; // In case doesn't get set up + + + Cinfo->hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 2,2,500,300, ChildWnd, NULL, hInstance, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(Cinfo->hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(Cinfo->hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + Cinfo->hwndInput = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "", + WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_NOHIDESEL, + 2,200,290,20, ChildWnd, NULL, hInstance, NULL); + + SendMessage(Cinfo->hwndInput, WM_SETFONT, (WPARAM)hFont, 0); + + // Set our own WndProcs for the controls. + + Cinfo->wpOrigInputProc = (WNDPROC) SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, (LONG) InputProc); + + sprintf(Item, "SessSize%d", i); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + Vallen=80; + RegQueryValueEx(hKey, Item, 0 , &Type, &Size[0], &Vallen); + + sscanf(Size,"%d,%d,%d,%d,%d", &Rect.left, &Rect.right, &Rect.top, &Rect.bottom, &Cinfo->Minimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 100; + Rect.top = 100; + 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; + } + + MoveWindow(Cinfo->hConsole, Rect.left - (OffsetW /2), Rect.top - OffsetH, + Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindows(Cinfo); + + SetFocus(Cinfo->hwndInput); + + if (Cinfo->Minimized && DuringInit) + ShowWindow(ChildWnd, SW_SHOWMINIMIZED); + else + ShowWindow(ChildWnd, SW_RESTORE); + + return Cinfo; +} + + +BOOL CreateMonitorWindow(char * MonSize) +{ + WNDCLASSEX wndclassMainFrame; + HWND ChildWnd = 0; + struct ConsoleInfo * Cinfo = &MonWindow; + RECT Rect = {0,0,0,0}; + char Size[80]; + HKEY hKey; + int retCode,Type,Vallen; + + memset(Cinfo, 0, sizeof(struct ConsoleInfo)); + + Cinfo->WarnLen = Cinfo->WrapLen = Cinfo->maxlinelen = 100; // In case doesn't get set up + Cinfo->StripLF = TRUE; + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE;; + wndclassMainFrame.lpfnWndProc = MonWndProc; + 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 = "MonWnd"; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + // return if RegisterClassEx fails + DWORD dw_LastError = GetLastError(); + if(ERROR_CLASS_ALREADY_EXISTS != dw_LastError) + { + // return if the error is other than "error class already exists" + return 0; + } + } + + // Create Window + + if(NULL != ChildWnd) + { + return 0; + } + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\BPQTermMDI"); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + Vallen=80; + RegQueryValueEx(hKey, "MonSize", 0 , &Type, &Size[0], &Vallen); + + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left, &Rect.right, &Rect.top, &Rect.bottom, &Cinfo->Minimized); + + if (Rect.right < 100 || Rect.bottom < 100) + { + Rect.right = 400; + 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; + } + + ChildWnd = CreateMDIWindow("MonWnd", "Monitor", 0, + Rect.left, Rect.top, + Rect.right - Rect.left, + Rect.bottom - Rect.top, + ClientWnd, hInstance, 1234); + + Cinfo->hConsole = ChildWnd; + + // return if its not possible to create the child window + if(NULL == ChildWnd) + { + return 0; + } + + + Cinfo->SendHeader = TRUE; + Cinfo->Finished = TRUE; + Cinfo->CurrentColour = 1; + + + Cinfo->hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 2,2,500,300, ChildWnd, NULL, hInstance, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(Cinfo->hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(Cinfo->hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + Cinfo = &MonWindow; + +// MoveWindow(Cinfo->hConsole, Rect.left - (OffsetW /2), Rect.top - OffsetH, +// Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindows(Cinfo); + + if (Cinfo->Minimized) + ShowWindow(ChildWnd, SW_SHOWMINIMIZED); + else + ShowWindow(ChildWnd, SW_RESTORE); + + return TRUE; +} diff --git a/BPQWinAPP.c b/BPQWinAPP.c new file mode 100644 index 0000000..f262d08 --- /dev/null +++ b/BPQWinAPP.c @@ -0,0 +1,246 @@ + +// Version 1. 0. 2. 1 October 2010 + +// Add Delay on start option, and dynamically load bpq32 + +// Version 1. 0. 3. 1 October 2011 + +// Call CloseBPQ32 on exit + +// Version 2.0.1.1 July 2002 + +// Add try/except round main loop + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#include +#include +#include +#include +#include +#include "DbgHelp.h" + +//#define DYNLOADBPQ // Dynamically Load BPQ32.dll +#include "..\Include\bpq32.h" + +VOID APIENTRY SetFrameWnd(HWND hWnd); + +#define BPQICON 400 + +HINSTANCE hInst; +char AppName[] = "BPQ32"; +char Title[80] = "Program to hold BPQ32.dll in memory"; + + +// Foward declarations of functions included in this code module: + +ATOM MyRegisterClass(CONST WNDCLASS*); +BOOL InitApplication(HINSTANCE); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK FrameWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +HWND FrameWnd; +int TimerHandle = 0; + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[8192]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf_s(Mess, sizeof(Mess), format, arglist); + strcat_s(Mess, 999, "\r\n"); + OutputDebugString(Mess); + return; +} + +VOID WriteMiniDump() +{ +#ifdef WIN32 + + HANDLE hFile; + BOOL ret; + char FN[256]; + + sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + + + +// +// FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) +// +// PURPOSE: Entry point for the application. +// +// COMMENTS: +// +// This function initializes the application and processes the +// message loop. +// +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + BOOL bRet; + struct _EXCEPTION_POINTERS exinfo; + + Debugprintf("BPQ32.exe %s Entered", lpCmdLine); + + if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 5 Secs + Sleep(5000); + +// GetAPI(); + + if (!InitInstance(hInstance, nCmdShow)) + return (FALSE); + + // Main message loop: + + __try + { + + while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) + { + if (bRet == -1) + { + Debugprintf("GetMessage Returned -1 %d", GetLastError()); + break; + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + } + + #define EXCEPTMSG "BPQ32.exe Main Loop" + #include "StdExcept.c" + } + + Debugprintf("BPQ32.exe exiting %d", msg.message); + + KillTimer(NULL,TimerHandle); + + CloseBPQ32(); // Close Ext Drivers if last bpq32 process + + return (msg.wParam); +} + +// + + +// +// FUNCTION: InitInstance(HANDLE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + WNDCLASSEX wndclassMainFrame; + + hInst = hInstance; // Store instance handle in our global variable + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW; + 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 = AppName; + wndclassMainFrame.hIconSm = NULL; + + + if(!RegisterClassEx(&wndclassMainFrame)) + { + Debugprintf("BPQ32.exe RC failed %d", GetLastError()); + return 0; + } + + FrameWnd = CreateWindow(AppName, + "BPQ32", + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, // allows system choose an x position + CW_USEDEFAULT, // allows system choose a y position + CW_USEDEFAULT, // width, CW_USEDEFAULT allows system to choose height and width + CW_USEDEFAULT, // height, CW_USEDEFAULT ignores heights as this is set by setting + // CW_USEDEFAULT in width above. + HWND_MESSAGE, // Message only Window + NULL, // 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 + + + + TimerHandle=SetTimer(FrameWnd,WM_TIMER,5000,NULL); + + CheckTimer(); + + return (TRUE); + +} + +// +// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) +// +// PURPOSE: Processes messages for the main window. +// + + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + + case WM_TIMER: + + CheckTimer(); + return 0; + + case WM_CLOSE: + + PostQuitMessage(0); + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + + return (0); +} + + diff --git a/BPQWinAPP.rc b/BPQWinAPP.rc new file mode 100644 index 0000000..53dd522 --- /dev/null +++ b/BPQWinAPP.rc @@ -0,0 +1,71 @@ + +//Microsoft Developer Studio generated resource script. +// +//#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + + +#define BPQICON 400 + +BPQICON ICON DISCARDABLE "..\\bpqicon.ico" + +// +// Version +// +#define TEXTVER "2. 0. 1. 1\0" +#define BINVER 2, 0, 1, 1 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BINVER + PRODUCTVERSION BINVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "Program to hold BPQ32.dll in memory\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "bpq32\0" + VALUE "FileVersion", TEXTVER + VALUE "InternalName", "bpq32\0" + VALUE "LegalCopyright", "Copyright © 2006-2011 G8BPQ\0" + VALUE "OriginalFilename", "bpq32.exe\0" + VALUE "ProductName", " bpq32\0" + VALUE "ProductVersion", TEXTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + + +#endif // not APSTUDIO_INVOKED diff --git a/BPQWinAPP.vcproj b/BPQWinAPP.vcproj new file mode 100644 index 0000000..7aa07c4 --- /dev/null +++ b/BPQWinAPP.vcproj @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user b/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40182c4 --- /dev/null +++ b/BPQWinAPP.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQWinAPP.vcproj.SKIGACER.johnw.user b/BPQWinAPP.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..b5b0536 --- /dev/null +++ b/BPQWinAPP.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/BPQtoAGW.c b/BPQtoAGW.c new file mode 100644 index 0000000..6848ee3 --- /dev/null +++ b/BPQtoAGW.c @@ -0,0 +1,763 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use AGWPE as a Port Driver +// 32bit environment, +// +// Uses BPQ EXTERNAL interface +// + + +// Version 1.0 January 2005 - Initial Version +// + +// Version 1.1 August 2005 +// +// Treat NULL string in Registry as use current directory + +// Version 1.2 January 2006 + +// Support multiple commections (not quire yet!) +// Fix memory leak when AGEPE not running + + +// Version 1.3 March 2006 + +// Support multiple connections + +// Version 1.4 October 1006 + +// Write diagmnostics to BPQ console window instead of STDOUT + +// Version 1.5 February 2008 + +// Changes for dynamic unload of bpq32.dll + +// Version 1.5.1 September 2010 + +// Add option to get config from BPQ32.cfg + +#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 VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +//uintptr_t _beginthread(void( *start_address )( int ), unsigned stack_size, int arglist); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; + +void ConnecttoAGWThread(void * portptr); + +VOID __cdecl Consoleprintf(const char * format, ...); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static BOOL ReadConfigFile(int Port); +int ConnecttoAGW(); +int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port, BOOL CheckPort); + + +extern UCHAR BPQDirectory[]; + +#pragma pack(1) + +static struct AGWHEADER +{ + byte Port; + byte filler1[3]; + char DataKind; + byte filler2; + unsigned char PID; + byte filler3; + unsigned char callfrom[10]; + unsigned char callto[10]; + int DataLength; + int reserved; + +} AGWHeader; + +static struct AGWHEADER RXHeader; + + +#pragma pack() + + +#define MAXBPQPORTS 32 +#define MAXAGWPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int AGWChannel[MAXBPQPORTS+1]; // BPQ Port to AGW Port +static int BPQPort[MAXAGWPORTS][MAXBPQPORTS+1]; // AGW Port and Connection to BPQ Port +static void * AGWtoBPQ_Q[MAXBPQPORTS+1]; // Frames for BPQ, indexed by BPQ Port +static void * BPQtoAGW_Q[MAXBPQPORTS+1]; // Frames for AGW. indexed by AGW port. Only used it TCP session is blocked + +// Each port may be on a different machine. We only open one connection to each AGW instance + +static SOCKET AGWSock[MAXBPQPORTS+1]; // Socket, indexed by BPQ Port + +BOOL Alerted[MAXBPQPORTS+1]; // Error msg sent + +static int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific AGW host + +static char * AGWSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static char * AGWHostName[MAXBPQPORTS+1]; // AGW Host - may be dotted decimal or DNS Name + + +static unsigned int AGWInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short AGWPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTED[MAXBPQPORTS+1]; +static BOOL CONNECTING[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + + +static size_t ExtProc(int fn, int port, PMESSAGE buff) +{ + int i,winerr; + int datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes,txlen=0; + char ErrMsg[255]; + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + SOCKET sock = AGWSock[port]; + + // Only on first port using a host + + if (CONNECTED[port] == FALSE && CONNECTING[port] == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] > 9 ) + { + ConnecttoAGW(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (CONNECTED[port]) FD_SET(sock,&readfs); + + FD_ZERO(&writefs); + + if (BPQtoAGW_Q[port]) FD_SET(sock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (CONNECTED[port]) FD_SET(sock,&errorfs); + + if (select((int)sock+1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(sock, &readfs)) + { + + // data available + + ProcessReceivedData(port); + + } + + if (FD_ISSET(sock, &writefs)) + { + if (BPQtoAGW_Q[port] == 0) + { + } + else + { + // Write block has cleared. Send rest of packet + + buffptr = Q_REM(&BPQtoAGW_Q[port]); + + txlen = buffptr->Len; + + memcpy(txbuff, buffptr->Data, txlen); + + bytes = send(AGWSock[port], (const char FAR *)&txbuff, txlen, 0); + + ReleaseBuffer(buffptr); + } + } + + if (FD_ISSET(sock, &errorfs)) + { + sprintf(ErrMsg, "AGW Connection lost for BPQ Port %d\n", port); + Alerted[port] = FALSE; + WritetoConsole(ErrMsg); + + CONNECTED[port]=FALSE; + } + } + } + + // See if any frames for this port + + if (AGWtoBPQ_Q[port] !=0) + { + buffptr=Q_REM(&AGWtoBPQ_Q[port]); + + datalen = buffptr->Len - 1; + + memcpy(buff->DEST, &buffptr->Data[1] , datalen); // Data goes to +7, but we have an extra byte + datalen += MSGHDDRLEN; + buff->LENGTH = datalen; + + ReleaseBuffer(buffptr); + + return (1); + + } + + return (0); + + + + case 2: // send + + + if (!CONNECTED[MasterPort[port]]) return 0; // Don't try if not connected + + if (BPQtoAGW_Q[MasterPort[port]]) return 0; // Socket is blocked - just drop packets + // till it clears + + // AGW has a control byte on front, so only subtract 6 from BPQ length + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff); + txlen -= (MSGHDDRLEN - 1); + + AGWHeader.Port=AGWChannel[port]; + AGWHeader.DataKind='K'; // raw send + +#ifdef __BIG_ENDIAN__ + AGWHeader.DataLength = reverse(txlen); +#else + AGWHeader.DataLength = txlen; +#endif + memcpy(&txbuff,&AGWHeader,sizeof(AGWHeader)); + memcpy(&txbuff[sizeof(AGWHeader) + 1], &buff->DEST, txlen); + txbuff[sizeof(AGWHeader)]=0; + + txlen+=sizeof(AGWHeader); + + bytes=send(AGWSock[MasterPort[port]],(const char FAR *)&txbuff, txlen, 0); + + if (bytes != txlen) + { + + // AGW doesn't seem to recover from a blocked write. For now just reset + + winerr = WSAGetLastError(); + + i = sprintf(ErrMsg, "AGW Write Failed for port %d - error code = %d\n", port, winerr); + WritetoConsole(ErrMsg); + + closesocket(AGWSock[MasterPort[port]]); + CONNECTED[MasterPort[port]] = FALSE; + return (0); + } + return (0); + + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + break; + + case 4: // reinit + +// return(ReadConfigFile("BPQAXIP.CFG")); + + return (0); + } + return 0; +} + +void * AGWExtInit(struct PORTCONTROL * PortEntry) + +{ + int i, port; + char Msg[255]; + + // + // Will be called once for each AGW port to be mapped to a BPQ Port + // The AGW port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port=PortEntry->PORTNUMBER; + + ReadConfigFile(port); + + AGWChannel[port]=PortEntry->CHANNELNUM-65; + + if (destaddr[port].sin_family == 0) + { + // not defined in config file + + destaddr[port].sin_family = AF_INET; + destaddr[port].sin_port = htons(PortEntry->IOBASE); + + AGWHostName[port]=malloc(10); + + if (AGWHostName[port] != NULL) + strcpy(AGWHostName[port],"127.0.0.1"); + + } + + i=sprintf(Msg,"AGW Port %d Host %s %d\n",AGWChannel[port]+1,AGWHostName[port],htons(destaddr[port].sin_port)); + WritetoConsole(Msg); + + // See if we already have a port for this host + + + MasterPort[port]=port; + + for (i=1;iCHANNELNUM-65][MasterPort[port]]=PortEntry->PORTNUMBER; + + if (MasterPort[port] == port) + ConnecttoAGW(port); + + time(&lasttime[port]); // Get initial time value + + + return ExtProc; + +} + +/* + +# Config file for BPQtoAGW +# +# For each AGW port defined in BPQCFG.TXT, Add a line here +# Format is BPQ Port, Host/IP Address, Port + +# +# Any unspecified Ports will use 127.0.0.1 and port for BPQCFG.TXT IOADDR field +# + +1 127.0.0.1 8000 +2 127.0.0.1 8001 + +*/ + + +BOOL ReadConfigFile(int Port) +{ + char buf[256],errbuf[256]; + char * Config; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + char * ptr1 = Config, * ptr2; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf, Port, FALSE)) + { + WritetoConsole("BPQtoAGW - Bad config record "); + Consoleprintf(errbuf); + } + } + return (TRUE); + } + + return (TRUE); +} + +static int ProcessLine(char * buf, int Port, BOOL CheckPort) +{ + char * ptr,* p_user,* p_password; + char * p_ipad; + char * p_udpport; + unsigned short AGWport; + int BPQport; + int len=510; + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (CheckPort) + { + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + BPQport = atoi(ptr); + + if (Port != BPQport) return TRUE; // Not for us + } + else + { + BPQport = Port; + p_ipad = ptr; + } + if(BPQport > 0 && BPQport <33) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + AGWport = atoi(p_udpport); + + destaddr[BPQport].sin_family = AF_INET; + destaddr[BPQport].sin_port = htons(AGWport); + + AGWHostName[BPQport]=malloc(strlen(p_ipad)+1); + + if (AGWHostName[BPQport] == NULL) return TRUE; + + strcpy(AGWHostName[BPQport],p_ipad); + + p_user = strtok(NULL, " \t\n\r"); + + if (p_user == NULL) return (TRUE); + + p_password = strtok(NULL, " \t\n\r"); + + if (p_password == NULL) return (TRUE); + + // Allocate buffer for signon message + + AGWSignon[BPQport]=malloc(546); + + if (AGWSignon[BPQport] == NULL) return TRUE; + + memset(AGWSignon[BPQport],0,546); + + AGWSignon[BPQport][4]='P'; + + memcpy(&AGWSignon[BPQport][28],&len,4); + + strcpy(&AGWSignon[BPQport][36],p_user); + + strcpy(&AGWSignon[BPQport][291],p_password); + + return (TRUE); + } + + // + // Bad line + // + return (FALSE); + +} + +int ConnecttoAGW(int port) +{ + _beginthread(ConnecttoAGWThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoAGWThread(void * portptr) +{ + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + + // Only called for the first BPQ port for a particular host/port combination + + destaddr[port].sin_addr.s_addr = inet_addr(AGWHostName[port]); + + if (destaddr[port].sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (AGWHostName[port]); + + if (!HostEnt) return; // Resolve failed + + memcpy(&destaddr[port].sin_addr.s_addr,HostEnt->h_addr,4); + + } + + AGWSock[port]=socket(AF_INET,SOCK_STREAM,0); + + if (AGWSock[port] == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for AGW socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + setsockopt (AGWSock[port],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(AGWSock[port], (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for AGW socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(AGWSock[port]); + return; + } + + CONNECTING[port] = TRUE; + + if (connect(AGWSock[port],(LPSOCKADDR) &destaddr[port],sizeof(destaddr[port])) == 0) + { + // + // Connected successful + // + + CONNECTED[port] = TRUE; + CONNECTING[port] = FALSE; + + ioctlsocket (AGWSock[port],FIONBIO,¶m); + + // If required, send signon + + if (AGWSignon[port]) + send(AGWSock[port],AGWSignon[port],546,0); + + // Request Raw Frames + + AGWHeader.Port=0; + AGWHeader.DataKind='k'; + AGWHeader.DataLength=0; + + send(AGWSock[port],(const char FAR *)&AGWHeader,sizeof(AGWHeader),0); + + return; + } + else + { + err=WSAGetLastError(); + + // + // Connect failed + // + + if (Alerted[port] == FALSE) + { + sprintf(Msg, "Connect Failed for AGW Port %d - error code = %d\n", port, err); + WritetoConsole(Msg); + Alerted[port] = TRUE; + } + + closesocket(AGWSock[port]); + CONNECTING[port] = FALSE; + + return; + } +} + +int ProcessReceivedData(int port) +{ + unsigned int bytes; + int datalen,i; + char ErrMsg[255]; + char Message[500]; + PMSGWITHLEN buffptr; + + // Need to extract messages from byte stream + + // Use MSG_PEEK to ensure whole message is available + + bytes = recv(AGWSock[port],(char *)&RXHeader,sizeof(RXHeader),MSG_PEEK); + + if (bytes == SOCKET_ERROR) + { + i=sprintf(ErrMsg, "Read Failed for AGW socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(ErrMsg); + + closesocket(AGWSock[port]); + + CONNECTED[port]=FALSE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "AGW Connection closed for BPQ Port %d\r\n", port); + WritetoConsole(ErrMsg); + + + CONNECTED[port]=FALSE; + return (0); + } + + // Have some data + + if (bytes == sizeof(RXHeader)) + { + // Have a header - see if we have any associated data + + datalen=RXHeader.DataLength; + + #ifdef __BIG_ENDIAN__ + datalen = reverse(datalen); + #endif + + if (datalen > 0) + { + // Need data - See if enough there + + bytes = recv(AGWSock[port],(char *)&Message,sizeof(RXHeader)+datalen,MSG_PEEK); + } + + if (bytes == sizeof(RXHeader)+datalen) + { + bytes = recv(AGWSock[port],(char *)&RXHeader,sizeof(RXHeader),0); + + if (datalen > 0) + { + bytes = recv(AGWSock[port],(char *)&Message,datalen,0); + } + + // Have header, and data if needed + + // Only use frame type K + + if (RXHeader.DataKind == 'K') // raw data + { + // Make sure it is for a port we want - we may not be using all AGW ports + + if (BPQPort[RXHeader.Port][MasterPort[port]] == 0) + return (0); + + // Get a buffer + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = datalen; + memcpy(buffptr->Data, &Message, datalen); + + C_Q_ADD(&AGWtoBPQ_Q[BPQPort[RXHeader.Port][MasterPort[port]]], buffptr); + } + + return (0); + } + + // Have header, but not sufficient data + + return (0); + } + + // Dont have at least header bytes + + return (0); +} diff --git a/Bpq32.c b/Bpq32.c new file mode 100644 index 0000000..3e5b2af --- /dev/null +++ b/Bpq32.c @@ -0,0 +1,6557 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ +// +// 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 ?? + +// 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 anf TUNE commands (18) + +#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); + +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 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]; + +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); + + +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(); +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; + +void GetSemaphore(struct SEM * Semaphore, int ID); +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; + +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; + +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; +ULONG 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", + Semaphore.SemProcessID, SemHeldByAPI); + + // 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(40); // 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(40); + 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(); + + 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(); + + 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"); + } + 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(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=0xffffffff; + +// 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 == 1) + 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); + } + + 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; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[100]; + char Addr1[100]; + char Addr2[100]; + char City[100]; + char State[100]; + char Country[100]; + char PostCode[100]; + char Email[100]; + char Website[100]; + char Phone[100]; + char Data[100]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\"", + + WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len = sprintf(RegLine, "%s00,", RegLine); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(40); // 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; +} + +// 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/BpqTermMDI.h b/BpqTermMDI.h new file mode 100644 index 0000000..b92785f --- /dev/null +++ b/BpqTermMDI.h @@ -0,0 +1,146 @@ + +#define MAXSTACK 20 +#define INPUTLEN 512 + +#define MAXLINES 1000 +#define LINELEN 200 + + +#define BPQICON 2 +#define IDR_MENU1 101 +#define BPQMENU 101 +#define BPQCONNECT 102 +#define BPQDISCONNECT 103 +#define IDD_FONT 105 + +#define ID_WARNWRAP 415 +#define ID_WRAP 416 +#define ID_FLASHONBELL 417 + +#define IDC_FONTWIDTH 1008 +#define IDC_FONTNAME 1009 +#define IDC_CODEPAGE 1010 +#define IDC_CHARSET 1011 +#define IDC_FONTSIZE 1012 +#define BPQMTX 1164 +#define BPQMCOM 1165 +#define BPQCOPYMON 1166 +#define BPQCOPYOUT 1167 +#define BPQCLEARMON 1168 +#define BPQCLEAROUT 1169 +#define BPQBELLS 1170 +#define BPQCHAT 1171 +#define BPQHELP 1172 +#define BPQStripLF 1173 +#define BPQLogOutput 1174 +#define BPQLogMonitor 1175 +#define BPQSendDisconnected 1176 +#define BPQMNODES 1177 +#define MONCOLOUR 1178 +#define CHATTERM 1179 +#define IDM_CLOSEWINDOW 1180 +#define MONITORAPRS 1181 +#define MON_UI_ONLY 40006 +#define StopALLMon 40007 + +#define IDR_MAINFRAME_MENU 191 +#define TERM_MENU 192 +#define MON_MENU 193 +#define IDI_SIGMA_MAIN_ICON 104 +#define IDI_SYSTEM_INFO 106 + +#define RTFCOPY 30000 +#define ID_INFORMATION_SYSTEMINFORMATION 30001 +#define ID_HELP_ABOUT 30002 +#define ID_WINDOWS_CASCADE 30003 +#define ID_FILE_EXIT 30004 +#define ID_WINDOWS_TILE 30005 +#define ID_NEWWINDOW 30006 +#define ID_WINDOWS_RESTORE 30007 +#define ID_SETUP_FONT 30008 +#define ID_ACTION_RESETWINDOWSPLIT 30009 + +#define BPQBASE 40100 + +#define IDM_FIRSTCHILD 50000 // used in structure when creating mdi client area for the main frame + +// Port monitoring flags use BPQBASE -> BPQBASE+100 +struct ConsoleInfo +{ + struct ConsoleInfo * next; + int BPQStream; + BOOL Active; + int Incoming; + WNDPROC wpOrigInputProc; + HWND hConsole; + HWND hwndInput; + HWND hwndOutput; + HMENU hMenu; // handle of menu + RECT ConsoleRect; + RECT OutputRect; + int CharWidth; + + int Height, Width, Top, Left; + + int ClientHeight, ClientWidth; + char kbbuf[INPUTLEN]; + int kbptr; + + int readbufflen; // Current Length + char * readbuff; // Malloc'ed + char * KbdStack[MAXSTACK]; + + int StackIndex; + +// BOOL Bells; +// BOOL FlashOnBell; // Flash instead of Beep + BOOL StripLF; + +// BOOL WarnWrap; +// BOOL FlashOnConnect; +// BOOL WrapInput; +// BOOL CloseWindowOnBye; + + unsigned int WrapLen; + int WarnLen; + int maxlinelen; + + int PartLinePtr; + int PartLineIndex; // Listbox index of (last) incomplete line + + DWORD dwCharX; // average width of characters + DWORD dwCharY; // height of characters + DWORD dwClientX; // width of client area + DWORD dwClientY; // height of client area + DWORD dwLineLen; // line length + int nCaretPosX; // horizontal position of caret + int nCaretPosY; // vertical position of caret + + COLORREF FGColour; // Text Colour + COLORREF BGColour; // Background Colour + COLORREF DefaultColour; // Default Text Colour + + int CurrentLine; // Line we are writing to in circular buffer. + + int Index; + BOOL SendHeader; + BOOL Finished; + + char OutputScreen[MAXLINES][LINELEN]; + + int Colourvalue[MAXLINES]; + int LineLen[MAXLINES]; + + int CurrentColour; + int Thumb; + int FirstTime; + BOOL Scrolled; // Set if scrolled back + int RTFHeight; // Height of RTF control in pixels + + BOOL CONNECTED; + int SlowTimer; + BOOL Minimized; + BOOL NeedRefresh; + +}; + diff --git a/CBPQ32.vcproj b/CBPQ32.vcproj new file mode 100644 index 0000000..ce65cb9 --- /dev/null +++ b/CBPQ32.vcproj @@ -0,0 +1,597 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user b/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40b9267 --- /dev/null +++ b/CBPQ32.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/CBPQ32.vcproj.HPLAPTOP.johnw.user b/CBPQ32.vcproj.HPLAPTOP.johnw.user new file mode 100644 index 0000000..58f2ea6 --- /dev/null +++ b/CBPQ32.vcproj.HPLAPTOP.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/CBPQ32.vcproj.SKIGACER.johnw.user b/CBPQ32.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..6a3e804 --- /dev/null +++ b/CBPQ32.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/CBPQ32.vcxproj b/CBPQ32.vcxproj new file mode 100644 index 0000000..fbdf53d --- /dev/null +++ b/CBPQ32.vcxproj @@ -0,0 +1,232 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8EFA1E59-8654-4A23-8102-AA77A074D57C} + CBPQ32 + Win32Proj + 10.0.17763.0 + + + + DynamicLibrary + v141 + NotSet + false + + + DynamicLibrary + v141 + MultiByte + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28127.55 + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ + false + + + + 3 + ..\CInclude + true + + + Disabled + ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + All + c:\devprogs\bpq32\listings\debug\ + true + Level3 + EditAndContinue + + + /section:_BPQDATA,srw %(AdditionalOptions) + WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;Iphlpapi.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + c:\DevProgs\BPQ32\bpq32.dll + false + LIBCMTD.lib;%(IgnoreSpecificDefaultLibraries) + ..\CommonSource\bpq32.def + true + true + c:\DevProgs\BPQ32\bpqdev.map + true + Windows + 8000000 + 4000000 + false + + 0x42000000 + ..\lib\bpq32.lib + MachineX86 + false + + + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc + + + "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll + + + + + 3 + $(IntDir)$(ProjectName) + ..\CInclude + true + true + true + true + + + /D "MDIKERNEL" %(AdditionalOptions) + Disabled + false + ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + All + c:\devprogs\bpq32\listings\ + Level3 + ProgramDatabase + + + /section:_BPQDATA,srw %(AdditionalOptions) + WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + C:\DevProgs\BPQ32\bpq32.dll + ..\CommonSource\bpq32.def + true + c:\DevProgs\BPQ32\bpq32.pdb + true + c:\DevProgs\BPQ32\bpqpdn.map + true + Windows + true + true + + 0x42000000 + C:\Dev\Msdev2005\Projects\BPQ32\lib\bpq32.lib + MachineX86 + + + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc + + + "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CBPQ32.vcxproj.filters b/CBPQ32.vcxproj.filters new file mode 100644 index 0000000..7c08db6 --- /dev/null +++ b/CBPQ32.vcxproj.filters @@ -0,0 +1,228 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/CHeaders.h b/CHeaders.h new file mode 100644 index 0000000..3885192 --- /dev/null +++ b/CHeaders.h @@ -0,0 +1,409 @@ +// +// Prototypes for BPQ32 Node Functions +// + +#define DllImport + +#define EXCLUDEBITS + +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#include "compatbits.h" + +#include "asmstrucs.h" + +#ifndef WIN32 +__asm__(".symver fcntl,fcntl@GLIBC_2.4"); +#endif + +BOOL CheckExcludeList(UCHAR * Call); + +Dll int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +Dll BOOL ConvToAX25(unsigned char * callsign, unsigned char * ax25call); +DllExport BOOL ConvToAX25Ex(unsigned char * callsign, unsigned char * ax25call); +int WritetoConsoleLocal(char * buff); +VOID Consoleprintf(const char * format, ...); +VOID FreeConfig(); +int GetListeningPortsPID(int Port); + +void * InitializeExtDriver(PEXTPORTDATA PORTVEC); + +VOID PutLengthinBuffer(PDATAMESSAGE buff, USHORT datalen); // Needed for arm5 portability +int GetLengthfromBuffer(PDATAMESSAGE buff); + + +#define GetBuff() _GetBuff(__FILE__, __LINE__) +#define ReleaseBuffer(s) _ReleaseBuffer(s, __FILE__, __LINE__) +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) +#define Q_REM_NP(s) _Q_REM_NP(s, __FILE__, __LINE__) + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__) + +void _CheckGuardZone(char * File, int Line); + +VOID * _Q_REM(VOID **Q, char * File, int Line); +VOID * _Q_REM_NP(VOID *Q, char * File, int Line); + +int _C_Q_ADD(VOID *Q, VOID *BUFF, char * File, int Line); + +UINT _ReleaseBuffer(VOID *BUFF, char * File, int Line); + +VOID * _GetBuff(char * File, int Line); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +int C_Q_COUNT(VOID *Q); + +DllExport char * APIENTRY GetApplCall(int Appl); +DllExport char * APIENTRY GetApplAlias(int Appl); +DllExport int APIENTRY FindFreeStream(); +DllExport int APIENTRY DeallocateStream(int stream); +DllExport int APIENTRY SessionState(int stream, int * state, int * change); +DllExport int APIENTRY SetAppl(int stream, int flags, int mask); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY GetConnectionInfo(int stream, char * callsign, + int * port, int * sesstype, int * paclen, + int * maxframe, int * l4window); + + +struct config_setting_t; + +int GetIntValue(struct config_setting_t * group, char * name); +BOOL GetStringValue(struct config_setting_t * group, char * name, char * value); +VOID SaveIntValue(struct config_setting_t * group, char * name, int value); +VOID SaveStringValue(struct config_setting_t * group, char * name, char * value); + +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +Dll VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset); +Dll BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase); + +DllExport int APIENTRY TXCount(int stream); +DllExport int APIENTRY RXCount(int stream); +DllExport int APIENTRY MONCount(int stream); + +VOID ReadNodes(); +int BPQTRACE(MESSAGE * Msg, BOOL APRS); + +VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer); + +VOID PostStateChange(TRANSPORTENTRY * Session); + +VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer); +VOID DoTheCommand(TRANSPORTENTRY * Session); +char * MOVEANDCHECK(TRANSPORTENTRY * Session, char * Bufferptr, char * Source, int Len); +VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer); +char * FormatUptime(int Uptime); +char * strlop(char * buf, char delim); +BOOL CompareCalls(UCHAR * c1, UCHAR * c2); + +VOID PostDataAvailable(TRANSPORTENTRY * Session); +int WritetoConsoleLocal(char * buff); +char * CHECKBUFFER(TRANSPORTENTRY * Session, char * Bufferptr); +VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session); + +VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len); + +struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum); + +int cCOUNT_AT_L2(struct _LINKTABLE * LINK); +VOID SENDL4CONNECT(TRANSPORTENTRY * Session); + +VOID CloseSessionPartner(TRANSPORTENTRY * Session); +int COUNTNODES(); +int DecodeNodeName(char * NodeName, char * ptr);; +VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer); +int cCOUNT_AT_L2(struct _LINKTABLE * LINK); +void * zalloc(int len); +BOOL FindDestination(UCHAR * Call, struct DEST_LIST ** REQDEST); + +BOOL ProcessConfig(); + +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID CLEAROUTLINK(struct _LINKTABLE * LINK); +VOID TellINP3LinkGone(struct ROUTE * Route); +VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason); + +// Reason Equates + +#define NORMALCLOSE 0 +#define RETRIEDOUT 1 +#define SETUPFAILED 2 +#define LINKLOST 3 +#define LINKSTUCK 4 + +int COUNT_AT_L2(struct _LINKTABLE * LINK); +VOID SENDIDMSG(); +VOID SENDBTMSG(); +VOID INP3TIMER(); +VOID REMOVENODE(dest_list * DEST); +BOOL ACTIVATE_DEST(struct DEST_LIST * DEST); +VOID TellINP3LinkSetupFailed(struct ROUTE * Route); +BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE); +VOID PROCROUTES(struct DEST_LIST * DEST, struct ROUTE * ROUTE, int Qual); +BOOL L2SETUPCROSSLINK(PROUTE ROUTE); +VOID REMOVENODE(dest_list * DEST); +char * SetupNodeHeader(struct DATAMESSAGE * Buffer); +VOID L4CONNECTFAILED(TRANSPORTENTRY * L4); +int CountFramesQueuedOnSession(TRANSPORTENTRY * Session); +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session); +VOID __cdecl Debugprintf(const char * format, ...); + +int APIENTRY Restart(); +int APIENTRY Reboot(); +int APIENTRY Reconfig(); +Dll int APIENTRY SaveNodes (); + +struct SEM; + +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); + +void MySetWindowText(HWND hWnd, char * Msg); + +Dll int APIENTRY SessionControl(int stream, int command, int Mask); + +HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits); +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength); +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite); +VOID CloseCOMPort(HANDLE fd); + +VOID initUTF8(); +int Is8Bit(unsigned char *cpt, int len); +int WebIsUTF8(unsigned char *ptr, int len); +int IsUTF8(unsigned char *ptr, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int TrytoGuessCode(unsigned char * Char, int Len); + + +#define CMD_TO_APPL 1 // PASS COMMAND TO APPLICATION +#define MSG_TO_USER 2 // SEND 'CONNECTED' TO USER +#define MSG_TO_APPL 4 // SEND 'CONECTED' TO APPL +#define CHECK_FOR_ESC 8 // Look for ^d (^D) to disconnect session) + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +// V2.2 Types + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + +#define SUPPORT2point2 1 + +// XID Optional Functions + +#define OPMustHave 0x02A080 // Sync TEST 16 bit FCS Extended Address +#define OPSREJ 4 +#define OPSREJMult 0x200000 +#define OPREJ 2 +#define OPMod8 0x400 +#define OPMod128 0x800 + +#define BPQHOSTSTREAMS 64 + +extern TRANSPORTENTRY * L4TABLE; +extern unsigned char NEXTID; +extern int MAXCIRCUITS; +extern int L4DEFAULTWINDOW; +extern int L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; +extern int NEEDMH; +extern int RFOnly; + +extern char SESSIONHDDR[]; + +extern UCHAR NEXTID; + +extern struct ROUTE * NEIGHBOURS; +extern int MAXNEIGHBOURS; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern struct DEST_LIST * ENDDESTLIST; +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + + + +extern char MYCALL[]; // DB 7 DUP (0) ; NODE CALLSIGN (BIT SHIFTED) +extern char MYALIASTEXT[]; // {" " ; NODE ALIAS (KEEP TOGETHER) + +extern UCHAR MYCALLWITHALIAS[13]; +extern APPLCALLS APPLCALLTABLE[NumberofAppls]; + +extern UCHAR MYNODECALL[]; // NODE CALLSIGN (ASCII) +extern UCHAR MYNETROMCALL[]; // NETROM CALLSIGN (ASCII) + +extern UCHAR NETROMCALL[]; // NETORM CALL (AX25) + +extern VOID * FREE_Q; + +extern struct PORTCONTROL * PORTTABLE; +extern int NUMBEROFPORTS; + + +extern int OBSINIT; // INITIAL OBSOLESCENCE VALUE +extern int OBSMIN; // MINIMUM TO BROADCAST +extern int L3INTERVAL; // "NODES" INTERVAL IN MINS +extern int IDINTERVAL; // "ID" BROADCAST INTERVAL +extern int BTINTERVAL; // "BT" BROADCAST INTERVAL +extern int MINQUAL; // MIN QUALITY FOR AUTOUPDATES +extern int HIDENODES; // N * COMMAND SWITCH +extern int BBSQUAL; // QUALITY OF BBS RELATIVE TO NODE + +extern int NUMBEROFBUFFERS; // PACKET BUFFERS +extern int PACLEN; //MAX PACKET SIZE + +// L2 SYSTEM TIMER RUNS AT 3 HZ + +extern int T3; // LINK VALIDATION TIMER (3 MINS) (+ a bit to reduce RR collisions) + +extern int L2KILLTIME; // IDLE LINK TIMER (16 MINS) +extern int L3LIVES; // MAX L3 HOPS +extern int L4N2; // LEVEL 4 RETRY COUNT +extern int L4LIMIT; // IDLE SESSION LIMIT - 15 MINS +extern int L4DELAY; // L4 DELAYED ACK TIMER + +extern int BBS; // INCLUDE BBS SUPPORT +extern int NODE; // INCLUDE SWITCH SUPPORT + +extern int FULL_CTEXT; // CTEXT ON ALL CONNECTS IF NZ + + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC DUMMY; // Needed to force correct order of following + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +extern int NODEORDER; +extern UCHAR LINKEDFLAG; + +extern UCHAR UNPROTOCALL[80]; + + +extern char * INFOMSG; + +extern char * CTEXTMSG; +extern int CTEXTLEN; + +extern UCHAR MYALIAS[7]; // ALIAS IN AX25 FORM +extern UCHAR BBSALIAS[7]; + +extern VOID * TRACE_Q; // TRANSMITTED FRAMES TO BE TRACED + +extern char HEADERCHAR; // CHAR FOR _NODE HEADER MSGS + +extern int AUTOSAVE; // AUTO SAVE NODES ON EXIT FLAG +extern int L4APPL; // Application for BBSCALL/ALIAS connects +extern int CFLAG; // C =HOST Command + +extern VOID * IDMSG_Q; // ID/BEACONS WAITING TO BE SENT + +extern struct DATAMESSAGE BTHDDR; +extern struct _MESSAGE IDHDDR; + +extern VOID * IDMSG; + +extern int L3TIMER; // TIMER FOR 'NODES' MESSAGE +extern int IDTIMER; // TIMER FOR ID MESSAGE +extern int BTTIMER; // TIMER FOR BT MESSAGE + +extern int STATSTIME; + + +extern BOOL IPRequired; +extern int MaxHops; +extern int MAXRTT; +extern USHORT CWTABLE[]; +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR ROUTEQUAL; +extern UINT BPQMsg; +extern UCHAR ExcludeList[]; + + +extern APPLCALLS APPLCALLTABLE[]; + +extern char VersionStringWithBuild[]; +extern char VersionString[]; + +extern int MAXHEARDENTRIES; +extern int MHLEN; + +extern int APPL1; +extern int PASSCMD; +extern int NUMBEROFCOMMANDS; + +extern char * ConfigBuffer; + +extern char * WL2KReportLine[]; + +extern CMDX COMMANDS[]; + +extern int QCOUNT, MAXBUFFS, MAXCIRCUITS, L4DEFAULTWINDOW, L4T1, CMDXLEN; +extern char CMDALIAS[ALIASLEN][NumberofAppls]; + +extern int SEMGETS; +extern int SEMRELEASES; +extern int SEMCLASHES; +extern int MINBUFFCOUNT; + +extern UCHAR BPQDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +extern char WINMOR[]; +extern char PACTORCALL[]; + +extern UCHAR MCOM; +extern UCHAR MUIONLY; +extern UCHAR MTX; +extern unsigned long long MMASK; + +extern UCHAR NODECALL[]; // NODES in ax.25 + +extern int L4CONNECTSOUT; +extern int L4CONNECTSIN; +extern int L4FRAMESTX; +extern int L4FRAMESRX; +extern int L4FRAMESRETRIED; +extern int OLDFRAMES; +extern int L3FRAMES; + +extern char * PortConfig[]; +extern struct SEM Semaphore; +extern UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +extern int REALTIMETICKS; + +extern time_t CurrentSecs; +extern time_t lastSlowSecs; + + +// SNMP Variables + +extern int InOctets[32]; +extern int OutOctets[32]; + +extern BOOL CloseAllNeeded; +extern int CloseOnError; diff --git a/CMSAuth.c b/CMSAuth.c new file mode 100644 index 0000000..6d1ac04 --- /dev/null +++ b/CMSAuth.c @@ -0,0 +1,73 @@ +/* +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 WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#ifdef LINBPQ + +#include "compatbits.h" + +#define APIENTRY +#define VOID void + +#else +#include +#endif + +char * strlop(char * buf, char delim); + +VOID APIENTRY md5 (char *arg, unsigned char * checksum); + +// Implementation of the WinLink password challenge/response protocol + +unsigned char seed [] = {77, 197, 101, 206, 190, 249, + 93, 200, 51, 243, 93, 237, + 71, 94, 239, 138, 68, 108, + 70, 185, 225, 137, 217, 16, + 51, 122, 193, 48, 194, 195, + 198, 175, 172, 169, 70, 84, + 61, 62, 104, 186, 114, 52, + 61, 168, 66, 129, 192, 208, + 187, 249, 232, 193, 41, 113, + 41, 45, 240, 16, 29, 228, + 208, 228, 61, 20, 0}; + +/* + Calculate the challenge password response as follows: + - Concatenate the challenge phrase, password, and supplied secret value (i.e. the salt) + - Generate an MD5 hash of the result + - Convert the first 4 bytes of the hash to an integer (big endian) and return it +*/ + +int GetCMSHash(char * Challenge, char * Password) +{ + unsigned char Hash[16]; + unsigned char Phrase[256]; + + strlop(Challenge, 13); + strlop(Password, 13); + + strcpy(Phrase, Challenge); + strcat(Phrase, Password); + strcat(Phrase, seed); + md5(Phrase, Hash); + + return ((Hash[3] & 0x3f) << 24) + (Hash[2] << 16) + (Hash[1] << 8) + Hash[0]; +} diff --git a/ChatDebug.c b/ChatDebug.c new file mode 100644 index 0000000..edafede --- /dev/null +++ b/ChatDebug.c @@ -0,0 +1,432 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Debug Window(s) Module + +#include "BPQChat.h" + +static char ClassName[]="BPQDEBUGWINDOW"; + + +static WNDPROC wpOrigInputProc; +static WNDPROC wpOrigOutputProc; + +HWND hDebug; +static HWND hwndInput; +static HWND hwndOutput; + +static HMENU hMenu; // handle of menu + +#define InputBoxHeight 25 + +RECT DebugRect; + + +int Height, Width, LastY; + +static char readbuff[1024]; + +static BOOL Bells = TRUE; +static BOOL StripLF = TRUE; +static BOOL MonBBS = TRUE; +static BOOL MonCHAT = TRUE; +static BOOL MonTCP = TRUE; + +static int PartLinePtr=0; +static int PartLineIndex=0; // Listbox index of (last) incomplete line + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY SplitProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static void MoveWindows(); + +#define BGCOLOUR RGB(236,233,216) + +extern char DebugSize[32]; + +BOOL CreateDebugWindow() +{ + WNDCLASS wc; + HBRUSH bgBrush; + char Text[80]; + + if (hDebug) + { + ShowWindow(hDebug, SW_SHOWNORMAL); + SetForegroundWindow(hDebug); + return FALSE; // Alreaqy open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MonWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hDebug=CreateDialog(hInst,ClassName,0,NULL); + + if (!hDebug) + return (FALSE); + + wsprintf(Text, "Chat %s Debug", Session); + SetWindowText(hDebug, Text); + + hMenu=GetMenu(hDebug); + + if (Bells & 1) + CheckMenuItem(hMenu,BPQBELLS, MF_CHECKED); + else + CheckMenuItem(hMenu,BPQBELLS, MF_UNCHECKED); + + if (StripLF & 1) + CheckMenuItem(hMenu,BPQStripLF, MF_CHECKED); + else + CheckMenuItem(hMenu,BPQStripLF, MF_UNCHECKED); + + CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + // Retrieve the handlse to the edit controls. + + hwndOutput = GetDlgItem(hDebug, 122); + + // Set our own WndProcs for the controls. + + wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); + + if (cfgMinToTray) + { + AddTrayMenuItem(hDebug, Text); + } + ShowWindow(hDebug, SW_SHOWNORMAL); + + if (DebugRect.right < 100 || DebugRect.bottom < 100) + { + GetWindowRect(hDebug, &DebugRect); + } + + MoveWindow(hDebug,DebugRect.left,DebugRect.top, DebugRect.right-DebugRect.left, DebugRect.bottom-DebugRect.top, TRUE); + + MoveWindows(); + + return TRUE; + +} + + +static void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hDebug, &rcMain); + GetClientRect(hDebug, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + + switch (message) { + + case WM_ACTIVATE: + + SetFocus(hwndInput); + break; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case MONBBS: + + ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); + break; + + case MONCHAT: + + ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); + break; + + case MONTCP: + + ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); + break; + + + case BPQCLEAROUT: + + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyToClipboard(hwndOutput); + break; + + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &DebugRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hDebug = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +VOID ClearDebugWindow() +{ + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); +} + +VOID WritetoDebugWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len ==0) + return; + + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + if (Bells) + { + do { + + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + Beep(440,250); + } + + } while (ptr2); + + } + +lineloop: + + if (PartLinePtr > 300) + PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return; + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + return; +} + +/*static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} +*/ +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; i" + "

BPQ32 Chat Server %s Access

" + "

Please enter Callsign and Password to access the Chat Server

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

"; + + +char ChatPage[] = "%s's Chat Server" + "

BPQ32 Chat Node %s

" + "

" + "" + "" + "" + "
StatusConfigurationNode Menu
"; + + + +static char LostSession[] = "" +"

" +"Sorry, Session had been lost

    " +"
"; + +char * ChatConfigTemplate = NULL; +char * ChatStatusTemplate = NULL; + +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 SendChatHeader(char * Reply, char * Key) +{ + return sprintf(Reply, ChatPage, OurNode, OurNode, Key, Key); +} + + +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen) +{ + char * Conxtext = 0, * NodeURL; + int ReplyLen; + char * Key; + char Appl = 'M'; + + NodeURL = strtok_s(URL, "?", &Conxtext); + Key = Session->Key; + + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/Chat/Header") == 0) + { + *RLen = SendChatHeader(Reply, Session->Key); + return; + } + + if (_stricmp(NodeURL, "/Chat/ChatConfig") == 0) + { + if (ChatConfigTemplate) + free(ChatConfigTemplate); + + ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt"); + + NodeURL[strlen(NodeURL)] = ' '; // Undo strtok + SaveChatInfo(Session, input, Reply, RLen, Key); + return ; + } + + if (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0) + { + ProcessChatDisUser(Session, input, Reply, RLen, Key); + return ; + } + + + // End of POST section + } + + if ((_stricmp(NodeURL, "/chat/Chat.html") == 0) || (_stricmp(NodeURL, "/chat/Header") == 0)) + { + *RLen = SendChatHeader(Reply, Session->Key); + return; + } + + if ((_stricmp(NodeURL, "/Chat/ChatStatus") == 0) || (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0)) + { + if (ChatStatusTemplate) + free(ChatStatusTemplate); + + ChatStatusTemplate = GetTemplateFromFile(1, "ChatStatus.txt"); + SendChatStatusPage(Reply, RLen, Key); + + return; + } + + if (_stricmp(NodeURL, "/Chat/ChatConf") == 0) + { + if (ChatConfigTemplate) + free(ChatConfigTemplate); + + ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt"); + + SendChatConfigPage(Reply, RLen, Key); + return; + } + + ReplyLen = sprintf(Reply, ChatSignon, OurNode, OurNode); + *RLen = ReplyLen; + +} + + +static 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); + } +} + +static VOID GetCheckBox(char * input, char * key, int * value) +{ + char * ptr = strstr(input, key); + if (ptr) + *value = 1; + else + *value = 0; +} + + +VOID SaveChatInfo(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = NULL; + char Temp[80]; + char Nodes[10000] = ""; + char * ptr1, * ptr2; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = SendChatHeader(Reply, Session->Key); + return; + } + + + GetParam(input, "ApplNum=", Temp); + ChatApplNum = atoi(Temp); + GetParam(input, "Streams=", Temp); + MaxChatStreams = atoi(Temp); + + GetParam(input, "nodes=", Nodes); + + ptr1 = Nodes; + ptr2 = OtherNodesList; + + // Now we just save with crlf in place + + strcpy(OtherNodesList, Nodes); + /* + while (*ptr1) + { + if ((*ptr1) == 13) + { + *(ptr2++) = ' '; + ptr1 += 2; + } + else + *(ptr2++) = *(ptr1++); + } + *ptr2 = 0; +*/ + GetParam(input, "Posn=", Position); + GetParam(input, "MapText=", PopupText); + GetParam(input, "welcome=", ChatWelcomeMsg); + + // Replace cr lf in string with $W + + ptr1 = ChatWelcomeMsg; + + scan2: + + ptr1 = strstr(ptr1, "\r\n"); + + if (ptr1) + { + *(ptr1++)='$'; // put in cr + *(ptr1++)='W'; // put in lf + + goto scan2; + } + + GetCheckBox(input, "PopType=Click", &PopupMode); + + if (strstr(input, "Restart=Restart+Links")) + { + char * ptr1, * ptr2, * Context; + + node_close(); + + Sleep(2); + + // Dont call removelinks - they may still be attached to a circuit. Just clear header + + link_hd = NULL; + + // Set up other nodes list. rtlink messes with the string so pass copy + + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); + + while (ptr1) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, "\r\n", &Context); + } + + free(ptr2); + + if (user_hd) // Any Users? + makelinks(); // Bring up links + } + + if (strstr(input, "UpdateMap=Update+Map")) + { + char Msg[500]; + int len; + + len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode); + + if (len < 256) + Send_MON_Datagram(Msg, len); + + } + SaveChatConfigFile(ChatConfigName); + GetChatConfig(ChatConfigName); + } + + SendChatConfigPage(Reply, RLen, Key); + return; +} + +VOID ProcessChatDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + char * input; + char * ptr; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + ptr = strstr(input, "Stream="); + if (ptr) + { + int Stream = atoi(ptr + 7); + SessionControl(Stream, 2, 0); + } + } + SendChatStatusPage(Reply, RLen, Rest); +} + +VOID SendChatConfigPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len; + char Nodes[10000]; + char Text[1000]; + char * ptr1, * ptr2; + + // Replace spaces in Node List with CR/LF + + ptr1 = OtherNodesList; + ptr2 = Nodes; + + if (strchr(OtherNodesList, 13)) // New format maybe with connect scripts + { + // OtherNodesList alresdy has crlf + + strcpy(Nodes, OtherNodesList); + } + else + { + while (*ptr1) + { + if ((*ptr1) == ' ') + { + *(ptr2++) = 13; + *(ptr2++) = 10; + ptr1++ ; + } + else + *(ptr2++) = *(ptr1++); + } + + *ptr2 = 0; + } + + // Replace " in Text with " + + ptr1 = PopupText; + ptr2 = Text; + + while (*ptr1) + { + if ((*ptr1) == '"') + { + *(ptr2++) = '&'; + *(ptr2++) = 'q'; + *(ptr2++) = 'u'; + *(ptr2++) = 'o'; + *(ptr2++) = 't'; + *(ptr2++) = ';'; + ptr1++ ; + } + else + *(ptr2++) = *(ptr1++); + } + + *ptr2 = 0; + + // Replace $W in Welcome Message with cr lf + + ptr2 = ptr1 = _strdup(ChatWelcomeMsg); + +scan: + + ptr1 = strstr(ptr1, "$W"); + + if (ptr1) + { + *(ptr1++)=13; // put in cr + *(ptr1++)=10; // put in lf + + goto scan; + } + + Len = sprintf(Reply, ChatConfigTemplate, + OurNode, Key, Key, Key, + ChatApplNum, MaxChatStreams, Nodes, Position, + (PopupMode) ? UNC : CHKD, + (PopupMode) ? CHKD : UNC, Text, ptr2); + + free(ptr2); + + *ReplyLen = Len; +} + +VOID SendChatStatusPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len = 0; + USER *user; + char * Alias; + char * Topic; + LINK *link; + + char Streams[8192]; + char Users[8192]; + char Links[8192]; + + ChatCIRCUIT * conn; + int i = 0, n; + + Users[0] = 0; + + for (user = user_hd; user; user = user->next) + { + if ((user->node == 0) || (user->node->alias == 0)) + Alias = "(Corrupt Alias)"; + else + Alias = user->node->alias; + + if ((user->topic == 0) || (user->topic->name == 0)) + Topic = "(Corrupt Topic)"; + else + Topic = user->topic->name; + + Len += sprintf(&Users[Len], "%s%s%s%s%d%s", + user->call, Alias, user->name, Topic, (int)(time(NULL) - user->lastrealmsgtime), user->qth); + + } + + Links[0] = 0; + + Len = 0; + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked ) + if (link->supportsPolls) + Len += sprintf(&Links[Len], "%sOpen   RTT %d", link->call, link->RTT); + else + Len += sprintf(&Links[Len], "%sOpen", link->call); + else if (link->flags & (p_linked | p_linkini)) + Len += sprintf(&Links[Len], "%sConnecting", link->call); + else if (link->flags & p_linkfailed) + Len += sprintf(&Links[Len], "%sConnect failed", link->call); + else + Len += sprintf(&Links[Len], "%sIdle", link->call); + } + + Len = 0; + Streams[0] = 0; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn=&ChatConnections[n]; + i = conn->BPQStream; + if (!conn->Active) + { + Len += sprintf(&Streams[Len], "Idle        ", i, i); + } + else + { + if (conn->Flags & CHATLINK) + { + if (conn->BPQStream > 64 || conn->u.link == 0) + Len += sprintf(&Streams[Len], "** Corrupt ChatLink **" + "        ", i, i); + else + Len += sprintf(&Streams[Len], "" + "%s%s%d%s%d", + i, i, "Chat Link", conn->u.link->alias, conn->BPQStream, + "", conn->OutputQueueLength - conn->OutputGetPointer); + } + else + if ((conn->Flags & CHATMODE) && conn->topic) + { + Len += sprintf(&Streams[Len], "%s%s%d%s%d", + i, i, conn->u.user->name, conn->u.user->call, conn->BPQStream, + conn->topic->topic->name, conn->OutputQueueLength - conn->OutputGetPointer); + } + else + { + if (conn->UserPointer == 0) + Len += sprintf(&Streams[Len], "Logging in"); + else + { + Len += sprintf(&Streams[Len], "%s%s%d%s%d", + i, i, conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, + "CHAT", conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + } + + Len = sprintf(Reply, ChatStatusTemplate, OurNode, OurNode, Key, Key, Key, Streams, Users, Links); + *ReplyLen = Len; +} + + +static struct HTTPConnectionInfo * AllocateSession(char Appl) +{ + int KeyVal; + struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo)); + + if (Session == NULL) + return NULL; + + KeyVal = (int)time(NULL); + + sprintf(Session->Key, "%c%012X", Appl, KeyVal); + + if (SessionList) + Session->Next = SessionList; + + SessionList = Session; + + return Session; +} + +static struct HTTPConnectionInfo * FindSession(char * Key) +{ + struct HTTPConnectionInfo * Session = SessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + return Session; + + Session = Session->Next; + } + + return NULL; +} +#ifdef WIN32 + +static char PipeFileName[] = "\\\\.\\pipe\\BPQChatWebPipe"; + +static DWORD WINAPI InstanceThread(LPVOID lpvParam) + +// This routine is a thread processing function to read from and reply to a client +// via the open pipe connection passed from the main loop. Note this allows +// the main loop to continue executing, potentially creating more threads of +// of this procedure to run concurrently, depending on the number of incoming +// client connections. +{ + DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; + BOOL fSuccess = FALSE; + HANDLE hPipe = NULL; + char Buffer[4096]; + char OutBuffer[100000]; + char * MsgPtr; + int InputLen = 0; + int OutputLen = 0; + struct HTTPConnectionInfo Session; + char URL[4096]; + char * Context, * Method; + int n; + + char * ptr; + +// Debugprintf("InstanceThread created, receiving and processing messages."); + +// The thread's parameter is a handle to a pipe object instance. + + hPipe = (HANDLE) lpvParam; + + // Read client requests from the pipe. This simplistic code only allows messages + // up to BUFSIZE characters in length. + + n = ReadFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL); + fSuccess = ReadFile(hPipe, Buffer, 4096, &InputLen, NULL); + + if (!fSuccess || InputLen == 0) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + Debugprintf("InstanceThread: client disconnected.", GetLastError()); + else + Debugprintf("InstanceThread ReadFile failed, GLE=%d.", GetLastError()); + } + else + { + Buffer[InputLen] = 0; + + MsgPtr = &Buffer[0]; + + strcpy(URL, MsgPtr); + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + Method = strtok_s(URL, " ", &Context); + + ProcessChatHTTPMessage(&Session, Method, Context, MsgPtr, OutBuffer, &OutputLen); + + WriteFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL); + WriteFile(hPipe, OutBuffer, OutputLen, &cbWritten, NULL); + + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + } + return 1; +} + +static DWORD WINAPI PipeThreadProc(LPVOID lpvParam) +{ + BOOL fConnected = FALSE; + DWORD dwThreadId = 0; + HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL; + +// The main loop creates an instance of the named pipe and +// then waits for a client to connect to it. When the client +// connects, a thread is created to handle communications +// with that client, and this loop is free to wait for the +// next client connect request. It is an infinite loop. + + for (;;) + { + hPipe = CreateNamedPipe( + PipeFileName, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_BYTE | // message type pipe + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + 4096, // output buffer size + 4096, // input buffer size + 0, // client time-out + NULL); // default security attribute + + if (hPipe == INVALID_HANDLE_VALUE) + { + Debugprintf("CreateNamedPipe failed, GLE=%d.\n", GetLastError()); + return -1; + } + + // Wait for the client to connect; if it succeeds, + // the function returns a nonzero value. If the function + // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. + + fConnected = ConnectNamedPipe(hPipe, NULL) ? + TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + if (fConnected) + { + // Create a thread for this client. + + hThread = CreateThread( + NULL, // no security attribute + 0, // default stack size + InstanceThread, // thread proc + (LPVOID) hPipe, // thread parameter + 0, // not suspended + &dwThreadId); // returns thread ID + + if (hThread == NULL) + { + Debugprintf("CreateThread failed, GLE=%d.\n", GetLastError()); + return -1; + } + else CloseHandle(hThread); + } + else + // The client could not connect, so close the pipe. + CloseHandle(hPipe); + } + + return 0; +} + +BOOL CreateChatPipeThread() +{ + DWORD ThreadId; + CreateThread(NULL, 0, PipeThreadProc, 0, 0, &ThreadId); + return TRUE; +} + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +static VOID FormatTime(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); + +} + +#endif + + + + diff --git a/ChatMonitor.c b/ChatMonitor.c new file mode 100644 index 0000000..e662bf8 --- /dev/null +++ b/ChatMonitor.c @@ -0,0 +1,455 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Monitor Window(s) Module + +#include "BPQChat.h" + +static char ClassName[]="BPQMONWINDOW"; + + +static WNDPROC wpOrigInputProc; +static WNDPROC wpOrigOutputProc; + +HWND hMonitor; + +static HWND hwndInput; +static HWND hwndOutput; + +static HMENU hMenu; // handle of menu + + +#define InputBoxHeight 25 +RECT MonitorRect; +RECT OutputRect; + +int Height, Width, LastY; + +static char kbbuf[160]; +static int kbptr=0; + +static char * readbuff; +static int readbufflen; + +static BOOL StripLF = TRUE; +static BOOL MonBBS = TRUE; +static BOOL MonCHAT = TRUE; +static BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + +static int PartLinePtr=0; +static int PartLineIndex=0; // Listbox index of (last) incomplete line + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static void MoveWindows(); + +#define BGCOLOUR RGB(236,233,216) + +extern char MonitorSize[32]; + +BOOL CreateMonitor() +{ + WNDCLASS wc; + HBRUSH bgBrush; + char Text[80]; + + if (hMonitor) + { + ShowWindow(hMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMonitor); + return FALSE; // Alreaqy open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MonWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hMonitor=CreateDialog(hInst,ClassName,0,NULL); + + if (!hMonitor) + return (FALSE); + + wsprintf(Text, "Chat %s Monitor", Session); + SetWindowText(hMonitor, Text); + + readbuff = zalloc(1000); + readbufflen = 1000; + + hMenu=GetMenu(hMonitor); + + CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + // Retrieve the handlse to the edit controls. + + hwndOutput = GetDlgItem(hMonitor, 121); + + // Set our own WndProcs for the controls. + + wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); + + if (cfgMinToTray) + { + AddTrayMenuItem(hMonitor, Text); + } + + ShowWindow(hMonitor, SW_SHOWNORMAL); + + if (MonitorRect.right < 100 || MonitorRect.bottom < 100) + { + GetWindowRect(hMonitor, &MonitorRect); + } + + MoveWindow(hMonitor,MonitorRect.left,MonitorRect.top, MonitorRect.right-MonitorRect.left, MonitorRect.bottom-MonitorRect.top, TRUE); + + MoveWindows(); + + return TRUE; + +} + + +static void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMonitor, &rcMain); + GetClientRect(hMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + + switch (message) { + + case WM_ACTIVATE: + + SetFocus(hwndInput); + break; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case MONBBS: + + ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); + break; + + case MONCHAT: + + ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); + break; + + case MONTCP: + + ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); + break; + + + case BPQCLEAROUT: + + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyToClipboard(hwndOutput); + break; + + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMonitor = NULL; + + free(readbuff); + readbufflen = 0; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_LBUTTONDBLCLK) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +int WritetoMonitorWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len+PartLinePtr > readbufflen) + { + readbufflen += len+PartLinePtr; + readbuff = realloc(readbuff, readbufflen); + } + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + *(ptr2)=32; + + } while (ptr2); + +lineloop: + +// if (PartLinePtr > 300) +// PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return (0); + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + + + return (0); +} + +static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; itm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +} + diff --git a/ChatMultiConsole.c b/ChatMultiConsole.c new file mode 100644 index 0000000..bd38475 --- /dev/null +++ b/ChatMultiConsole.c @@ -0,0 +1,1731 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Console Window Module + +#include "BPQChat.h" + +extern BOOL WINE; + +char ClassName[]="CONSOLEWINDOW"; + + +struct UserInfo * user; + +struct ConsoleInfo BBSConsole; +struct ConsoleInfo ChatConsole; +struct ConsoleInfo * ConsHeader[2] = {&BBSConsole, &ChatConsole}; + +struct ConsoleInfo * InitHeader; + +HWND hConsole; + +int AutoColours[20] = {0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89}; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(64,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) // 100 ? +}; + + +#define InputBoxHeight 25 +static LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +void MoveWindows(struct ConsoleInfo * Cinfo); +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo); +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, WCHAR * Line); +VOID DoRefresh(struct ConsoleInfo * Cinfo); + +void ChatFlush(ChatCIRCUIT * conn); +char * lookupuser(char * call); + +VOID SaveIntValue(char * Key, int Value); +VOID SaveStringValue(char * Key, char * Value); +int GetIntValue(char * Key, int Default); +VOID GetStringValue(char * Key, char * Value, int Len); +int ChatIsUTF8(unsigned char *ptr, int len); + + +#define BGCOLOUR RGB(236,233,216) + +extern int Bells, FlashOnBell, StripLF, WarnWrap, WrapInput, FlashOnConnect, CloseWindowOnBye; + +char Version[32]; +char ConsoleSize[32]; +char MonitorSize[32]; +char DebugSize[32]; +char WindowSize[32]; + +RECT ConsoleRect; + + +HMENU trayMenu = 0; + +BOOL CreateConsole(int Stream) +{ + WNDCLASS wc = {0}; + HBRUSH bgBrush; + HMENU hMenu; + char Size[80] = ""; + char RTFColours[3000]; + struct ConsoleInfo * Cinfo; + int i, n; + char Text[80]; + + if (Stream == -1) + Cinfo = &BBSConsole; + else + Cinfo = &ChatConsole; + + InitHeader = Cinfo; + + if (Cinfo->hConsole) + { + ShowWindow(Cinfo->hConsole, SW_SHOWNORMAL); + SetForegroundWindow(Cinfo->hConsole); + return FALSE; // Already open + } + + memset(Cinfo, 0, sizeof(struct ConsoleInfo)); + + if (BBSConsole.next == NULL) BBSConsole.next = &ChatConsole; + + Cinfo->BPQStream = Stream; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = ConsWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hConsole = CreateDialog(hInst,ClassName,0,NULL); + + if (!hConsole) + return (FALSE); + + wsprintf(Text, "Chat %s Console", Session); + SetWindowText(hConsole, Text); + + Cinfo->readbuff = zalloc(2000); + Cinfo->readbufflen = 1000; + + hMenu=GetMenu(hConsole); + Cinfo->hMenu = hMenu; + + CheckMenuItem(hMenu,BPQBELLS, (Bells) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQFLASHONBELL, (FlashOnBell) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQStripLF, (StripLF) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WARNINPUT, (WarnWrap) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WRAPTEXT, (WrapInput) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_Flash, (FlashOnConnect) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_CLOSEWINDOW, (CloseWindowOnBye) ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + if (trayMenu == 0) + { + trayMenu = CreatePopupMenu(); + AppendMenu(trayMenu,MF_STRING,40000,"Copy"); + } + + // Set up RTF Header, including Colours String; + + memcpy(RTFColours, "{\\colortbl ;", 12); + n = 12; + + for (i = 1; i < 100; i++) + { + COLORREF Colour = Colours[i]; + n += wsprintf(&RTFColours[n], "\\red%d\\green%d\\blue%d;", GetRValue(Colour), GetGValue(Colour),GetBValue(Colour)); + } + + RTFColours[n++] = '}'; + RTFColours[n] = 0; + + strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1 FixedSys;}}"); +// strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fcharset204\\fprq1 FixedSys;}}"); + strcat(RTFHeader, RTFColours); + strcat(RTFHeader, "\\viewkind4\\uc1\\pard\\f0"); + + sprintf(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1\\cpg%d\\fcharset%d %s;}}", 0, 0, "Courier New"); + sprintf(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fprq1;}}"); + strcat(RTFHeader, RTFColours); + strcat(RTFHeader, "\\viewkind4\\uc1\\pard\\f0\\fs20\\uc0"); + + + RTFHddrLen = strlen(RTFHeader); + + // Create a Rich Text Control + + Cinfo->SendHeader = TRUE; + Cinfo->Finished = TRUE; + Cinfo->CurrentColour = 1; + + LoadLibrary("riched20.dll"); + + Cinfo->hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 6,145,290,130, hConsole, NULL, hInst, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(Cinfo->hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(Cinfo->hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + Cinfo->hwndInput = GetDlgItem(hConsole, 118); + + // Set our own WndProcs for the controls. + + Cinfo->wpOrigInputProc = (WNDPROC) SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, (LONG) InputProc); + + if (cfgMinToTray) + { + char Text[80]; + wsprintf(Text, "Chat %s Console", Session); + AddTrayMenuItem(hConsole, Text); + } + + ShowWindow(hConsole, SW_SHOWNORMAL); + + if (ConsoleRect.right < 100 || ConsoleRect.bottom < 100) + { + GetWindowRect(hConsole, &ConsoleRect); + } + + MoveWindow(hConsole, ConsoleRect.left, ConsoleRect.top, + ConsoleRect.right-ConsoleRect.left, + ConsoleRect.bottom-ConsoleRect.top, TRUE); + + Cinfo->hConsole = hConsole; + + MoveWindows(Cinfo); + + Cinfo->Console = zalloc(sizeof(ChatCIRCUIT)); + + Cinfo->Console->Active = TRUE; + Cinfo->Console->BPQStream = Stream; + + strcpy(Cinfo->Console->Callsign, ChatSYSOPCall); + + user = zalloc(sizeof(struct UserInfo)); + + strcpy(user->Call, ChatSYSOPCall); + + Cinfo->Console->UserPointer = user; + Cinfo->Console->paclen=236; + Cinfo->Console->sysop = TRUE; + + if (user->Name[0] == 0) + { + char * Name = lookupuser(user->Call); + + if (Name) + { + if (strlen(Name) > 17) + Name[17] = 0; + + strcpy(user->Name, Name); + free(Name); + } + else + { + Cinfo->Console->Flags |= GETTINGUSER; + SendUnbuffered(-2, NewUserPrompt, strlen(NewUserPrompt)); + return TRUE; + } + } + + if (rtloginu (Cinfo->Console, TRUE)) + Cinfo->Console->Flags |= CHATMODE; + + return TRUE; +} + + +VOID CloseConsole(int Stream) +{ + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + CloseConsoleSupport(Cinfo); + return; + } + } + } +} + + + +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo) +{ + if (Cinfo->Console->Flags & CHATMODE) + { + __try + { + logout(Cinfo->Console); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + + Cinfo->Console->Flags = 0; + } + + + if (CloseWindowOnBye) + { +// PostMessage(hConsole, WM_DESTROY, 0, 0); + DestroyWindow(Cinfo->hConsole); + } +} + +void MoveWindows(struct ConsoleInfo * Cinfo) +{ + RECT rcClient; + int ClientWidth; + + GetClientRect(Cinfo->hConsole, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(Cinfo->hwndOutput,2, 2, ClientWidth-4, Cinfo->ClientHeight-InputBoxHeight-4, TRUE); + MoveWindow(Cinfo->hwndInput,2, Cinfo->ClientHeight-InputBoxHeight-2, ClientWidth-4, InputBoxHeight, TRUE); + + GetClientRect(Cinfo->hwndOutput, &rcClient); + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + Cinfo->WarnLen = ClientWidth/8 - 1; + Cinfo->WrapLen = Cinfo->WarnLen; + Cinfo->maxlinelen = Cinfo->WarnLen; + +} + + +INT_PTR CALLBACK ChatColourDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + + + switch (message) + { + int Colour; + USER *user; + char Call[100]; + int Sel; + char ColourString[100]; + + case WM_INITDIALOG: + + for (user = user_hd; user; user = user->next) + { + SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_ADDSTRING, 0, (LPARAM) user->call); + } + + for (Colour = 0; Colour < 100; Colour++) + { + SendDlgItemMessage(hDlg, IDC_CHATCOLOURS, CB_ADDSTRING, 0, (LPARAM) Colours [Colour]); + } + return TRUE; + + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + break; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + SetTextColor(lpdis->hDC, Colours[lpdis->itemID]); + +// SetBkColor(lpdis->hDC, 0); + + wsprintf(ColourString, "XXXXXX %06X", Colours[lpdis->itemID]); + + TextOut(lpdis->hDC, + 6, + y, + ColourString, + 13); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + + case WM_COMMAND: + + switch LOWORD(wParam) + { + case IDC_CHATCALLS: + + if (HIWORD(wParam) == CBN_SELCHANGE) + { + Sel = SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETCURSEL, 0, 0); + + SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&Call); + + user = user_find(Call, NULL); + + if (user) + SendDlgItemMessage(hDlg, IDC_CHATCOLOURS, CB_SETCURSEL, user->Colour - 10, 0); + } + + break; + + case IDOK: + + Sel = SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETCURSEL, 0, 0); + + SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&Call); + + Sel = SendDlgItemMessage(hDlg, IDC_CHATCOLOURS, CB_GETCURSEL, 0, 0); + + user = user_find(Call, NULL); + + if (user) + { + user->Colour = Sel + 10; + upduser(user); + } + break; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + + } + } + return FALSE; +} + + + +LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + int i; + struct ConsoleInfo * Cinfo; + UCHAR tchBuffer[100000]; + UCHAR * buf = tchBuffer; + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hConsole == hWnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + switch (message) { + + case WM_CTLCOLOREDIT: + + if (Cinfo->Scrolled) + { + HDC hdcStatic = (HDC)wParam; + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)GetStockObject(LTGRAY_BRUSH); + } + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + case WM_VSCROLL: + break; + + case WM_NOTIFY: + { + const MSGFILTER * pF = (MSGFILTER *)lParam; + POINT pos; + CHARRANGE Range; + + if(pF->nmhdr.hwndFrom == Cinfo->hwndOutput) + { + if(pF->msg == WM_VSCROLL) + { +// int Command = LOWORD(pF->wParam); +// int Pos = HIWORD(pF->wParam); + +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + DoRefresh(Cinfo); + break; + } + + if(pF->msg == WM_KEYUP) + { + if (pF->wParam == VK_PRIOR || pF->wParam == VK_NEXT) + { +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + DoRefresh(Cinfo); + } + } + + if(pF->msg == WM_RBUTTONDOWN) + { + // Only allow popup if something is selected + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + if (Range.cpMin == Range.cpMax) + return TRUE; + + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return TRUE; + } + } + break; + } + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + return TRUE; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + SendMessage(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + if ((Cinfo->BPQStream == -2) && (tchBuffer[0] == 0x1b)) + { + SetTextColor(lpdis->hDC, Colours[tchBuffer[1] - 10]); + buf += 2; + } +// SetBkColor(lpdis->hDC, 0); + + TextOut(lpdis->hDC, + 6, + y, + buf, + strlen(buf)); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + return TRUE; + + + case WM_ACTIVATE: + + SetFocus(Cinfo->hwndInput); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case 40000: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(Cinfo->hConsole)) + { + len = SendMessage(Cinfo->hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + SetFocus(Cinfo->hwndInput); + } + + + + + case BPQBELLS: + + ToggleParam(Cinfo->hMenu, hWnd, &Bells, BPQBELLS); + break; + + case BPQFLASHONBELL: + + ToggleParam(Cinfo->hMenu, hWnd, &FlashOnBell, BPQFLASHONBELL); + break; + + case BPQStripLF: + + ToggleParam(Cinfo->hMenu, hWnd, &StripLF, BPQStripLF); + break; + + case IDM_WARNINPUT: + + ToggleParam(Cinfo->hMenu, hWnd, &WarnWrap, IDM_WARNINPUT); + break; + + + case IDM_WRAPTEXT: + + ToggleParam(Cinfo->hMenu, hWnd, &WrapInput, IDM_WRAPTEXT); + break; + + case IDM_Flash: + + ToggleParam(Cinfo->hMenu, hWnd, &FlashOnConnect, IDM_Flash); + break; + + case IDM_CLOSEWINDOW: + + ToggleParam(Cinfo->hMenu, hWnd, &CloseWindowOnBye, IDM_CLOSEWINDOW); + break; + + case BPQCLEAROUT: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + break; + + + SendMessage(Cinfo->hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyRichTextToClipboard(Cinfo->hwndOutput); + break; + + + case IDM_EDITCHATCOLOURS: + DialogBox(hInst, MAKEINTRESOURCE(IDD_CHATCOLCONFIG), hWnd, ChatColourDialogProc); + break; + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Cinfo->Height = lprc->bottom-lprc->top; + Cinfo->Width = lprc->right-lprc->left; + + MoveWindows(Cinfo); + + return TRUE; + + case WM_SIZE: + + MoveWindows(Cinfo); + return TRUE; + + case WM_CLOSE: + + CloseConsoleSupport(Cinfo); + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &ConsoleRect); // For save soutine + + SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, + (LONG) Cinfo->wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + if (Cinfo->Console && Cinfo->Console->Active) + { + ChatClearQueue(Cinfo->Console); + + Cinfo->Console->Active = FALSE; + RefreshMainWindow(); + logout(Cinfo->Console); + } + + // Free Scrollback + + for (i = 0; i < MAXSTACK ; i++) + { + if (Cinfo->KbdStack[i]) + { + free(Cinfo->KbdStack[i]); + Cinfo->KbdStack[i] = NULL; + } + } + + Sleep(500); + + free(Cinfo->readbuff); + Cinfo->readbufflen = 0; + + free(Cinfo->Console); + Cinfo->Console = 0; + Cinfo->hConsole = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int i; + unsigned int TextLen; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hwndInput == hwnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + + if (uMsg == WM_KEYUP) + { + unsigned int i; +// Debugprintf("5%x", LOBYTE(HIWORD(lParam))); + + if (LOBYTE(HIWORD(lParam)) == 0x48 && wParam == 0x26) + { + // Scroll up + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput, WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + Cinfo->StackIndex++; + if (Cinfo->StackIndex == 20) + Cinfo->StackIndex = 19; + + return TRUE; + } + + if (LOBYTE(HIWORD(lParam)) == 0x50 && wParam == 0x28) + { + // Scroll up + + Cinfo->StackIndex--; + if (Cinfo->StackIndex < 0) + Cinfo->StackIndex = 0; + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + return TRUE; + } + } + + + if (uMsg == WM_CHAR) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (TextLen > INPUTLEN-10) Beep(220, 150); + + if(WarnWrap || WrapInput) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (WarnWrap) + if (TextLen == Cinfo->WarnLen) Beep(220, 150); + + if (WrapInput) + if ((wParam == 0x20) && (TextLen > Cinfo->WrapLen)) + wParam = 13; // Replace space with Enter + + } + + if (wParam == 13) + { + Cinfo->kbptr=SendMessage(Cinfo->hwndInput, WM_GETTEXT, INPUTLEN-1, + (LPARAM) (LPCSTR)Cinfo->kbbuf); + + Cinfo->StackIndex = 0; + + // Stack it + + if (Cinfo->KbdStack[19]) + free(Cinfo->KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + Cinfo->KbdStack[i+1] = Cinfo->KbdStack[i]; + } + + Cinfo->KbdStack[0] = _strdup(Cinfo->kbbuf); + + Cinfo->kbbuf[Cinfo->kbptr]=13; + + // Echo + + if (Cinfo->BPQStream == -2) + { + UCHAR Msg[INPUTLEN * 2]; // Should be plenty + Msg[0] = 0x1b; + Msg[1] = 11; + + memcpy(&Msg[2], Cinfo->kbbuf, Cinfo->kbptr+1); + WritetoConsoleWindow(Cinfo->BPQStream, Msg, Cinfo->kbptr+3); + } + else + WritetoConsoleWindow(Cinfo->BPQStream, Cinfo->kbbuf, Cinfo->kbptr+1); + + if (Cinfo->Scrolled) + { + POINT Point; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(Cinfo->hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Scrolled = FALSE; + } + + DoRefresh(Cinfo); + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + if (wParam == 0x1a) // Ctrl/Z + { + + Cinfo->kbbuf[0]=0x1a; + Cinfo->kbbuf[1]=13; + + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], 2); + + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + + } + + return CallWindowProc(Cinfo->wpOrigInputProc, hwnd, uMsg, wParam, lParam); +} + + + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, WCHAR * Msg, int len); + +int WritetoConsoleWindow(int Stream, UCHAR * Msg, int len) +{ + struct ConsoleInfo * Cinfo; + WCHAR BufferW[65536]; + + int wlen; + + if (ChatIsUTF8(Msg, len)) + wlen = MultiByteToWideChar(CP_UTF8, 0, Msg, len, BufferW, 65536); + else + wlen = MultiByteToWideChar(CP_ACP, 0, Msg, len, BufferW, 65536); + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + WritetoConsoleWindowSupport(Cinfo, BufferW, wlen); + DoRefresh(Cinfo); + return 0; + } + } + } + return 0; +} + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, WCHAR * Msg, int len) +{ + WCHAR * ptr1, * ptr2; + + if (len + Cinfo->PartLinePtr > Cinfo->readbufflen) + { + Cinfo->readbufflen += len + Cinfo->PartLinePtr; + Cinfo->readbuff = realloc(Cinfo->readbuff, Cinfo->readbufflen * 2); + } + + if (Cinfo->PartLinePtr != 0) + { + Cinfo->CurrentLine--; // Overwrite part line in buffer + if (Cinfo->CurrentLine < 0) + Cinfo->CurrentLine = MAXLINES - 1; + + if (Msg[0] == 0x1b && len > 1) + { + Msg += 2; // Remove Colour Escape + len -= 2; + } + } + + memcpy(&Cinfo->readbuff[Cinfo->PartLinePtr], Msg, len * 2); + + len=len+Cinfo->PartLinePtr; + + ptr1=&Cinfo->readbuff[0]; + Cinfo->readbuff[len]=0; + + if (Bells) + { + do { + + ptr2=memchr(ptr1,7 , len * 2); + + if (ptr2) + { + *(ptr2)=32; + + if (FlashOnBell) + FlashWindow(Cinfo->hConsole, TRUE); + else + Beep(440,250); + } + + } while (ptr2); + } + +lineloop: + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1, 13, len * 2); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + Cinfo->PartLinePtr = len; + memmove(Cinfo->readbuff, ptr1, len * 2); + AddLinetoWindow(Cinfo, ptr1); +// InvalidateRect(Cinfo->hwndOutput, NULL, FALSE); + + return (0); + } + + *(ptr2++)=0; + + // If len is greater that screen with, fold + + if ((ptr2 - ptr1) > Cinfo->maxlinelen) + { + WCHAR * ptr3; + WCHAR * saveptr1 = ptr1; + int linelen = ptr2 - ptr1; + int foldlen; + WCHAR save; + + foldloop: + + ptr3 = ptr1 + Cinfo->maxlinelen; + + while(*ptr3!= 0x20 && ptr3 > ptr1) + { + ptr3--; + } + + foldlen = ptr3 - ptr1 ; + + if (foldlen == 0) + { + // No space before, so split at width + + foldlen = Cinfo->maxlinelen; + ptr3 = ptr1 + Cinfo->maxlinelen; + + } + else + { + ptr3++ ; // Omit space + linelen--; + } + save = ptr1[foldlen]; + ptr1[foldlen] = 0; + AddLinetoWindow(Cinfo, ptr1); + ptr1[foldlen] = save; + linelen -= foldlen; + ptr1 = ptr3; + + if (linelen > Cinfo->maxlinelen) + goto foldloop; + + AddLinetoWindow(Cinfo, ptr1); + + ptr1 = saveptr1; + } + else + AddLinetoWindow(Cinfo, ptr1); + + Cinfo->PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + goto lineloop; + } + + + return (0); +} + +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +void CopyRichTextToClipboard(HWND hWnd) +{ + int len=0; + HGLOBAL hMem; + char * ptr; + + // Copy Rich Text to Clipboard + + len = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); + + hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(MainWnd)) + { + len = SendMessage(hWnd, WM_GETTEXT , len, (LPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); +} + + +void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; i 127 || val < -128 ) + ptr2 += sprintf(ptr2, "\\u%d ", val); + else + *(ptr2++) = val; + } + val = *ptr1++; + } + *ptr2 = 0; +} + + +DWORD CALLBACK EditStreamCallback(struct ConsoleInfo * Cinfo, LPBYTE lpBuff, LONG cb, PLONG pcb) +{ + int ReqLen = cb; + int i; + int Line; + char LineB[4096]; + +// if (cb != 4092) +// return 0; + + if (Cinfo->SendHeader) + { + // Return header + + memcpy(lpBuff, RTFHeader, RTFHddrLen); + *pcb = RTFHddrLen; + Cinfo->SendHeader = FALSE; + Cinfo->Finished = FALSE; + Cinfo->Index = 0; + return 0; + } + + if (Cinfo->Finished) + { + *pcb = 0; + return 0; + } + +/* + if (BufferLen > cb) + { + memcpy(lpBuff, &Buffer[Offset], cb); + BufferLen -= cb; + Offset += cb; + *pcb = cb; + return 0; + } + + memcpy(lpBuff, &Buffer[Offset], BufferLen); + + *pcb = BufferLen; +*/ + + // Return 10 line at a time + + for (i = 0; i < 10; i++); + { + Line = Cinfo->Index++ + Cinfo->CurrentLine - MAXLINES; + + if (Line <0) + Line = Line + MAXLINES; + + wcstoRTF(&LineB[0], Cinfo->OutputScreen[Line]); + + sprintf(lpBuff, "\\cf%d ", Cinfo->Colourvalue[Line]); + strcat(lpBuff, LineB); + strcat(lpBuff, "\\line"); + + if (Cinfo->Index == MAXLINES) + { + Cinfo->Finished = TRUE; + strcat(lpBuff, "}"); + i = 10; + } + } + *pcb = strlen(lpBuff); + return 0; +} + +VOID DoRefresh(struct ConsoleInfo * Cinfo) +{ + EDITSTREAM es = {0}; + int Min, Max, Pos; + POINT Point; + SCROLLINFO ScrollInfo; + int LoopTrap = 0; + HWND hwndOutput = Cinfo->hwndOutput; + + if(WINE) + Cinfo->Thumb = 30000; + else + Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + Pos = Cinfo->Thumb + Cinfo->ClientHeight; + + if ((Cinfo->Thumb + Cinfo->ClientHeight) > Cinfo->RTFHeight - 10) // Don't bother writing to screen if scrolled back + { + es.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback; + es.dwCookie = (DWORD_PTR)Cinfo; + Cinfo->SendHeader = TRUE; + SendMessage(hwndOutput, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); + ScrollInfo.cbSize = sizeof(ScrollInfo); + ScrollInfo.fMask = SIF_ALL; + + GetScrollInfo(hwndOutput, SB_VERT, &ScrollInfo); + +// Debugprintf("Pos %d Max %d Min %d nMax %d ClientH %d", Pos, Min, Max, ScrollInfo.nMax, Cinfo->ClientHeight); + + if (Cinfo->FirstTime == FALSE) + { + // RTF Controls don't immediately scroll to end - don't know why. + + Cinfo->FirstTime = TRUE; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + while (LoopTrap++ < 20) + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + Cinfo->RTFHeight = Max; + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + } + + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + + if (Cinfo->Thumb > (Point.y - 10)) // Don't Scroll if user has scrolled back + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + + if (Cinfo->Scrolled) + { + Cinfo->Scrolled = FALSE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } + return; + } + + if (!Cinfo->Scrolled) + { + Cinfo->Scrolled = TRUE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } +} + +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, WCHAR * Line) +{ + int Len = wcslen(Line); + WCHAR * ptr1 = Line; + WCHAR * ptr2; + int l, Index; + WCHAR LineCopy[LINELEN * 2]; + + if (Line[0] == 0x1b && Len > 1) + { + // Save Colour Char + + Cinfo->CurrentColour = Line[1] - 10; + ptr1 +=2; + Len -= 2; + } + + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], ptr1); + + // Look for chars we need to escape (\ { }) + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = wcschr(ptr1, L'\\'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ++ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l * 2); // Copy Including found char + Index += l; + LineCopy[Index++] = '\\'; + Len++; + ptr1 = ptr2; + ptr2 = wcschr(ptr1, '\\'); + } + wcscpy(&LineCopy[Index], ptr1); // Copy in rest + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = wcschr(ptr1, '{'); + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l * 2); + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '{'; + Len++; + ptr1 = ++ptr2; + ptr2 = wcschr(ptr1, '{'); + } + wcscpy(&LineCopy[Index], ptr1); // Copy in rest + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = wcschr(ptr1, '}'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l * 2); // Copy + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '}'; + Len++; + ptr1 = ++ptr2; + ptr2 = wcschr(ptr1, '}'); + } + wcscpy(&LineCopy[Index], ptr1); // Copy in rest + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + + Cinfo->Colourvalue[Cinfo->CurrentLine] = Cinfo->CurrentColour; + Cinfo->LineLen[Cinfo->CurrentLine++] = Len; + if (Cinfo->CurrentLine >= MAXLINES) Cinfo->CurrentLine = 0; +} + + +/* +#define XBITMAP 80 +#define YBITMAP 20 + +#define BUFFER MAX_PATH + +HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork; +HBITMAP hbmpPicture, hbmpOld; + +void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp) +{ + int nItem; + + nItem = SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)lpstr); + SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)nItem, (LPARAM)hbmp); +} + +DWORD APIENTRY DlgDrawProc( + HWND hDlg, // window handle to dialog box + UINT message, // type of message + UINT wParam, // message-specific information + LONG lParam) +{ + int nItem; + TCHAR tchBuffer[BUFFER]; + HBITMAP hbmp; + HWND hListBox; + TEXTMETRIC tm; + int y; + HDC hdcMem; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + RECT rcBitmap; + HRESULT hr; + size_t * pcch; + + switch (message) + { + + case WM_INITDIALOG: + + // Load bitmaps. + + hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700)); + hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701)); + hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702)); + hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703)); + hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704)); + + // Retrieve list box handle. + + hListBox = GetDlgItem(hDlg, IDL_STUFF); + + // Initialize the list box text and associate a bitmap + // with each list box item. + + AddItem(hListBox, "pencil", hbmpPencil); + AddItem(hListBox, "crayon", hbmpCrayon); + AddItem(hListBox, "marker", hbmpMarker); + AddItem(hListBox, "pen", hbmpPen); + AddItem(hListBox, "fork", hbmpFork); + + SetFocus(hListBox); + SendMessage(hListBox, LB_SETCURSEL, 0, 0); + return TRUE; + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 20; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + break; + } + + // Draw the bitmap and text for the list box item. Draw a + // rectangle around the bitmap if it is selected. + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // Display the bitmap associated with the item. + + hbmpPicture =(HBITMAP)SendMessage(lpdis->hwndItem, + LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0); + + hdcMem = CreateCompatibleDC(lpdis->hDC); + hbmpOld = SelectObject(hdcMem, hbmpPicture); + + BitBlt(lpdis->hDC, + lpdis->rcItem.left, lpdis->rcItem.top, + lpdis->rcItem.right - lpdis->rcItem.left, + lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem, 0, 0, SRCCOPY); + + // Display the text associated with the item. + + SendMessage(lpdis->hwndItem, LB_GETTEXT, + lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - + tm.tmHeight) / 2; + + hr = StringCchLength(tchBuffer, BUFFER, pcch); + if (FAILED(hr)) + { + // TODO: Handle error. + } + + TextOut(lpdis->hDC, + XBITMAP + 6, + y, + tchBuffer, + pcch); + + SelectObject(hdcMem, hbmpOld); + DeleteDC(hdcMem); + + // Is the item selected? + + if (lpdis->itemState & ODS_SELECTED) + { + // Set RECT coordinates to surround only the + // bitmap. + + rcBitmap.left = lpdis->rcItem.left; + rcBitmap.top = lpdis->rcItem.top; + rcBitmap.right = lpdis->rcItem.left + XBITMAP; + rcBitmap.bottom = lpdis->rcItem.top + YBITMAP; + + // Draw a rectangle around bitmap to indicate + // the selection. + + DrawFocusRect(lpdis->hDC, &rcBitmap); + } + break; + + case ODA_FOCUS: + + // Do not process focus changes. The focus caret + // (outline rectangle) indicates the selection. + // The IDOK button indicates the final + // selection. + + break; + } + return TRUE; + + case WM_COMMAND: + + switch (LOWORD(wParam)) + { + case IDOK: + // Get the selected item's text. + + nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETCURSEL, 0, (LPARAM) 0); + hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETITEMDATA, nItem, 0); + + // If the item is not the correct answer, tell the + // user to try again. + // + // If the item is the correct answer, congratulate + // the user and destroy the dialog box. + + if (hbmp != hbmpFork) + { + MessageBox(hDlg, "Try again!", "Oops", MB_OK); + return FALSE; + } + else + { + MessageBox(hDlg, "You're right!", + "Congratulations.", MB_OK); + + // Fall through. + } + + case IDCANCEL: + + // Destroy the dialog box. + + EndDialog(hDlg, TRUE); + return TRUE; + + default: + + return FALSE; + } + + case WM_DESTROY: + + // Free any resources used by the bitmaps. + + DeleteObject(hbmpPencil); + DeleteObject(hbmpCrayon); + DeleteObject(hbmpMarker); + DeleteObject(hbmpPen); + DeleteObject(hbmpFork); + + return TRUE; + + default: + return FALSE; + + } + return FALSE; +} +*/ diff --git a/ChatUsers.txt b/ChatUsers.txt new file mode 100644 index 0000000..5f50364 --- /dev/null +++ b/ChatUsers.txt @@ -0,0 +1,16 @@ +IZ4FVW 0 Giulio Cesena ITA JN64ce, 1k2 bps radio link¬55¬0 +GM8BPQ 40 John ?_qth¬14¬0 +N5UXT 0 Angelo th New Orleans, La¬99¬0 +N0NJY 0 Rick Oak Island, NC¬76¬0 +KB8UVN 0 Matt Johnstown, Ohio¬95¬0 +WA3WLH 0 Rich East Greenville, PA¬55¬0 +EI2GYB 0 Steve_353 Donegal, Ireland (IO65JG)¬55¬0 +PE1RRR 0 Red Network Hub Monitor¬91¬0 +PE1NNZ 0 Guido nr Eindhoven¬91¬0 +G7TAJ 0 Steve_44 E.Sussex UK,JO00FU¬91¬0 +N3HYM 0 Ray Frederick, Md¬97¬0 +VE3CGH 0 Ted ?_qth¬71¬0 +WE1H 0 Matt Merrimack, NH¬26¬0 +W9IKU 0 Greg Portage, Wisconsin USA¬55¬0 +KA3VSP 0 Brian New Castle, DE¬91¬0 +G8BPQ 0 John Nottingham¬26¬0 diff --git a/ChatUtilities.c b/ChatUtilities.c new file mode 100644 index 0000000..56d43a9 --- /dev/null +++ b/ChatUtilities.c @@ -0,0 +1,363 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Utility Routines + + + +#include "BPQChat.h" + +#include "Winspool.h" + +int LogAge = 7; + +int SEMCLASHES = 0; + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf_s(Mess, sizeof(Mess), format, arglist); + ChatWriteLogLine(NULL, '!',Mess, Len, LOG_DEBUGx); +// #ifdef _DEBUG + strcat(Mess, "\r\n"); + OutputDebugString(Mess); +// #endif + return; +} + +VOID __cdecl Logprintf(int LogMode, ChatCIRCUIT * conn, int InOut, const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf_s(Mess, sizeof(Mess), format, arglist); + ChatWriteLogLine(conn, InOut, Mess, Len, LogMode); + + return; +} + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // + + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; +// Debugprintf("MailChat Semaphore Clash"); + } +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + + _asm{ + + mov eax,1 + mov ebx, Semaphore + xchg [ebx],eax // this instruction is locked + + cmp eax,0 + jne loop1 // someone else got it - try again +; +; ok, we've got the semaphore +; + } + + return; +} +void FreeSemaphore(struct SEM * Semaphore) +{ + Semaphore->Flag=0; + + return; +} + +VOID __cdecl nodeprintf(ChatCIRCUIT * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf_s(Mess, sizeof(Mess), format, arglist); + + ChatQueueMsg(conn, Mess, len); + + ChatWriteLogLine(conn, '>',Mess, len-1, LOG_CHAT); + + return; +} + + +// Costimised message handling routines. +/* + Variables - a subset of those used by FBB + + $C : Number of the next message. + $I : First name of the connected user. + $L : Number of the latest message. + $N : Number of active messages. + $U : Callsign of the connected user. + $W : Inserts a carriage return. + $Z : Last message read by the user (L command). + %X : Number of messages for the user. + %x : Number of new messages for the user. +*/ + +VOID ExpandAndSendMessage(ChatCIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + int len; + char Dollar[] = "$"; + char CR[] = "\r"; + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = ptr - OldP; // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + + case 'I': // First name of the connected user. + pptr = conn->UserPointer->Name; + break; + + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, strlen(NewMessage)); + + ChatWriteLogLine(conn, '>', NewMessage, len, LOG); + ChatQueueMsg(conn, NewMessage, len); +} + +BOOL isdigits(char * string) +{ + // Returns TRUE id sting is decimal digits + + int i, n = strlen(string); + + for (i = 0; i < n; i++) + { + if (isdigit(string[i]) == FALSE) return FALSE; + } + return TRUE; +} + +BOOL wildcardcompare(char * Target, char * Match) +{ + // Do a compare with string *string string* *string* + + // Strings should all be UC + + char Pattern[100]; + char * firststar; + + strcpy(Pattern, Match); + firststar = strchr(Pattern,'*'); + + if (firststar) + { + int Len = strlen(Pattern); + + if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end + { + Pattern[Len - 1] = 0; + return (BOOL)(strstr(Target, &Pattern[1])); + } + if (Pattern[0] == '*') // * at start + { + // Compare the last len - 1 chars of Target + + int Targlen = strlen(Target); + int Comparelen = Targlen - (Len - 1); + + if (Len == 1) // Just * + return TRUE; + + if (Comparelen < 0) // Too Short + return FALSE; + + return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); + } + + // Must be * at end - compare first Len-1 char + + return (memcmp(Target, Pattern, Len - 1) == 0); + + } + + // No WildCards - straight strcmp + return (strcmp(Target, Pattern) == 0); +} + +int DeleteLogFiles() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetLogDirectory()); + strcat(szDir, "\\logs\\Log_*.txt"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return dwError; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (now - ft.LowPart) / 86400; + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + dwError = GetLastError(); + + FindClose(hFind); + return dwError; +} + +int RemoveLF(char * Message, int len) +{ + // Remove lf chars + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (ptr2 - Message); +} + +char * ReadInfoFile(char * File) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1 = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", GetBPQDirectory(), File); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + +#ifndef WIN32 + + // Replace LF with CR + + // Remove lf chars + + ptr1 = MsgBytes; + + while (*ptr1) + { + if (*ptr1 == '\n') + *(ptr1) = '\r'; + + ptr1++; + } +#endif + + return MsgBytes; +} diff --git a/ChatUtils.c b/ChatUtils.c new file mode 100644 index 0000000..9e005e7 --- /dev/null +++ b/ChatUtils.c @@ -0,0 +1,830 @@ +// Chat Server for BPQ32 Packet Switch +// +// +// Based on MailChat Version 1.4.48.1 + + +#define _CRT_SECURE_NO_DEPRECATE + +#include "BPQChat.h" +#include + +#define MaxSockets 64 + +extern ChatCIRCUIT ChatConnections[MaxSockets+1]; +extern int NumberofChatStreams; + +extern char ChatConfigName[MAX_PATH]; +extern char Session[20]; + +extern struct SEM ChatSemaphore; +extern struct SEM AllocSemaphore; +extern struct SEM ConSemaphore; +extern struct SEM OutputSEM; + +extern char OtherNodesList[1000]; +extern int MaxChatStreams; + +extern char Position[81]; +extern char PopupText[260]; +extern int PopupMode; +extern int Bells, FlashOnBell, StripLF, WarnWrap, WrapInput, FlashOnConnect, CloseWindowOnBye; + +extern char Version[32]; +extern char ConsoleSize[32]; +extern char MonitorSize[32]; +extern char DebugSize[32]; +extern char WindowSize[32]; + +extern int RunningConnectScript; + +INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +int GetMultiLineDialog(HWND hDialog, int DLGItem); +BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len); + +int Connected(int Stream) +{ + int n; + ChatCIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + LINK *link; + KNOWNNODE *node; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + if (conn->rtcflags == p_linkini) + { + conn->paclen = 236; + + // Run first line of connect script + + ProcessChatConnectScript(conn, ConnectedMsg, 15); + return 0; + +// nprintf(conn, "c %s\r", conn->u.link->call); + } + return 0; + } + + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + conn->paclen = paclen; + + strlop(callsign, ' '); // Remove trailing spaces + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = zalloc(sizeof(struct UserInfo)); + + strcpy(user->Call, callsign); + + conn->UserPointer = user; + + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + conn->Flags |= CHATMODE; + + nodeprintf(conn, ChatSID, Ver[0], Ver[1], Ver[2], Ver[3]); + + // See if from a defined node + + for (link = link_hd; link; link = link->next) + { + if (matchi(conn->Callsign, link->call)) + { + conn->rtcflags = p_linkwait; + return 0; // Wait for *RTL + } + } + + // See if from a previously known node + + // I'm not sure this is safe. If it really is from a node the *RTL will be rejected + // Actually this protects against repeated attempts from a node that isn't configured. Maybe leave as is + + node = knownnode_find(conn->Callsign); + + if (node) + { + // A node is trying to link, but we don't have it defined - close + + Logprintf(LOG_CHAT, conn, '!', "Node %s connected, but is not defined as a Node - closing", + conn->Callsign); + + nodeprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + + return 0; + } + + if (user->Name[0] == 0) + { + char * Name = lookupuser(user->Call); + + if (Name) + { + if (strlen(Name) > 17) + Name[17] = 0; + + strcpy(user->Name, Name); + free(Name); + } + else + { + conn->Flags |= GETTINGUSER; + nputs(conn, NewUserPrompt); + return TRUE; + } + } + + SendWelcomeMsg(Stream, conn, user); + RefreshMainWindow(); + ChatFlush(conn); + + return 0; + } + } + + return 0; +} + +int Disconnected (int Stream) +{ + struct UserInfo * user = NULL; + ChatCIRCUIT * conn; + int n; + char Msg[255]; + int len; + struct _EXCEPTION_POINTERS exinfo; + + for (n = 0; n <= NumberofChatStreams-1; n++) + { + conn=&ChatConnections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active == FALSE) + { + return 0; + } + + ChatClearQueue(conn); + + conn->Active = FALSE; + + if (conn->Flags & CHATMODE) + { + if (conn->Flags & CHATLINK && conn->u.link) + { + // if running connect script, clear script active + + if (conn->u.link->flags & p_linkini) + { + RunningConnectScript = 0; + conn->u.link->scriptRunning = 0; + } + + len = sprintf_s(Msg, sizeof(Msg), "Chat Node %s Disconnected", conn->u.link->call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + __try {link_drop(conn);} My__except_Routine("link_drop"); + + } + else + { + len=sprintf_s(Msg, sizeof(Msg), "Chat User %s Disconnected", conn->Callsign); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + __try + { + logout(conn); + } + #define EXCEPTMSG "logout" + #include "StdExcept.c" + } + } + + conn->Flags = 0; + conn->u.link = NULL; + conn->UserPointer = NULL; + } + +// RefreshMainWindow(); + return 0; + } + } + return 0; +} + +int DoReceivedData(int Stream) +{ + int count, InputLen; + UINT MsgLen; + int n; + ChatCIRCUIT * conn; + struct UserInfo * user; + char * ptr, * ptr2; + char Buffer[10000]; + int Written; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (Stream == conn->BPQStream) + { + do + { + // May have several messages per packet, or message split over packets + + if (conn->InputLen + 1000 > 10000) // Shouldnt have lines longer than this in text mode + conn->InputLen = 0; // discard + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0) return 0; + + if (conn->DebugHandle) // Receiving a Compressed Message + WriteFile(conn->DebugHandle, &conn->InputBuffer[conn->InputLen], + InputLen, &Written, NULL); + + conn->Watchdog = 900; // 15 Minutes + + conn->InputLen += InputLen; + + { + + loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + + if (conn->u.user->circuit && conn->u.user->circuit->rtcflags & p_user) // Local User + conn->u.user->lastmsgtime = time(NULL); + + return 0; + } + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + + if (ptr) // CR in buffer + { + user = conn->UserPointer; + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single meg in buffer + + __try + { + if (conn->rtcflags == p_linkini) // Chat Connect + ProcessChatConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + conn->InputBuffer[conn->InputLen] = 0; + Debugprintf("CHAT *** Program Error Processing input %s ", conn->InputBuffer); + Disconnect(conn->BPQStream); + conn->InputLen=0; + CheckProgramErrors(); + return 0; + } + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (ptr2-ptr); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + __try + { + if (conn->rtcflags == p_linkini) + ProcessChatConnectScript(conn, Buffer, MsgLen); + else + ProcessLine(conn, user, Buffer, MsgLen); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + Buffer[MsgLen] = 0; + Debugprintf("CHAT *** Program Error Processing input %s ", Buffer); + Disconnect(conn->BPQStream); + conn->InputLen=0; + CheckProgramErrors(); + return 0; + } + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= MsgLen; + + goto loop; + + } + } + else + { + // no cr - testing.. + +// Debugprintf("Test"); + } + } + } while (count > 0); + + return 0; + } + } + + // Socket not found + + return 0; + +} + +int ConnectState(Stream) +{ + int state; + + SessionStateNoAck(Stream, &state); + return state; +} +UCHAR * EncodeCall(UCHAR * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; + +} + + +VOID SendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user) +{ + if (!rtloginu (conn, TRUE)) + { + // Already connected - close + + ChatFlush(conn); + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + +} + +VOID SendPrompt(ChatCIRCUIT * conn, struct UserInfo * user) +{ + nodeprintf(conn, "de %s>\r", OurNode); +} + +VOID ProcessLine(ChatCIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + char seps[] = " \t\r"; + struct _EXCEPTION_POINTERS exinfo; + + { + GetSemaphore(&ChatSemaphore, 0); + + __try + { + ProcessChatLine(conn, user, Buffer, len); + } + #define EXCEPTMSG "ProcessChatLine" + #include "StdExcept.c" + + FreeSemaphore(&ChatSemaphore); + + if (conn->BPQStream < 0) + CloseConsole(conn->BPQStream); + else + Disconnect(conn->BPQStream); + + return; + } + FreeSemaphore(&ChatSemaphore); + return; + } + + // Send if possible + + ChatFlush(conn); +} + + +VOID SendUnbuffered(int stream, char * msg, int len) +{ + if (stream < 0) + WritetoConsoleWindow(stream, msg, len); + else + SendMsg(stream, msg, len); +} + + +void TrytoSend() +{ + // call Flush on any connected streams with queued data + + ChatCIRCUIT * conn; + struct ConsoleInfo * Cons; + + int n; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (conn->Active == TRUE) + ChatFlush(conn); + } + + for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) + { + if (Cons->Console) + ChatFlush(Cons->Console); + } +} + + +void ChatFlush(ChatCIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent=0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 4) + return; // Busy + + if (tosend <= conn->paclen) + len=tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer+=len; + + FreeSemaphore(&OutputSEM); + + tosend-=len; + sent++; + + if (sent > 4) + return; + } + + // All Sent. Free buffers and reset pointers + + ChatClearQueue(conn); +} + +VOID ChatClearQueue(ChatCIRCUIT * conn) +{ + GetSemaphore(&OutputSEM, 0); + + conn->OutputGetPointer=0; + conn->OutputQueueLength=0; + + FreeSemaphore(&OutputSEM); +} + +/* +char * FormatDateAndTime(time_t Datim, BOOL DateOnly) +{ + struct tm *tm; + static char Date[]="xx-xxx hh:mmZ"; + + tm = gmtime(&Datim); + + if (tm) + sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", + tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); + + if (DateOnly) + { + Date[6]=0; + return Date; + } + + return Date; +} +*/ + + +VOID FreeList(char ** Hddr) +{ + VOID ** Save; + + if (Hddr) + { + Save = Hddr; + while(Hddr[0]) + { + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + +config_t cfg; +config_setting_t * group; + +extern char ChatWelcomeMsg[1000]; + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + +int GetIntValue(config_setting_t * group, char * name, int Default) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return Default; +} + + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +BOOL GetChatConfig(char * ConfigName) +{ + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "%d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return(EXIT_FAILURE); + } + + group = config_lookup (&cfg, "Chat"); + + if (group == NULL) + return EXIT_FAILURE; + + ChatApplNum = GetIntValue(group, "ApplNum", 0); + MaxChatStreams = GetIntValue(group, "MaxStreams", 0); + GetStringValue(group, "OtherChatNodes", OtherNodesList); + GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); + GetStringValue(group, "MapPosition", Position); + GetStringValue(group, "MapPopup", PopupText); + PopupMode = GetIntValue(group, "PopupMode", 0); + + Bells = GetIntValue(group, "Bells", 0); + FlashOnBell = GetIntValue(group, "FlashOnBell",0 ); + StripLF = GetIntValue(group, "StripLF", 0); + WarnWrap = GetIntValue(group, "WarnWrap", 0); + WrapInput = GetIntValue(group, "WrapInput",0 ); + FlashOnConnect = GetIntValue(group, "FlashOnConnect", 0); + CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye", 0); + + GetStringValue(group, "ConsoleSize", ConsoleSize); + GetStringValue(group, "MonitorSize", MonitorSize); + GetStringValue(group, "DebugSize", DebugSize); + GetStringValue(group, "WindowSize", WindowSize); + GetStringValue(group, "Version", Version); + + return EXIT_SUCCESS; +} + + + +VOID SaveChatConfigFile(char * File) +{ + config_setting_t *root, *group; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "Chat", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "ApplNum", ChatApplNum); + SaveIntValue(group, "MaxStreams", MaxChatStreams); + SaveStringValue(group, "OtherChatNodes", OtherNodesList); + SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); + + SaveStringValue(group, "MapPosition", Position); + SaveStringValue(group, "MapPopup", PopupText); + SaveIntValue(group, "PopupMode", PopupMode); + + SaveIntValue(group, "Bells", Bells); + SaveIntValue(group, "FlashOnBell", FlashOnBell); + SaveIntValue(group, "StripLF", StripLF); + SaveIntValue(group, "WarnWrap", WarnWrap); + SaveIntValue(group, "WrapInput", WrapInput); + SaveIntValue(group, "FlashOnConnect", FlashOnConnect); + SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); + + SaveStringValue(group, "ConsoleSize", ConsoleSize); + SaveStringValue(group, "MonitorSize", MonitorSize); + SaveStringValue(group, "DebugSize", DebugSize); + SaveStringValue(group, "WindowSize",WindowSize ); + SaveStringValue(group, "Version",Version ); + + if(! config_write_file(&cfg, File)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + config_destroy(&cfg); +} + + + +VOID SaveChatConfig(HWND hDlg) +{ + BOOL OK1; + HKEY hKey=0; + int OldChatAppl; + char * ptr1; + char * Save, * Context; + + OldChatAppl = ChatApplNum; + + ChatApplNum = GetDlgItemInt(hDlg, ID_CHATAPPL, &OK1, FALSE); + MaxChatStreams = GetDlgItemInt(hDlg, ID_STREAMS, &OK1, FALSE); + + if (ChatApplNum) + { + ptr1=GetApplCall(ChatApplNum); + + if (ptr1 && (*ptr1 < 0x21)) + { + MessageBox(NULL, "WARNING - There is no APPLCALL in BPQCFG matching the confgured ChatApplNum. Chat will not work", + "BPQMailChat", MB_ICONINFORMATION); + } + } + + GetMultiLineDialog(hDlg, IDC_ChatNodes); + + // Show dialog box now - gives time for links to close + + // reinitialise other nodes list. rtlink messes with the string so pass copy + + node_close(); + + if (ChatApplNum == OldChatAppl) + wsprintf(InfoBoxText, "Configuration Changes Saved and Applied"); + else + wsprintf(InfoBoxText, "Warning Program must be restarted to change Chat Appl Num"); + + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + Sleep(2); + + // Dont call removelinks - they may still be attached to a circuit. Just clear header + + link_hd = NULL; + + // Set up other nodes list. rtlink messes with the string so pass copy + + Save = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); + + while (ptr1 && ptr1[0]) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, "\r\n", &Context); + } + + +// if (strchr(ptr1, '|') == 0) // No script + +// while (*ptr1) +// { +// if (*ptr1 == '\r') +// { +// while (*(ptr1+2) == '\r') // Blank line +// ptr1+=2; + +// *++ptr1 = 32; +// } +// *ptr2++=*ptr1++; +// } + +// *ptr2++ = 0; + + + free(Save); + + if (user_hd) // Any Users? + makelinks(); // Bring up links + + SaveChatConfigFile(ChatConfigName); // Commit to file + GetChatConfig(ChatConfigName); + +} diff --git a/Cmd.c b/Cmd.c new file mode 100644 index 0000000..f4ea017 --- /dev/null +++ b/Cmd.c @@ -0,0 +1,5504 @@ +/* +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 +*/ + +// +// C replacement for cmd.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE +#pragma data_seg("_BPQDATA") + +//#include "windows.h" +//#include "winerror.h" + + +#include "time.h" +#include "stdio.h" +#include +//#include "vmm.h" +//#include "SHELLAPI.H" + +#include "CHeaders.h" +#include "bpqaprs.h" + +#pragma pack() + +#include "tncinfo.h" +#include "telnetserver.h" + +//#include "GetVersion.h" + +//#define DllImport __declspec( dllimport ) +//#define DllExport __declspec( dllexport ) + +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR *AXCalls); +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +int APIENTRY ClearNodes(); +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +VOID SendHTTPRequest(SOCKET sock, char * Host, int Port, char * Request, char * Params, int Len, char * Return); +SOCKET OpenWL2KHTTPSock(); +VOID FormatTime3(char * Time, time_t cTime); +VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6); +VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst); +VOID FindLostBuffers(); +BOOL CheckCMS(struct TNCINFO * TNC); +VOID L2SENDXID(struct _LINKTABLE * LINK); +int CountBits(unsigned long in); +VOID SaveMH(); +BOOL RestartTNC(struct TNCINFO * TNC); +void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID WriteMiniDump(); + +char COMMANDBUFFER[81] = ""; // Command Hander input buffer +char OrigCmdBuffer[81] = ""; // Command Hander input buffer + +struct DATAMESSAGE * REPLYBUFFER = NULL; +UINT APPLMASK = 0; +UCHAR SAVEDAPPLFLAGS = 0; + +UCHAR ALIASINVOKED = 0; + +extern struct TNCINFO * TNCInfo[41]; + +VOID * CMDPTR = 0; + +short CMDPACLEN = 0; + +char OKMSG[] = "Ok\r"; + +char CMDERRMSG[] = "Invalid command - Enter ? for command list\r"; +#define CMDERRLEN sizeof(CMDERRMSG) - 1 + +char PASSWORDMSG[] = "Command requires SYSOP status - enter password\r"; +#define LPASSMSG sizeof(PASSWORDMSG) - 1 + +char CMDLIST[] = "CONNECT BYE INFO NODES PORTS ROUTES USERS MHEARD"; + +#define CMDLISTLEN sizeof(CMDLIST) - 1 + +char BADMSG[] = "Bad Parameter\r"; +char BADPORT[] = "Invalid Port Number\r"; +char NOTEXTPORT[] = "Only valid on EXT ports\r"; +char NOVALCALLS[] = "No Valid Calls defined on this port\r"; + +char BADVALUEMSG[] = "Invalid parameter\r"; + +char BADCONFIGMSG[] = "Configuration File check falled - will continue with old config\r"; +#ifdef LINBPQ +char REBOOTOK[] = "Rebooting\r"; +#else +char REBOOTOK[] = "Rebooting in 20 secs\r"; +#endif +char REBOOTFAILED[] = "Shutdown failed\r"; + +char RESTARTOK[] = "Restarting\r"; +char RESTARTFAILED[] = "Restart failed\r"; + +UCHAR ARDOP[7] = {'A'+'A','R'+'R','D'+'D','O'+'O','P'+'P',' '+' '}; // ARDOP IN AX25 +UCHAR VARA[7] = {'V'+'V','A'+'A','R'+'R','A'+'A',' '+' ',' '+' '}; // VARA IN AX25 + +int STATSTIME = 0; +int MAXBUFFS = 0; +int QCOUNT = 0; +int MINBUFFCOUNT = 65535; +int NOBUFFCOUNT = 0; +int BUFFERWAITS = 0; +int MAXDESTS = 0; +int NUMBEROFNODES = 0; +int L4CONNECTSOUT = 0; +int L4CONNECTSIN = 0; +int L4FRAMESTX = 0; +int L4FRAMESRX = 0; +int L4FRAMESRETRIED = 0; +int OLDFRAMES = 0; +int L3FRAMES = 0; + +VOID SENDSABM(); +VOID RESET2(); + +int APPL1 = 0; +int PASSCMD = 0; + +#pragma pack(1) + +struct _EXTPORTDATA DP; // Only way I can think of to get offets to port data into cmd table + +char CMDALIAS[ALIASLEN][NumberofAppls] = {0}; +char * ALIASPTR = &CMDALIAS[0][0]; + +extern int RigReconfigFlag; + +CMDX COMMANDS[]; + +int CMDXLEN = sizeof (CMDX); + +VOID SENDNODESMSG(); +VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); +void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); +VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); + +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...) +{ + // Send Command response checking PACLEN + + char Mess[4096]; + va_list(arglist); + int OldLen; + int MsgLen; + struct DATAMESSAGE * Buffer; + char * Messptr = Mess; + int Paclen = Session->SESSPACLEN; + + if (Paclen == 0) + Paclen = 255; + + va_start(arglist, format); + + MsgLen = vsprintf(Mess, format, arglist); + + OldLen = (int)(Bufferptr - (char *)REPLYBUFFER->L2DATA); + + while ((OldLen + MsgLen) > Paclen) + { + // Have to send Paclen then get a new buffer + + int ThisBit = Paclen - OldLen; // What we can send this time + + if (ThisBit < 0) + ThisBit = 0; // How can this happen?? + + memcpy(Bufferptr, Messptr, ThisBit); + Messptr += ThisBit; + MsgLen -= ThisBit; + + // QUEUE IT AND GET ANOTHER BUFFER + + Buffer = (struct DATAMESSAGE *)GetBuff(); + + if (Buffer == NULL) + + // No buffers, so just reuse the old one (better than crashing !!) + + Buffer = REPLYBUFFER; + else + SendCommandReply(Session, REPLYBUFFER, Paclen + (4 + sizeof(void *))); + + + REPLYBUFFER = Buffer; + Buffer->PID = 0xf0; + + Bufferptr = &Buffer->L2DATA[0]; + OldLen = 0; + } + + // Add last bit to buffer + + memcpy(Bufferptr, Messptr, MsgLen); + + return Bufferptr + MsgLen; +} + + +VOID SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + SENDNODESMSG(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SAVEMHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + SaveMH(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SAVENODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + SaveNodes(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID DUMPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + WriteMiniDump(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID RIGRECONFIG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check falled - will continue with old config"); + } + else + { + RigReconfigFlag = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Rigcontrol Reconfig requested"); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID REBOOT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (Reboot()) + { + strcpy(Bufferptr, REBOOTOK); + Bufferptr += (int)strlen(REBOOTOK); + } + else + { + strcpy(Bufferptr, REBOOTFAILED); + Bufferptr += (int)strlen(REBOOTFAILED); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID RESTART(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (Restart()) + { + strcpy(Bufferptr, RESTARTOK); + Bufferptr += (int)strlen(RESTARTOK); + } + else + { + strcpy(Bufferptr, RESTARTFAILED); + Bufferptr += (int)strlen(RESTARTFAILED); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID RESTARTTNC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char * ptr, *Context; + int portno; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno && portno < 33) + { + struct TNCINFO * TNC = TNCInfo[portno]; + + if (TNC == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + } + else + { + if (TNC->ProgramPath) + { + if (RestartTNC(TNC)) + Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Ok\r", TNC->ProgramPath); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Failed\r", TNC->ProgramPath); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "PATH not defined so can't restart TNC\r"); + } + } + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +UCHAR VALNODESFLAG = 0, EXTONLY = 0; + +VOID PORTVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); + +VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + VALNODESFLAG = 1; + PORTVAL(Session, Bufferptr, CmdTail, CMD); +} + +VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + EXTONLY = 1; + PORTVAL(Session, Bufferptr, CmdTail, CMD); +} +VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS PORT VALUE COMMANDS + + char * ptr, *Context, * ptr1; + int portno; + UCHAR oldvalue, newvalue; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + UCHAR * valueptr; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + if (VALNODESFLAG) + { + char * VNPtr = PORT->PERMITTEDCALLS; + char Normcall[10]; + + VALNODESFLAG = 0; + + if (VNPtr) + { + while (VNPtr[0]) + { + Normcall[ConvFromAX25(VNPtr, Normcall)] = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); + VNPtr += 7; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", NOVALCALLS); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + + } + + if (EXTONLY) + { + // Make sure an Extenal Port + + EXTONLY = 0; + + if (PORT->PORTTYPE != 0x10) + { + strcpy(Bufferptr, NOTEXTPORT); + Bufferptr += (int)strlen(NOTEXTPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + valueptr = (UCHAR *)PORT + CMD->CMDFLAG; + oldvalue = *valueptr; + + // Display Param Namee + + ptr1 = &CMD->String[0]; + n = 12; + + while (*(ptr1) != ' ' && n--) + *(Bufferptr++) = *(ptr1++); + + // See if another param - if not, just display current value + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + *valueptr = newvalue; + + Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); + } + + else + Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + +VOID SWITCHVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Update switch 8 bit value + + char * ptr, *Context, * ptr1; + UCHAR oldvalue, newvalue; + int n; + UCHAR * valueptr; + + valueptr = (UCHAR *)CMD->CMDFLAG; + + oldvalue = *valueptr; + + // Display Param Name + + ptr1 = &CMD->String[0]; + n = 12; + + while (*(ptr1) != ' ' && n--) + *(Bufferptr++) = *(ptr1++); + + // See if a param - if not, just display current value + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + *valueptr = newvalue; + + Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); + + if (memcmp(CMD->String, "NODESINT ", 8) == 0) + L3TIMER = L3INTERVAL; + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + +VOID SWITCHVALW (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Update switch 16 bit value + + char * ptr, *Context, * ptr1; + USHORT oldvalue, newvalue; + int n; + USHORT * valueptr; + + valueptr = (USHORT *)CMD->CMDFLAG; + + oldvalue = (USHORT)*valueptr; + + // Display Param Name + + ptr1 = &CMD->String[0]; + n = 12; + + while (*(ptr1) != ' ' && n--) + *(Bufferptr++) = *(ptr1++); + + // See if a param - if not, just display current value + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + *valueptr = newvalue; + + Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + +TRANSPORTENTRY * SetupSessionFromSession(TRANSPORTENTRY * Session, PBPQVECSTRUC HOSTSESS, UINT APPLMASK) +{ + // Create a Transport (L4) session linked to an incoming Session + + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + UCHAR * ourcall = &MYCALL[0]; + + Session->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = Session; + + if (APPLMASK) + { + // Circuit for APPL - look for an APPLCALL + + APPLCALLS * APPL = APPLCALLTABLE; + + while ((APPLMASK & 1) == 0) + { + APPLMASK >>= 1; + APPL++; + } + if (APPL->APPLCALL[0] > 0x40) // We have an applcall + ourcall = &APPL->APPLCALL[0]; + } + + memcpy(NewSess->L4USER, ourcall, 7); + memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = Session->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + NewSess->SESSPACLEN = PACLEN; // Default; + + NewSess->L4TARGET.HOST = HOSTSESS; + NewSess->L4STATE = 5; + return NewSess; + } + Index++; + NewSess++; + } + return NULL; +} + +extern int GETCONNECTIONINFO(); + + +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions) +{ + PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; + TRANSPORTENTRY * NewSess; + int ApplNum; + int n = BPQHOSTSTREAMS; + int ConfigedPorts = 0; + + // LOOK FOR A FREE HOST SESSION + + while (n--) + { + if (HOSTSESS->HOSTAPPLMASK & Mask) + { + // Right appl + + ConfigedPorts++; + + if (HOSTSESS->HOSTSESSION == NULL && (HOSTSESS->HOSTFLAGS & 3) == 0) // Not attached and no report outstanding + { + // WEVE GOT A FREE BPQ HOST PORT - USE IT + + NewSess = SetupSessionFromSession(Session, HOSTSESS, Mask); + + if (NewSess == NULL) + return FALSE; // Appl not available + + HOSTSESS->HOSTSESSION = NewSess; + + // Convert APPLMASK to APPLNUM + + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + HOSTSESS->HOSTAPPLNUM = ApplNum; + + HOSTSESS->HOSTFLAGS |= 2; // Indicate State Change + + NewSess->L4CIRCUITTYPE = BPQHOST | DOWNLINK; + + PostStateChange(NewSess); + + NewSess->SESS_APPLFLAGS = HOSTSESS->HOSTAPPLFLAGS; + + NewSess->SESSPACLEN = Paclen; + + return TRUE; + } + } + HOSTSESS++; + } + + *AnySessions = ConfigedPorts; // to distinguish between none and all in use + return FALSE; +} + +VOID APPLCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + BOOL CONFAILED = 0; + UINT CONERROR ; + char APPName[13]; + char * ptr1, *ptr2; + int n = 12; + BOOL Stay = FALSE; + + // Copy Appl and Null Terminate + + ptr1 = &CMD->String[0]; + ptr2 = APPName; + + while (*(ptr1) != ' ' && n--) + *(ptr2++) = *(ptr1++); + + *(ptr2) = 0; + + if (Session->LISTEN) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Can't use %s while listening\r", APPName); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + if (CmdTail[0] == 'S') + Stay = TRUE; + + Session->STAYFLAG = Stay; + + memcpy(Session->APPL, CMD->String, 12); + + // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND + + if (ALIASPTR[0] > ' ') + { + // COPY ALIAS TO COMMAND BUFFER, THEN REENTER COMMAND HANDLER + + int SaveSecure = Session->Secure_Session; + + memcpy(COMMANDBUFFER, ALIASPTR, ALIASLEN); + _strupr(COMMANDBUFFER); + memcpy(OrigCmdBuffer, ALIASPTR, ALIASLEN); // In case original case version needed + + ALIASINVOKED = 1; // To prevent Alias Loops + + // Set secure session for application alias in case telnet outward connect + + Session->Secure_Session = 1; + DoTheCommand(Session); + Session->Secure_Session = SaveSecure; + + return; + } + + if (cATTACHTOBBS(Session, APPLMASK, CMDPACLEN, &CONERROR) == 0) + { + // No Streams + + if (CONERROR) + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, All %s Ports are in use - Please try later\r", APPName); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Application %s is not running - Please try later\r", APPName); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // IF CMD_TO_APPL SET IN APPLFLAGS, SEND INPUT MSG TO APPL + + if (Session->L4CROSSLINK->SESS_APPLFLAGS & CMD_TO_APPL) + { + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff(); + TRANSPORTENTRY * XSession = Session->L4CROSSLINK; + + if (Msg) + { + COMMANDBUFFER[72] = 13; + memcpy(Msg->L2DATA, COMMANDBUFFER, 73); + Msg->LENGTH = 73 + 4 + sizeof(void *); + Msg->PID = 0xf0; + + C_Q_ADD(&XSession->L4TX_Q, (UINT *)Msg); + PostDataAvailable(XSession); + } + } + + if (Stay) + Session->L4CROSSLINK->L4TARGET.HOST->HOSTFLAGS |= 0x20; + + // IF MSG_TO_USER SET, SEND 'CONNECTED' MESSAGE TO USER + + Session->SESS_APPLFLAGS = Session->L4CROSSLINK->SESS_APPLFLAGS; + + if (Session->L4CROSSLINK->SESS_APPLFLAGS & MSG_TO_USER) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Connected to %s\r", APPName); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + // DONT NEED BUFFER ANY MORE + + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + + +VOID CMDI00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + if (sizeof(void *) == 4) + Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s\r", VersionString); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s (64 bit)\r", VersionString); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID BYECMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + +VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // SET PACLEN FOR THIS SESSION + + char * ptr, *Context; + int newvalue; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + if (newvalue > 29 && newvalue < 256) + Session->SESSPACLEN = newvalue & 0xff; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "PACLEN - %d\r", Session->SESSPACLEN); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDIDLE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // SET IDLETIME FOR THIS SESSION + + char * ptr, *Context; + int newvalue; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + if (newvalue > 59 && newvalue < 901) + Session->L4LIMIT = newvalue; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "IDLETIME - %d\r", Session->L4LIMIT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + +} +VOID CMDT00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // SET L4 TIMEOUT FOR CONNECTS ON THIS SESSION + + char * ptr, *Context; + int newvalue; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Get new value + + newvalue = atoi(ptr); + if (newvalue > 20) + Session->SESSIONT1 = newvalue; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "L4TIMEOUT - %d\r", Session->SESSIONT1); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +UCHAR PWLen; +char PWTEXT[80]; + +VOID PWDCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char * ptr, *Context; + USHORT pwsum = 0; + int n = 5, p1, p2, p3, p4, p5; + + if (Session->Secure_Session) // HOST - SET AUTHORISED REGARDLESS + { + Session->PASSWORD = 0xFFFF; // SET AUTHORISED + Session->Secure_Session = 1; + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + { + // Check Password + + n = 5; + + while (n--) + pwsum += *(ptr++); + + if (Session->PASSWORD == pwsum) + { + Session->PASSWORD = 0xFFFF; // SET AUTHORISED + Session->Secure_Session = 1; + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + ReleaseBuffer((UINT *)REPLYBUFFER); + return; + } + + // SEND PASSWORD PROMPT + + if (PWLen == 0) + PWLen = 1; + + p1 = rand() % PWLen; + pwsum += PWTEXT[p1++]; + + p2 = rand() % PWLen; + pwsum += PWTEXT[p2++]; + + p3 = rand() % PWLen; + pwsum += PWTEXT[p3++]; + + p4 = rand() % PWLen; + pwsum += PWTEXT[p4++]; + + p5 = rand() % PWLen; + pwsum += PWTEXT[p5++]; + + Session->PASSWORD = pwsum; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%d %d %d %d %d\r", p1, p2, p3, p4, p5); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char * ptr, *Context; + int Port = 0, cols = NUMBEROFPORTS, i; + char * uptime; + struct PORTCONTROL * PORT = PORTTABLE; + struct PORTCONTROL * STARTPORT; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + // SEE IF ANY PARAM + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && ptr[0]) + Port = atoi(ptr); + + // IF ASKING FOR PORT STATS, DONT DO SYSTEM ONES + + if (Port == 0) + { + uptime = FormatUptime(STATSTIME); + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", uptime); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Semaphore Get-Rel/Clashes %9d%9d\r", + Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Buffers:Max/Cur/Min/Out/Wait%9d%9d%9d%9d%9d\r", + MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Known Nodes/Max Nodes %9d%9d\r", + NUMBEROFNODES, MAXDESTS); + + Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Connects Sent/Rxed %9d%9d\r", + L4CONNECTSOUT, L4CONNECTSIN); + + Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Frames TX/RX/Resent/Reseq%9d%9d%9d%9d\r", + L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); + + Bufferptr = Cmdprintf(Session, Bufferptr, "L3 Frames Relayed %9d\r", L3FRAMES); + + if (ptr && ptr[0] == 'S') + { + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + // POSITION TO REQUESTED PORT + + if (Port) + { + while (PORT && PORT->PORTNUMBER != Port) + { + PORT = PORT->PORTPOINTER; + cols--; + } + } + + if (PORT == NULL) // REQUESTED PORT NOT FOUND + { + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + STARTPORT = PORT; + + if (cols > 7) + cols = 7; + + Bufferptr = Cmdprintf(Session, Bufferptr, " "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port %02d ", PORT->PORTNUMBER); + PORT = PORT->PORTPOINTER; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Digied"); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2DIGIED); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Heard "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMES); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Rxed "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESFORUS); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Sent "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESSENT); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Timeouts "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2TIMEOUTS); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "REJ Frames Rxed "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2REJCOUNT); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "RX out of Seq "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2OUTOFSEQ); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Resequenced "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2RESEQ); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "Undrun/Poll T/o "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2URUNC); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "RX Overruns "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2ORUNC); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "RX CRC Errors "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->RXERRORS); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Sent "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRTX); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Received "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRRX); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "Frames abandoned"); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L1DISCARD); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + PORT = STARTPORT; + Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% "); + + for (i = 0; i < cols; i++) + { + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d", PORT->AVSENDING, PORT->AVACTIVE); + PORT = PORT->PORTPOINTER; + } + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS 'LINKS' MESSAGE + + struct _LINKTABLE * LINK = LINKS; + int n = MAXLINKS; + int len; + char Normcall[11] = ""; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r"); + + while (n--) + { + if (LINK->LINKCALL[0]) + { + len = ConvFromAX25(LINK->LINKCALL, Normcall); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); + + len = ConvFromAX25(LINK->OURCALL, Normcall); + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); + + if (LINK->Ver2point2) + Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=2.2\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE); + else + Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG); + } + LINK++; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID CMDS00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS 'USERS' + + int n = MAXCIRCUITS; + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + char State[12] = "", Type[12] = "Uplink"; + char LHS[50] = "", MID[10] = "", RHS[50] = ""; + char Line[100]; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%d)\r", SESSIONHDDR, QCOUNT); + + while (n--) + { + if (L4->L4USER[0]) + { + RHS[0] = MID[0] = 0; + + if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES + { + // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK + + if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END + { + // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION + // DISPLAY TO THE RIGHT FOR NOW + + strcpy(LHS, "(Closing) "); + DISPLAYCIRCUIT(L4, RHS); + goto CMDS50; + } + else + goto CMDS60; // WILL PROCESS FROM OTHER END + } + + if (L4->L4CROSSLINK == 0) + { + // Single Entry + + DISPLAYCIRCUIT(L4, LHS); + } + else + { + DISPLAYCIRCUIT(L4, LHS); + + Partner = L4->L4CROSSLINK; + + if (Partner->L4STATE == 5) + strcpy(MID, "<-->"); + else + strcpy(MID, "<~~>"); + + DISPLAYCIRCUIT(Partner, RHS); + } +CMDS50: + memset(Line, 32, 100); + memcpy(Line, LHS, (int)strlen(LHS)); + memcpy(&Line[35], MID, (int)strlen(MID)); + strcpy(&Line[40], RHS); + strcat(&Line[40], "\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Line); + } +CMDS60: + L4++; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Process PORTS Message + + struct PORTCONTROL * PORT = PORTTABLE; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r"); + + while (PORT) + { + if (PORT->Hide == 0) + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION); + + PORT = PORT->PORTPOINTER; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +char * DisplayRoute(TRANSPORTENTRY * Session, char * Bufferptr, struct ROUTE * Routes, char Verbose) +{ + char Normcall[10]; + char locked[] = " ! "; + int NodeCount; + int Percent = 0; + char PercentString[20]; + int Iframes, Retries; + char Active[10]; + int Queued; + + int Port = 0; + + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + + Normcall[9]=0; + + if ((Routes->NEIGHBOUR_FLAG & 1) == 1) + strcpy(locked, "!"); + else + strcpy(locked, " "); + + NodeCount = COUNTNODES(Routes); + + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + strcpy(Active, ">"); + else + strcpy(Active, " "); + + if (Verbose) + { + if (Routes->NEIGHBOUR_LINK) + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); // SEE HOW MANY QUEUED + else + Queued = 0; + + Iframes = Routes->NBOUR_IFRAMES; + Retries = Routes->NBOUR_RETRIES; + + if (Iframes) + { + Percent = (Retries * 100) / Iframes; + sprintf(PercentString, "%3d%%", Percent); + } + else + strcpy(PercentString, " "); + + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%2d %s %3d %3d%s%4d %4d %s %d %d %02d:%02d %d %d", + Active, Routes->NEIGHBOUR_PORT, Normcall, + Routes->NEIGHBOUR_QUAL, NodeCount, locked, Iframes, Retries, PercentString, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, + Routes->NEIGHBOUR_TIME >> 8, (Routes->NEIGHBOUR_TIME) & 0xff, Queued, Routes->OtherendsRouteQual); + + // IF INP3 DISPLAY SRTT + + if (Routes->INP3Node) // INP3 Enabled? + { + double srtt = Routes->SRTT/1000.0; + double nsrtt = Routes->NeighbourSRTT/1000.0; + + Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt); + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %d %s %d %d%s\r", + Active, Routes->NEIGHBOUR_PORT, Normcall, Routes->NEIGHBOUR_QUAL, NodeCount, locked); + } + + return Bufferptr; +} + + +VOID CMDR00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + char locked[] = " ! "; + int Percent = 0; + char * ptr, * Context; + char Verbose = 0; + int Port = 0; + char AXCALL[7]; + BOOL Found; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr && (int)strlen(ptr) > 1) + { + // Route Update + + goto ROUTEUPDATE; + } + + if (ptr) + { + Verbose = ptr[0]; + ptr = strtok_s(NULL, " ", &Context); + if (ptr) + Port = atoi(ptr); + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Routes\r"); + + while (MaxRoutes--) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + if (Port == 0 || Port == Routes->NEIGHBOUR_PORT) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose); + + Routes++; + } + goto SendReply; + +ROUTEUPDATE: + + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + goto SendReply; + } + + // Line is + + // ROUTES G8BPQ-2 2 100 - Set quality to 100 + // ROUTES G8BPQ-2 2 ! - Toggle 'Locked Route' flag + // ROUTES G8BPQ-2 2 100 ! - Set quality and toggle 'locked' flag + + + ConvToAX25(ptr, AXCALL); + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number Missing \r"); + goto SendReply; + } + + Found = FindNeighbour(AXCALL, Port, &Routes); + + if (Context && Context[0] > 32) + { + // More Params + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + { + // Adding + + memcpy(Routes->NEIGHBOUR_CALL, AXCALL, 7); // In case Add + Routes->NEIGHBOUR_PORT = Port; + Found = TRUE; + } + + if (strcmp(ptr, "!") == 0) + { + // Toggle Lock + + Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT + goto Displayit; + } + + if (strcmp(ptr, "Z") == 0) + { + // Clear Counts + + Routes->NBOUR_IFRAMES = 0; + Routes->NBOUR_RETRIES = 0; + goto Displayit; + } + + Routes->NEIGHBOUR_QUAL = atoi(ptr); + + if (Context && Context[0] == '!') + { + // Toggle Lock + + Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT + goto Displayit; + } + } + +Displayit: + + // Just display + + if (Found) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); + + + +/* MOV ROUTEDISP,1 + + CMP BYTE PTR [ESI],20H + JE SHORT JUSTDISPLAY + + MOV ZAPFLAG,0 + + CMP BYTE PTR [ESI],'Z' + JNE SHORT NOTZAP + + MOV ZAPFLAG,1 + JMP SHORT JUSTDISPLAY + + PUBLIC NOTZAP +NOTZAP: + + MOV ROUTEDISP,2 ; LOCK UPDATE + + CMP BYTE PTR [ESI],'!' + JE SHORT JUSTDISPLAY +; +; LOOK FOR V FOR ADDING A DIGI +; + CMP WORD PTR [ESI],' V' ; V [SPACE] + JE ADDDIGI + + CALL GETVALUE ; GET NUMBER, UP TO SPACE , CR OR OFFH + JC SHORT BADROUTECMD ; INVALID DIGITS + + MOV NEWROUTEVAL,AL + + MOV ROUTEDISP,0 + + CALL SCAN ; SEE IF ! + MOV AH,[ESI] + + + PUBLIC JUSTDISPLAY +JUSTDISPLAY: + + + MOV ESI,OFFSET32 AX25CALL + CALL _FINDNEIGHBOUR + JZ SHORT FOUNDROUTE ; IN LIST - OK + + CMP EBX,0 + JE SHORT BADROUTECMD ; TABLE FULL?? + + MOV ECX,7 + MOV EDI,EBX + REP MOVSB ; PUT IN CALL + + MOV AL,SAVEPORT + MOV NEIGHBOUR_PORT[EBX],AL + + JMP SHORT FOUNDROUTE + + + PUBLIC BADROUTECMD +BADROUTECMD: + + POP EDI + + JMP PBADVALUE + + PUBLIC FOUNDROUTE +FOUNDROUTE: + + CMP ZAPFLAG,1 + JNE SHORT NOTCLEARCOUNTS + + XOR AX,AX + MOV ES:WORD PTR NBOUR_IFRAMES[EDI],AX + MOV ES:WORD PTR NBOUR_IFRAMES+2[EDI],AX + MOV ES:WORD PTR NBOUR_RETRIES[EDI],AX + MOV ES:WORD PTR NBOUR_RETRIES+2[EDI],AX + + JMP SHORT NOUPDATE + + PUBLIC NOTCLEARCOUNTS +NOTCLEARCOUNTS: + + CMP ROUTEDISP,1 + JE SHORT NOUPDATE + + CMP ROUTEDISP,2 + JE SHORT LOCKUPDATE + + MOV AL,NEWROUTEVAL + MOV NEIGHBOUR_QUAL[EBX],AL + + CMP AH,'!' + JNE SHORT NOUPDATE + + PUBLIC LOCKUPDATE +LOCKUPDATE: + + XOR NEIGHBOUR_FLAG[EBX],1 ; FLIP LOCKED BIT + + PUBLIC NOUPDATE +NOUPDATE: + + MOV ESI,EBX + POP EDI + + POP EBX + CALL DISPLAYROUTE + + JMP SENDCOMMANDREPLY + + PUBLIC ADDDIGI +ADDDIGI: + + ADD ESI,2 + PUSH ESI ; SAVE INPUT BUFFER + + MOV ESI,OFFSET32 AX25CALL + CALL _FINDNEIGHBOUR + + POP ESI + + JZ SHORT ADD_FOUND ; IN LIST - OK + + JMP BADROUTECMD + + PUBLIC ADD_FOUND +ADD_FOUND: + + CALL CONVTOAX25 ; GET DIGI CALLSIGN + + PUSH ESI + + MOV ESI,OFFSET32 AX25CALL + LEA EDI,NEIGHBOUR_DIGI[EBX] + MOV ECX,7 + REP MOVSB + + POP ESI ; MSG BUFFER +; +; SEE IF ANOTHER DIGI +; + CMP BYTE PTR [ESI],20H + JE SHORT NOMORE + + CALL CONVTOAX25 ; GET DIGI CALLSIGN + MOV ESI,OFFSET32 AX25CALL + LEA EDI,NEIGHBOUR_DIGI+7[EBX] + MOV ECX,7 + REP MOVSB + + PUBLIC NOMORE +NOMORE: + + JMP NOUPDATE + + + +*/ + +SendReply: + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS LISTEN COMMAND + + // for monitoring a remote ax.25 port + + int Port = 0, index =0; + unsigned int ListenMask = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + char ListenPortList[128] = ""; + + ptr = strtok_s(CmdTail, " ,", &Context); + + // Now accepts a list of ports + + if (ptr == 0 || memcmp(ptr, "OFF", 3) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Listening disabled\r"); + Session->LISTEN = 0; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + while (ptr) + { + Port = atoi(ptr); + + if (Port == 0 && NUMBEROFPORTS == 1) + Port = 1; + + ptr = strtok_s(NULL, ", ", &Context); // Get Unproto String + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port %d\r", Port); + continue; + } + + if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is not an ax.25 port\r", Port); + continue; + } + + if (PORT->PORTL3FLAG) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is for internode traffic only\r", Port); + continue; + } + + if (Session->L4CIRCUITTYPE == L2LINK + UPLINK) + { + if (Session->L4TARGET.LINK->LINKPORT->PORTNUMBER == Port) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "You can't Listen to the port you are connected on\r"); + continue; + } + } + + sprintf(ListenPortList, "%s %d", ListenPortList, Port); + + + ListenMask |= (1 << (Port - 1)); + } + + Session->LISTEN = ListenMask; + + if (ListenMask) + { + if (CountBits(ListenMask) == 1) + Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on port%s. Use CQ to send a beacon, LIS to disable\r", ListenPortList); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on ports%s. Use LIS to disable\r", ListenPortList); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID UNPROTOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS UNPROTO COMMAND + + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + UCHAR axcalls[64]; + BOOL Stay, Spy; + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port == 0 && NUMBEROFPORTS == 1) + Port = 1; + else + ptr = strtok_s(NULL, " ", &Context); // Get Unproto String + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Destination missing\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + ptr[strlen(ptr)] = ' '; // Put param back together + + if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PORTL3FLAG) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Copy Address Info to Session Record + + Session->UNPROTO = Port; + Session->UAddrLen = (int)strlen(axcalls); + memcpy(Session->UADDRESS, axcalls, 63); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Unproto Mode - enter ctrl/z or /ex to exit\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID CALCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS CAL COMMAND + + int Port = 0, index = 0, Count = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port == 0 && NUMBEROFPORTS == 1) + Port = 1; + else + ptr = strtok_s(NULL, " ", &Context); // Get Unproto String + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Count Missing\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Count = atoi(ptr); + + ptr = strtok_s(NULL, " ", &Context); // Get Unproto String + + Bufferptr = Cmdprintf(Session, Bufferptr, "Ok\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + + +VOID CQCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Send a CQ Beacon on a radio port. Must be in LISTEN state + + DIGIMESSAGE Msg; + int Port = 0; + int OneBits = 0; + unsigned int MaskCopy = Session->LISTEN; + int Len; + UCHAR CQCALL[7]; + char Empty[] = ""; + char * ptr1 = &OrigCmdBuffer[3]; + UCHAR * axptr = &Msg.DIGIS[0][0]; + char * ptr2, *Context; + + while (MaskCopy) + { + if (MaskCopy & 1) + OneBits++; + + Port++; + MaskCopy = MaskCopy >> 1; + } + + if (OneBits == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "You must enter LISTEN before calling CQ\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (OneBits > 1) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "You can't call CQ if LISTENing on more than one port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + Len = (int)strlen(OrigCmdBuffer) - 3; + + if (Len < 0) + Len = 0; + + memset(&Msg, 0, sizeof(Msg)); + + Msg.PORT = Port; + Msg.CTL = 3; // UI + + // see if a Via specified + + if (_memicmp(ptr1, "via ", 4) == 0) + { + ptr2 = strtok_s(ptr1 + 4, ",", &Context); + + while (ptr2) + { + if (ConvToAX25(ptr2, axptr) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid via string\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + axptr += 7; + + if (axptr == &Msg.DIGIS[7][0]) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Too many digis\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + ptr1 = ptr2; + ptr2 = strtok_s(NULL, ",", &Context); + } + + // ptr1 is start of last digi call. We need to position to data + + ptr1 = strchr(ptr1, ' '); + + if (ptr1 == NULL) + ptr1 = Empty; + else + ptr1++ ; // to message + + Len = (int)strlen(ptr1); + + } + + ConvToAX25("CQ", CQCALL); + memcpy(Msg.DEST, CQCALL, 7); + memcpy(Msg.ORIGIN, Session->L4USER, 7); + Msg.ORIGIN[6] ^= 0x1e; // Flip SSID + Msg.PID = 0xf0; // Data PID + memcpy(&Msg.L2DATA, ptr1, Len); + + Send_AX_Datagram(&Msg, Len + 2, Port); // Len is Payload ie CTL, PID and Data + + Bufferptr = Cmdprintf(Session, Bufferptr, "CQ sent\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + +} + + +TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr) +{ + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + Session->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = Session; + + memcpy(NewSess->L4USER, Session->L4USER, 7); + memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); + + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = Session->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + return NewSess; + } + Index++; + NewSess++; + } + + if (Bufferptr) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + } + + return NULL; +} + + +VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy) +{ + TRANSPORTENTRY * NewSess; + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; // Tables Full + + NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK; + + NewSess->L4TARGET.DEST = Dest; + NewSess->L4STATE = 2; // CONNECTING + + NewSess->SPYFLAG = Spy; + + ReleaseBuffer((UINT *)REPLYBUFFER); + + SENDL4CONNECT(NewSess); + + L4CONNECTSOUT++; + + return; +} + +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK) +{ + struct _LINKTABLE * LINK = LINKS; + struct _LINKTABLE * FIRSTSPARE = NULL; + int n = MAXLINKS; + + while (n--) + { + if (LINK->LINKCALL[0] == 0) // Spare + { + if (FIRSTSPARE == NULL) + FIRSTSPARE = LINK; + + LINK++; + continue; + } + + if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall)) + { + *REQLINK = LINK; + return TRUE; + } + + LINK++; + } + // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL + + *REQLINK = FIRSTSPARE; + return FALSE; +} + +VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); + +VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS CONNECT COMMAND + + TRANSPORTENTRY * NewSess; + + int CONNECTPORT, Port; + BOOL CallEvenIfInNodes = FALSE; + char * ptr, *Context; + UCHAR axcalls[64]; + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + int ret; + struct PORTCONTROL * PORT = PORTTABLE; + struct _LINKTABLE * LINK; + int CQFLAG = 0; // NOT CQ CALL + BOOL Stay, Spy; + int n; + char TextCall[10]; + int TextCallLen; + char PortString[10]; + char cmdCopy[256]; + struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;; + + +#ifdef EXCLUDEBITS + + if (CheckExcludeList(Session->L4USER) == FALSE) + { + // CONNECTS FROM THIS STATION ARE NOT ALLOWED + + ReleaseBuffer((UINT *)REPLYBUFFER); + return; + } + +#endif + + if (Session->LISTEN) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Can't connect while listening\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + CONNECTPORT = 0; // NO PORT SPECIFIED + + ptr = strtok_s(CmdTail, " ", &Context); + + strcpy(cmdCopy, Context); // Save in case Telnet Connect + + if (ptr == 0) + { + // No param + + if (CFLAG) // C Command Disabled ? + { + // Convert to HOST (appl 32) command + + //MOV _CMDPTR,OFFSET32 _HOSTCMD + //MOV _ALIASPTR,OFFSET32 _HOSTCMD + 32 * 31 + + //MOV _APPLMASK, 80000000H ; Internal Term + + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Port = atoi(ptr); + + if (Port) + { + // IF THERE IS NOTHING FOLLOWING THE NUMBER, ASSUME IT IS A + // NUMERIC ALIAS INSTEAD OF A PORT + + sprintf(PortString, "%d", Port); + + if (strlen(PortString) < (int)strlen(ptr)) + goto NoPort; + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + EXTPORT = (struct _EXTPORTDATA *)PORT; + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr == 0) + { + // No param + + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + CONNECTPORT = Port; + + if (strcmp(ptr, "CMS") == 0 || strcmp(ptr, "HOST") == 0) // In case someeone has CMS or HOST as an alias + goto Downlink; + + } + +NoPort: + + ptr[strlen(ptr)] = ' '; // Put param back together + + if (ptr[0] == '!') + { + CallEvenIfInNodes = TRUE; + ptr++; + } + + if (memcmp(ptr, "RELAY ", 5) == 0 || memcmp(ptr, "SYNC ", 5) == 0) + { + // c p relay with extra parms + + goto Downlink; + } + + // Skip call validation if using a ptc to allow 1:call, 2:call format + + if (PORT->PROTOCOL == 10 && memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) + { + char * p; + + if (p = strstr(cmdCopy, " S ")) + { + Stay = TRUE; + p++; + *p = ' '; + } + + if (p = strstr(cmdCopy, " Z ")) + { + Spy = TRUE; + p++; + *p = ' '; + } + + goto Downlink; + } + else + { + if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + Session->STAYFLAG = Stay; + + TextCallLen = ConvFromAX25(axcalls, TextCall); + + if (CallEvenIfInNodes) + goto Downlink; + + // SEE IF CALL TO ANY OF OUR HOST SESSIONS - UNLESS DIGIS SPECIFIED + + if (axcalls[7] == 0) + { + // If this connect is as a result of a command alias, don't check appls or we will loop + + if (ALIASINVOKED == 0) + { + APPLCALLS * APPL = APPLCALLTABLE; + int n = NumberofAppls; + APPLMASK = 1; + + while (n--) + { + if (memcmp(axcalls, APPL->APPLALIAS, 6) == 0 || CompareCalls(axcalls, APPL->APPLCALL)) + { + // Call to an appl + + // Convert to an APPL command, so any alias is actioned + + // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND + + if (APPL->APPLHASALIAS && APPL->APPLALIASVAL[0] != 0x20) + { + // COPY ALIAS TO COMMAND _BUFFER, THEN REENTER COMMAND HANDLER + + memcpy(COMMANDBUFFER, APPL->APPLALIASVAL, ALIASLEN); + COMMANDBUFFER[80] = 0; + _strupr(COMMANDBUFFER); + memcpy(OrigCmdBuffer, APPL->APPLALIASVAL, ALIASLEN); // In case original case version needed + + ALIASINVOKED = TRUE; // To prevent Alias Loops + } + else + { + + // Copy Appl Command to Command Buffer. Ensure doesn't contain old command + + memset(COMMANDBUFFER, ' ', 72); + memcpy(COMMANDBUFFER, APPL->APPLCMD, 12); + } + DoTheCommand(Session); + return; + } + APPL++; + APPLMASK <<= 1; + } + } + } + + if (axcalls[7] == 0) + { + // SEE IF CALL TO ANOTHER NODE + + struct DEST_LIST * Dest = DESTS; + int n = MAXDESTS; + + if (axcalls[6] == 0x60) // if SSID, dont check aliases + { + while (n--) + { + if (memcmp(Dest->DEST_ALIAS, TextCall, 6) == 0) + { + DoNetromConnect(Session, Bufferptr, Dest, Spy); + return; + } + Dest++; + } + } + + Dest = DESTS; + n = MAXDESTS; + + while (n--) + { + if (CompareCalls(Dest->DEST_CALL, axcalls)) + { + DoNetromConnect(Session, Bufferptr, Dest, Spy); + return; + } + Dest++; + } + } + + // Must be Downlink Connect + +Downlink: + + if (CONNECTPORT == 0 && NUMBEROFPORTS > 1) + { + // L2 NEEDS PORT NUMBER + + Bufferptr = Cmdprintf(Session, Bufferptr, "Downlink connect needs port number - C P CALLSIGN\r"); + + // Send Port List + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // ENSURE PORT IS AVAILABLE FOR L2 USE + + if (PORT->PROTOCOL >= 10) // Pactor=-style port? + { + int count; + + // if Via PACTOR ARDOP WINMOR or VARA, convert to attach and call = Digi's are in AX25STRING (+7) + + if (memcmp(&axcalls[7], &WINMOR[0], 6) == 0 || + memcmp(&axcalls[7], &ARDOP[0], 6) == 0 || + memcmp(&axcalls[7], &VARA[0], 6) == 0 || + memcmp(&axcalls[7], &PACTORCALL[0], 6) == 0) + { + char newcmd[80]; + + TextCall[TextCallLen] = 0; + sprintf(newcmd, "%s %s", CmdTail, TextCall); + + ATTACHCMD(Session, Bufferptr, newcmd, NULL); + return; + } + + // If on a KAM or SCS with ax.25 on port 2, do an Attach command, then pass on connect + + if (EXTPORT->MAXHOSTMODESESSIONS <= 1) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set + + if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + count = EXTPORT->MAXHOSTMODESESSIONS; + count--; // First is Pactor Stream, count is now last ax.25 session + + while (count) + { + if (EXTPORT->ATTACHEDSESSIONS[count] == 0) + { + int Paclen, PortPaclen; + struct DATAMESSAGE * Buffer; + struct DATAMESSAGE Message = {0}; + char Callstring[80]; + int len; + + // Found a free one - use it + + // See if TNC is OK + + Message.PORT = count; + + ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); + + if ((ret & 0xff00) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK + + NewSess = SetupNewSession(Session, Bufferptr); + if (NewSess == NULL) + return; + + // if a UZ7HO port, and the uplink is L2 or Uz7HO invert SSID bits + + // We only get here if multisession + + if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip; + + if ((Session->L4CIRCUITTYPE & BPQHOST))// host + goto noFlip; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip; + else + NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + NewSess->L4USER[6] ^= 0x1e; // Flip SSID +noFlip: + EXTPORT->ATTACHEDSESSIONS[count] = NewSess; + + NewSess->KAMSESSION = count; + + // Set paclen to lower of incoming and outgoing + + Paclen = Session->SESSPACLEN; // Incoming PACLEN + + if (Paclen == 0) + Paclen = 256; // 0 = 256 + + PortPaclen = PORT->PORTPACLEN; + + if (PortPaclen == 0) + PortPaclen = 256; // 0 = 256 + + if (PortPaclen < Paclen) + Paclen = PortPaclen; + + NewSess->SESSPACLEN = Paclen; + Session->SESSPACLEN = Paclen; + + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; + NewSess->L4TARGET.PORT = PORT; + + // Send the connect command to the TNC + + Buffer = REPLYBUFFER; + + Buffer->PORT = count; + Buffer->PID = 0xf0; + + // if on Telnet Port convert use original cmd tail + + // Why just on telnet - what not all ports?? + + if (memcmp(EXTPORT->PORT_DLL_NAME, "TELNET", 6) == 0 || memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) + { + NewSess->Secure_Session = Session->Secure_Session; + len = sprintf(Callstring,"C %s", cmdCopy); + } + else + { + TextCall[TextCallLen] = 0; + + len = sprintf(Callstring,"C %s", TextCall); + + if (axcalls[7]) + { + int digi = 7; + + // we have digis + + len += sprintf(&Callstring[len], " via"); + + while (axcalls[digi]) + { + TextCall[ConvFromAX25(&axcalls[digi], TextCall)] = 0; + len += sprintf(&Callstring[len], " %s", TextCall); + digi += 7; + } + } + } + Callstring[len++] = 13; + Callstring[len] = 0; + + Buffer->LENGTH = len + MSGHDDRLEN + 1; + memcpy(Buffer->L2DATA, Callstring, len); + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); + + return; + } + count--; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if ((Session->L4CIRCUITTYPE & BPQHOST) == 0 && PORT->PORTL3FLAG) + { + //Port only for L3 + + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PortUIONLY) + { + //Port only for UI + + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for UI traffic only\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - Can't make ax.25 calls with SSID of T or R\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Get Session Entry for Downlink + + NewSess = SetupNewSession(Session, Bufferptr); + if (NewSess == NULL) + return; + + NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK; + + // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION + + memcpy(ourcall, NewSess->L4USER, 7); + + // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO) + + if ((Session->L4CIRCUITTYPE & BPQHOST))// host + goto noFlip3; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip3; + + if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession + goto noFlip3; + + ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + ourcall[6] ^= 0x1e; // Flip SSID + +noFlip3: + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(axcalls, ourcall, Port, &LINK); + + if (LINK == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + // Should release NewSess + + return; + } + + memcpy(LINK->LINKCALL, axcalls, 7); + memcpy(LINK->OURCALL, ourcall, 7); + + LINK->LINKPORT = PORT; + + LINK->L2TIME = PORT->PORTT1; + + // Copy Digis + + n = 7; + ptr = &LINK->DIGIS[0]; + + while (axcalls[n]) + { + memcpy(ptr, &axcalls[n], 7); + n += 7; + ptr += 7; + + LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI + } + + LINK->LINKTYPE = 2; // DOWNLINK + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + if (CMD->String[0] == 'N' && SUPPORT2point2) + LINK->L2STATE = 1; // New (2.2) send XID + else + LINK->L2STATE = 2; // Send SABM + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN; + + if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM + { + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + } + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls) +{ + // CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN + // CONVERTED STRING IN AXCALLS. Return FALSE if invalied + + char * axptr = AXCalls; + char * ptr, *Context; + int CQFLAG = 0; // NOT CQ CALL + int n = 8; // Max digis + + *Stay = 0; + *Spy = 0; + + memset(AXCalls, 0, 64); + + ptr = strtok_s(Calls, " ,", &Context); + + if (ptr == NULL) + return FALSE; + + // First field is Call + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + + ptr = strtok_s(NULL, " ,", &Context); + + while (ptr && n--) + { + // NEXT FIELD = COULD BE CALLSIGN, VIA, OR S (FOR STAY) + + if (strcmp(ptr, "S") == 0) + *Stay = TRUE; + else if (strcmp(ptr, "Z") == 0) + *Spy = TRUE; + else if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0) + { + } //skip via + else + { + // Convert next digi + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + } + + ptr = strtok_s(NULL, " ,", &Context); + } + + return TRUE; +} + + +VOID LINKCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // PROCESS *** LINKED to CALLSIGN + + char * ptr, *Context; + UCHAR axcall[7]; + int ret; + + if (LINKEDFLAG == 'Y' || // UNCONDITIONAL? + (LINKEDFLAG == 'A' && + ((Session->L4CIRCUITTYPE & BPQHOST) || Session->Secure_Session || Session->PASSWORD == 0xffff))) + { + ptr = strtok_s(CmdTail, " ", &Context); + if (ptr) + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + { + ret = ConvToAX25Ex(ptr, axcall); + + if (ret) + { + memcpy(Session->L4USER, axcall, 7); + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + strcpy(Bufferptr, BADMSG); + Bufferptr += (int)strlen(BADMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + memcpy(Bufferptr, PASSWORDMSG, LPASSMSG); + Bufferptr += LPASSMSG; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); + +char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest) +{ + char Normcall[10]; + char Alias[10]; + struct NR_DEST_ROUTE_ENTRY * NRRoute; + struct DEST_ROUTE_ENTRY * Route; + struct ROUTE * Neighbour; + int i, Active, len; + + Alias[6] = 0; + + memcpy(Alias, Dest->DEST_ALIAS, 6); + strlop(Alias, ' '); + + Normcall[ConvFromAX25(Dest->DEST_CALL, Normcall)] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Routes to: %s:%s", Alias, Normcall); + + if (Dest->DEST_COUNT) + Bufferptr = Cmdprintf(Session, Bufferptr, " RTT=%4.2f FR=%d %c %.1d\r", + Dest->DEST_RTT /1000.0, Dest->DEST_COUNT, + (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + NRRoute = &Dest->NRROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = NRRoute->ROUT_NEIGHBOUR; + + if (Neighbour) + { + len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); + Normcall[len] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %d %d %s\r", + (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); + } + NRRoute++; + } + + // DISPLAY INP3 ROUTES + + Route = &Dest->ROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = Route->ROUT_NEIGHBOUR; + + if (Neighbour) + { + double srtt = Route->SRTT/1000.0; + + len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); + Normcall[len] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %4.2fs %d %s\r", + (Active == i + 3)?'>':' ',Route->Hops, srtt, Neighbour->NEIGHBOUR_PORT, Normcall); + } + Route++; + } + + return Bufferptr; +} + + +int DoViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) +{ + char Portcall[10]; + int len; + + if (Dest->NRROUTE[n].ROUT_NEIGHBOUR != 0 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25(Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dest->NRROUTE[n].ROUT_QUALITY); + + cursor+=len; + + if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + return cursor; +} + +int DoINP3ViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) +{ + char Portcall[10]; + int len; + double srtt; + + if (Dest->ROUTE[n].ROUT_NEIGHBOUR != 0) + { + srtt = Dest->ROUTE[n].SRTT/1000.0; + + len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d %4.2fs ", + Portcall, + Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dest->ROUTE[n].Hops, srtt); + + cursor+=len; + + if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + return cursor; +} + +int WildCmp(char * pattern, char * string) +{ + // Check if string is at end or not. + + if (*pattern == '\0') + return *string == '\0'; + + // Check for single character missing or match + + if (*pattern == '?' || *pattern == *string) + return *string != '\0' && WildCmp(pattern + 1, string + 1); + + if (*pattern == '*') + { + // Check for multiple character missing + + return WildCmp(pattern + 1, string) || (*string != '\0' && WildCmp(pattern, string + 1)); + } + + return 0; +} + +VOID CMDN00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + struct DEST_LIST * Dest = DESTS; + int count = MAXDESTS, i; + char Normcall[10]; + char Alias[10]; + int Width = 4; + int x = 0, n = 0; + struct DEST_LIST * List[1000]; + char Param = 0; + char * ptr, * param2,* Context; + char Nodeline[21]; + char AXCALL[7]; + char * Call; + char * Qualptr; + int Qual; + char line[160]; + int cursor, len; + UCHAR axcall[7]; + int SavedOBSINIT = OBSINIT; + struct ROUTE * ROUTE = NULL; + char Pattern[80] = ""; + char * firststar; + int minqual = 0; + + ptr = strtok_s(CmdTail, " ", &Context); + param2 = strtok_s(NULL, " ", &Context); + + if (ptr) + { + if (strcmp(ptr, "ADD") == 0) + goto NODE_ADD; + + if (strcmp(ptr, "DEL") == 0) + goto NODE_DEL; + + if (strcmp(ptr, "VIA") == 0) + goto NODE_VIA; + } + + if (ptr) + { + // Could be C or a pattern. Accept C pattern or pattern C + + if ((int)strlen(ptr) > 1) + { + strcpy(Pattern, ptr); + if (param2 && param2[0] == 'C') + Param = 'C'; + } + else + { + Param = ptr[0]; + if (param2) + strcpy(Pattern, param2); + } + } + + // Pattern >nnn selects nodes with at least that quality + + if (Pattern[0] == '>') + { + minqual = atoi(&Pattern[1]); + Pattern[0] = 0; + } + + // We need to pick out CALL or CALL* from other patterns (as call use detail display) + + firststar = strchr(Pattern, '*'); + + if ((firststar && *(firststar + 1) != 0)|| strchr(Pattern, '?')) //(* not on end) + + // definitely pattern + + goto DoNodePattern; + + // If it works as CALL*, process, else drop through + + if (Pattern[0]) + { + UCHAR AXCall[8]; + int count; + int paramlen = (int)strlen(ptr); + char parampadded[20]; + int n = 0; + + Alias[8] = 0; + strcpy(parampadded, Pattern); + strcat(parampadded, " "); + + ConvToAX25(Pattern, AXCall); + + // if * on end, list all ssids + + if (firststar) + { + AXCall[6] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + while (AXCall[6] < 32) + { + Dest = DESTS; + + for (count = 0; count < MAXDESTS; count++) + { + if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count < MAXDESTS) + { + Bufferptr = DoOneNode(Session, Bufferptr, Dest); + n++; + } + + AXCall[6] += 2; + } + + if (n) // Found Some + { + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Dest = DESTS; // Reset + + // Drop through to try as pattern + } + else + { + // process as just call + + for (count = 0; count < MAXDESTS; count++) + { + if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count == MAXDESTS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = DoOneNode(Session, Bufferptr, Dest); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + +DoNodePattern: + + Bufferptr = Cmdprintf(Session, Bufferptr, "Nodes\r"); + + while (count--) + { + if (Dest->DEST_CALL[0] != 0) + { + if (Dest->NRROUTE->ROUT_QUALITY >= minqual) + if (Param != 'T' || Dest->DEST_COUNT) + List[n++] = Dest; + + if (n > 999) + break; + } + Dest++; + } + + if (Param == 'C') + qsort(List, n, sizeof(void *), CompareNode); + else + qsort(List, n, sizeof(void *), CompareAlias); + + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); + Normcall[len]=0; + + memcpy(Alias, List[i]->DEST_ALIAS, 6); + Alias[6] = 0; + strlop(Alias, ' '); + + if (strlen(Alias)) + strcat(Alias, ":"); + + if (Alias[0] == '#' && HIDENODES == 1 && Param != '*') // Hidden Node and not N * command + continue; + + if (Pattern[0]) + if (!WildCmp(Pattern, Normcall) && !WildCmp(Pattern, Alias)) + continue; + + if (Param == 'T') + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s RTT=%4.2f Frames = %d %c %.1d\r", + Alias, Normcall, List[i]->DEST_RTT /1000.0, List[i]->DEST_COUNT, + (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); + } + else + { + len = sprintf(Nodeline, "%s%s", Alias, Normcall); + memset(&Nodeline[len], ' ', 20 - len); + Nodeline[20] = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Nodeline); + + if (++x == Width) + { + x = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + } + } + } + + if (x) + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + goto SendReply; + + +NODE_VIA: + + // List Nodes reachable via a neighbour + + ptr = param2; + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); + goto SendReply; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + ConvToAX25(ptr, AXCALL); + + Dest = DESTS; + + Dest-=1; + + for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[0].ROUT_NEIGHBOUR == 0) + continue; + + + if ((Dest->NRROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->NRROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->NRROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + + || (Dest->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))) + { + len=ConvFromAX25(Dest->DEST_CALL,Normcall); + + Normcall[len]=0; + + memcpy(Alias,Dest->DEST_ALIAS,6); + + Alias[6]=0; + + for (i=0;i<6;i++) + { + if (Alias[i] == ' ') + Alias[i] = 0; + } + + cursor=sprintf(line,"%s:%s ", Alias,Normcall); + + cursor = DoViaEntry(Dest, 0, line, cursor); + cursor = DoViaEntry(Dest, 1, line, cursor); + cursor = DoViaEntry(Dest, 2, line, cursor); + cursor = DoINP3ViaEntry(Dest, 0, line, cursor); + cursor = DoINP3ViaEntry(Dest, 1, line, cursor); + cursor = DoINP3ViaEntry(Dest, 2, line, cursor); + + line[cursor++]='\r'; + line[cursor++]=0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", line); + } + } + + + goto SendReply; + +NODE_ADD: + + // FORMAT IS NODE ADD ALIAS:CALL QUAL ROUTE PORT + + + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + goto SendReply; + } + + ptr = param2; + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); + goto SendReply; + } + + Call = strlop(ptr, ':'); + + if (Call == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); + goto SendReply; + } + + + ConvToAX25(Call, AXCALL); + + Qualptr = strtok_s(NULL, " ", &Context); + + if (Qualptr == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Quality missing\r"); + goto SendReply; + } + + Qual = atoi(Qualptr); + + if (Qual < MINQUAL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Quality is below MINQUAL\r"); + goto SendReply; + } + + if (FindDestination(AXCALL, &Dest)) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Node already in Table\r"); + goto SendReply; + } + + if (Dest == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Table Full\r"); + goto SendReply; + } + + memcpy(Dest->DEST_CALL, AXCALL, 7); + memcpy(Dest->DEST_ALIAS, ptr, 6); + + NUMBEROFNODES++; + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr == NULL || ptr[0] == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Neighbour missing\r"); + goto SendReply; + } + + if (ConvToAX25(ptr, axcall) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Neighbour\r"); + goto SendReply; + } + else + { + int Port; + + ptr = strtok_s(NULL, " ", &Context); + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port missing\r"); + goto SendReply; + } + + Port = atoi(ptr); + + if (Context[0] == '!') + { + OBSINIT = 255; //; SPECIAL FOR LOCKED + } + + if (FindNeighbour(axcall, Port, &ROUTE)) + { + PROCROUTES(Dest, ROUTE, Qual); + } + + OBSINIT = SavedOBSINIT; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Added\r"); + goto SendReply; + } + + + + +/* +PNODE48: + + +; GET NEIGHBOURS FOR THIS DESTINATION +; + CALL CONVTOAX25 + JNZ SHORT BADROUTE +; + CALL GETVALUE + MOV SAVEPORT,AL ; SET PORT FOR _FINDNEIGHBOUR + + CALL GETVALUE + MOV ROUTEQUAL,AL +; + MOV ESI,OFFSET32 AX25CALL + + PUSH EBX ; SAVE DEST + CALL _FINDNEIGHBOUR + MOV EAX,EBX ; ROUTE TO AX + POP EBX + + JZ SHORT NOTBADROUTE + + JMP SHORT BADROUTE + +NOTBADROUTE: +; +; UPDATE ROUTE LIST FOR THIS DEST +; + MOV ROUT1_NEIGHBOUR[EBX],EAX + MOV AL,ROUTEQUAL + MOV ROUT1_QUALITY[EBX],AL + MOV ROUT1_OBSCOUNT[EBX],255 ; LOCKED +; + POP EDI + POP EBX + + INC _NUMBEROFNODES + + JMP SENDOK + +BADROUTE: +; +; KILL IT +; + MOV ECX,TYPE DEST_LIST + MOV EDI,EBX + MOV AL,0 + REP STOSB + + JMP BADROUTECMD + +*/ + + goto SendReply; + + +NODE_DEL: + + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + goto SendReply; + } + + ptr = param2; + + if (ptr == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); + goto SendReply; + } + + if (strcmp(ptr, "ALL") == 0) + { + struct DEST_LIST * DEST = DESTS; + int n = MAXDESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] && ((DEST->DEST_STATE & 0x80) == 0)) // Don't delete appl node + REMOVENODE(DEST); + + DEST++; + } + + ClearNodes(); + + Bufferptr = Cmdprintf(Session, Bufferptr, "All Nodes Deleted\r"); + goto SendReply; + } + + ConvToAX25(ptr, AXCALL); + + if (FindDestination(AXCALL, &Dest) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); + goto SendReply; + } + + if (Dest->DEST_STATE & 0x80) + Bufferptr = Cmdprintf(Session, Bufferptr, "APPL Node - Can't delete\r"); + else + { + REMOVENODE(Dest); + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); + } + Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); + +SendReply: + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDQUERY(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // DISPLAY AVAILABLE COMMANDS + + int n; + char * ptr; + char ApplList[2048]; + char * out = ApplList; + + CMDX * CMD = &COMMANDS[APPL1]; + + for (n = 0; n < NumberofAppls; n++) + { + ptr = &CMD->String[0]; + if (*(ptr) != '*') + { + while (*ptr != ' ') + { + *(out++) = *(ptr++); + } + *(out++) = ' '; + } + CMD++; + } + + *(out) = 0; + + n = CMDLISTLEN; + + if (NEEDMH == 0) + n -= 7; // Dont show MH + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s\r", ApplList, CMDLIST); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +char * FormatMH(MHSTRUC * MH, char Format); + +VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY HEARD LIST + + int Port = 0, sess = 0; + char * ptr, *Context, *pattern; + struct PORTCONTROL * PORT = NULL; + MHSTRUC * MH; + int count = MHENTRIES; + int n; + char Normcall[20]; + char From[10]; + char DigiList[100]; + char * Output; + int len; + char Digi = 0; + + + // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find + // how many digis there are + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + pattern = strtok_s(NULL, " ", &Context); + + if (pattern) + _strupr(pattern); // Optional filter + + MH = PORT->PORTMHEARD; + + if (MH == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "MHEARD not enabled on that port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (pattern && strstr(pattern, "CLEAR")) + { + if (Session->Secure_Session) + { + memset(MH, 0, MHENTRIES * sizeof(MHSTRUC)); + SaveMH(); + Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d Cleared\r", Port); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "MH Clear needs SYSOP status\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + else + { + if (CMD->String[2] == 'V') // MHV + { + Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port); + Bufferptr = Cmdprintf(Session, Bufferptr, "Callsign Last heard Pkts RX via Digi ;) \r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "--------- ----------- ------- ------------------------------------------\r"); + } + else + if (pattern) + Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d filtered by %s\r", Port, pattern); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d\r", Port); + } + while (count--) + { + if (MH->MHCALL[0] == 0) + break; + + Digi = 0; + + len = ConvFromAX25(MH->MHCALL, Normcall); + + Normcall[len++] = MH->MHDIGI; + Normcall[len++] = 0; + + if (pattern && strstr(Normcall, pattern) == 0) + { + MH++; + continue; + } + + n = 8; // Max number of digi-peaters + + ptr = &MH->MHCALL[6]; // End of Address bit + + Output = &DigiList[0]; + + if ((*ptr & 1) == 0) + { + // at least one digi + + strcpy(Output, "via "); + Output += 4; + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + Output += sprintf((char *)Output, "%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + { + *(Output++) = '*'; + Digi = '*'; + } + + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + { + *(Output++) = '*'; // No, so need * + Digi = '*'; + } + +} + *(Output++) = ','; + } + *(--Output) = 0; // remove last comma + } + else + *(Output) = 0; + + // if we used a digi set * on call and display via string + + + if (Digi) + Normcall[len++] = Digi; + else + DigiList[0] = 0; // Dont show list if not used + + Normcall[len++] = 0; + + + ptr = FormatMH(MH, CMD->String[2]); + + if (CMD->String[2] == 'V') // MHV + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %-10s %-10d %-30s\r", + Normcall, ptr, MH->MHCOUNT, DigiList); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %s %s\r", Normcall, ptr, DigiList); + + MH++; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int Rig_Command(int Session, char * Command); + +VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + char * ptr; + + if (Rig_Command(Session->CIRCUITINDEX, CmdTail)) + { + ReleaseBuffer((UINT *)REPLYBUFFER); + return; + } + + // Error Message is in buffer + + ptr = strchr(CmdTail, 13); + + if (ptr) + { + int len = (int)(++ptr - CmdTail); + + memcpy(Bufferptr, CmdTail, len); + Bufferptr += len; + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session); + + +VOID NRRCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // PROCESS 'NRR - Netrom Record Route' COMMAND + + char * ptr, *Context; + struct DEST_LIST * Dest = DESTS; + int count = MAXDESTS; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + UCHAR AXCall[8]; + int count; + + ConvToAX25(ptr, AXCall); + strcat(ptr, " "); + + for (count = 0; count < MAXDESTS; count++) + { + if (memcmp(Dest->DEST_ALIAS, ptr, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) + { + SendNRRecordRoute(Dest, Session); + memcpy(Bufferptr, OKMSG, 3); + Bufferptr += 3; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + } + Dest++; + } + } + Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +int CHECKINTERLOCK(struct PORTCONTROL * OURPORT) +{ + // See if any Interlocked ports are Busy + + struct PORTCONTROL * PORT = PORTTABLE; + struct _EXTPORTDATA * EXTPORT; + + int n = NUMBEROFPORTS; + int ourgroup = OURPORT->PORTINTERLOCK; + + while (PORT) + { + if (PORT != OURPORT) + { + if (PORT->PORTINTERLOCK == ourgroup) + { + // Same Group - is it busy + + int i = 0; + + EXTPORT = (struct _EXTPORTDATA *)PORT; + + while (i < 27) + if (EXTPORT->ATTACHEDSESSIONS[i++]) + return PORT->PORTNUMBER; + } + } + PORT = PORT->PORTPOINTER; + } + + return 0; +} + +VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // ATTACH to a PACTOR or similar port + + TRANSPORTENTRY * NewSess; + struct _EXTPORTDATA * EXTPORT; + struct TNCINFO * TNC; + + int Port = 0, sess = 0; + char * ptr, *Context; + int ret; + struct PORTCONTROL * PORT = NULL; + struct DATAMESSAGE Message = {0}; + int Paclen, PortPaclen; + struct DATAMESSAGE * Buffer; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL || PORT->PROTOCOL < 10) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // If attach on telnet port, find a free stream + + EXTPORT = (struct _EXTPORTDATA *)PORT; + + if (strstr(EXTPORT->PORT_DLL_NAME, "TELNET")) + { + int count = EXTPORT->MAXHOSTMODESESSIONS; + count--; // First is Pactor Stream, count is now last ax.25 session + + while (count) + { + if (EXTPORT->ATTACHEDSESSIONS[count] == 0) + { + int Paclen, PortPaclen; + struct DATAMESSAGE Message = {0}; + + // Found a free one - use it + + // See if TNC is OK + + Message.PORT = count; + + ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); + + if ((ret & 0xff00) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; + + EXTPORT->ATTACHEDSESSIONS[count] = NewSess; + + NewSess->Secure_Session = Session->Secure_Session; + + NewSess->KAMSESSION = count; + + // Set paclen to lower of incoming and outgoing + + Paclen = Session->SESSPACLEN; // Incoming PACLEN + + if (Paclen == 0) + Paclen = 256; // 0 = 256 + + PortPaclen = PORT->PORTPACLEN; + + if (PortPaclen == 0) + PortPaclen = 256; // 0 = 256 + + if (PortPaclen < Paclen) + Paclen = PortPaclen; + + NewSess->SESSPACLEN = Paclen; + Session->SESSPACLEN = Paclen; + + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; + NewSess->L4TARGET.PORT = PORT; + + ptr = strtok_s(NULL, " ", &Context); + sess = count; + + // Replace command tail with original (before conversion to upper case + + Context = Context + (OrigCmdBuffer - COMMANDBUFFER); + + goto checkattachandcall; + + + memcpy(Bufferptr, OKMSG, 3); + Bufferptr += 3; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + } + count--; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + Message.PORT = 0; + + ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); + + if ((ret & 0xff00) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // See if "Attach and Call" (for VHF ports) + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && strcmp(ptr, "S") == 0) + { + Session->STAYFLAG = TRUE; + ptr = strtok_s(NULL, " ", &Context); + } + + if (ptr) + { + // we have another param + + // if it is a single char it is a channel number for vhf attach + + if (strlen(ptr) == 1) + { + // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set + + if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + sess = ptr[0] - '@'; + + if (sess < 1 || sess > EXTPORT->MAXHOSTMODESESSIONS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Invalid Channel\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && strcmp(ptr, "S") == 0) + { + Session->STAYFLAG = TRUE; + ptr = strtok_s(NULL, " ", &Context); + } + } + } + + if (ret & 0x8000) // Disconnecting + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Check Interlock. Only ports with a TNC record can be interlocked + + TNC = PORT->TNC; + + if (TNC) + { + // See if any interlocked ports are in use + + struct TNCINFO * OtherTNC; + int i; + int rxInterlock = TNC->RXRadio; + int txInterlock = TNC->TXRadio; + + if (rxInterlock || txInterlock) + { + for (i=1; i<33; i++) + { + OtherTNC = TNCInfo[i]; + + if (OtherTNC == NULL) + continue; + + if (OtherTNC == TNC) + continue; + + if (rxInterlock == OtherTNC->RXRadio || txInterlock == OtherTNC->TXRadio) // Same Group + { + if (OtherTNC->PortRecord->ATTACHEDSESSIONS[0]) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, interlocked port %d is in use\r", i); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + } + } + } + + + + + if (EXTPORT->ATTACHEDSESSIONS[sess]) + { + // In use + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; + + // if a UZ7HO port, and the uplink is L2 or Uz7HO and multisession, + // invert SSID bits + + if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip1; + + if (EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession + goto noFlip1; + + if ((Session->L4CIRCUITTYPE & BPQHOST)) // host + goto noFlip1; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip1; + else + NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + NewSess->L4USER[6] ^= 0x1e; // Flip SSID +noFlip1: + + EXTPORT->ATTACHEDSESSIONS[sess] = NewSess; + + NewSess->KAMSESSION = sess; + + // Set paclen to lower of incoming and outgoing + + Paclen = Session->SESSPACLEN; // Incoming PACLEN + + if (Paclen == 0) + Paclen = 256; // 0 = 256 + + PortPaclen = PORT->PORTPACLEN; + + if (PortPaclen == 0) + PortPaclen = 256; // 0 = 256 + + if (PortPaclen < Paclen) + Paclen = PortPaclen; + + NewSess->SESSPACLEN = Paclen; + Session->SESSPACLEN = Paclen; + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; + NewSess->L4TARGET.PORT = PORT; + +checkattachandcall: + + if (ptr) + { + // we have a call to connect to + + char Callstring[80]; + int len; + + Buffer = REPLYBUFFER; + Buffer->PORT = sess; + Buffer->PID = 0xf0; + + len = sprintf(Callstring,"C %s", ptr); + + ptr = strtok_s(NULL, " ", &Context); + + while (ptr) // if any other params (such as digis) copy them + { + if (strcmp(ptr, "S") == 0) + { + Session->STAYFLAG = TRUE; + } + else + len += sprintf(&Callstring[len], " %s", ptr); + + ptr = strtok_s(NULL, " ", &Context); + } + + Callstring[len++] = 13; + Callstring[len] = 0; + + Buffer->LENGTH = len + MSGHDDRLEN + 1; + memcpy(Buffer->L2DATA, Callstring, len); + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); + + return; + } + + memcpy(Bufferptr, OKMSG, 3); + Bufferptr += 3; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; +} + +// SYSOP COMMANDS + +CMDX COMMANDS[] = +{ + "SAVENODES ",8, SAVENODES, 0, + "TELRECONFIG ",4, RECONFIGTELNET, 0, + "SAVEMH ",6, SAVEMHCMD, 0, + "REBOOT ",6, REBOOT, 0, + "RIGRECONFIG ",8 , RIGRECONFIG, 0, + "RESTART ",7,RESTART,0, + "RESTARTTNC ",10,RESTARTTNC,0, + "SENDNODES ",8,SENDNODES,0, + "EXTRESTART ",10, EXTPORTVAL, offsetof(EXTPORTDATA, EXTRESTART), + "TXDELAY ",3, PORTVAL, offsetof(PORTCONTROLX, PORTTXDELAY), + "MAXFRAME ",3, PORTVAL, offsetof(PORTCONTROLX, PORTWINDOW), + "RETRIES ",3, PORTVAL, offsetof(PORTCONTROLX, PORTN2), + "FRACK ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT1), + "RESPTIME ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT2), + "PPACLEN ",3,PORTVAL, offsetof(PORTCONTROLX, PORTPACLEN), + "QUALITY ",3,PORTVAL, offsetof(PORTCONTROLX, PORTQUALITY), + "PERSIST ",2,PORTVAL, offsetof(PORTCONTROLX, PORTPERSISTANCE), + "TXTAIL ",3,PORTVAL, offsetof(PORTCONTROLX, PORTTAILTIME), + "XMITOFF ",7,PORTVAL, offsetof(PORTCONTROLX, PORTDISABLED), + "DIGIFLAG ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIFLAG), + "DIGIPORT ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIPORT), + "MAXUSERS ",4,PORTVAL, offsetof(PORTCONTROLX, USERS), + "L3ONLY ",6,PORTVAL, offsetof(PORTCONTROLX, PORTL3FLAG), + "BBSALIAS ",4,PORTVAL, offsetof(PORTCONTROLX, PORTBBSFLAG), + "VALIDCALLS ",5,VALNODES,0, + "WL2KSYSOP ",5,WL2KSYSOP,0, + "STOPPORT ",4,STOPPORT,0, + "STARTPORT ",5,STARTPORT,0, + "STOPCMS ",7,STOPCMS,0, + "STARTCMS ",8,STARTCMS,0, + + "FINDBUFFS ",4,FINDBUFFS,0, + "KISS ",4,KISSCMD,0, + "GETPORTCTEXT",9,GetPortCTEXT, 0, + +#ifdef EXCLUDEBITS + + "EXCLUDE ",4,ListExcludedCalls,0, + +#endif + + "FULLDUP ",4,PORTVAL, offsetof(PORTCONTROLX, FULLDUPLEX), + "SOFTDCD ",4,PORTVAL, offsetof(PORTCONTROLX, SOFTDCDFLAG), + "OBSINIT ",7,SWITCHVAL,(size_t)&OBSINIT, + "OBSMIN ",6,SWITCHVAL,(size_t)&OBSMIN, + "NODESINT ",8,SWITCHVAL,(size_t)&L3INTERVAL, + "L3TTL ",5,SWITCHVAL,(size_t)&L3LIVES, + "L4RETRIES ",5,SWITCHVAL,(size_t)&L4N2, + "L4TIMEOUT ",5,SWITCHVALW,(size_t)&L4T1, + "T3 ",2,SWITCHVALW,(size_t)&T3, + "NODEIDLETIME",8,SWITCHVALW,(size_t)&L4LIMIT, + "LINKEDFLAG ",10,SWITCHVAL,(size_t)&LINKEDFLAG, + "IDINTERVAL ",5,SWITCHVAL,(size_t)&IDINTERVAL, + "MINQUAL ",7,SWITCHVAL,(size_t)&MINQUAL, + "FULLCTEXT ",6,SWITCHVAL,(size_t)&FULL_CTEXT, + "HIDENODES ",8,SWITCHVAL,(size_t)&HIDENODES, + "L4DELAY ",7,SWITCHVAL,(size_t)&L4DELAY, + "L4WINDOW ",6,SWITCHVAL,(size_t)&L4DEFAULTWINDOW, + "BTINTERVAL ",5,SWITCHVAL,(size_t)&BTINTERVAL, + "PASSWORD ", 8, PWDCMD, 0, + + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, + "************", 12, APPLCMD, 0, // Apppl 32 is internal Terminal + "*** LINKED ",10,LINKCMD,0, + "CQ ",2,CQCMD,0, + "CONNECT ",1,CMDC00,0, + "NC ",2,CMDC00,0, + "BYE ",1,BYECMD,0, + "QUIT ",1,BYECMD,0, + "INFO ",1,CMDI00,0, + "VERSION ",1,CMDV00,0, + "NODES ",1,CMDN00,0, + "LINKS ",1,CMDL00,0, + "LISTEN ",3,LISTENCMD,0, + "L4T1 ",2,CMDT00,0, + "PORTS ",1,CMDP00,0, + "PACLEN ",3,CMDPAC,0, + "IDLETIME ",4,CMDIDLE,0, + "ROUTES ",1,CMDR00,0, + "STATS ",1,CMDSTATS,0, + "USERS ",1,CMDS00,0, + "UNPROTO ",2,UNPROTOCMD,0, + "? ",1,CMDQUERY,0, + "DUMP ",4,DUMPCMD,0, + "MHU ",3,MHCMD,0, // UTC Times + "MHL ",3,MHCMD,0, // Local Times + "MHV ",3,MHCMD,0, + "MHEARD ",1,MHCMD,0, + "APRS ",2,APRSCMD,0, + "ATTACH ",1,ATTACHCMD,0, + "RADIO ",3,RADIOCMD,0, + "AXRESOLVER ",3,AXRESOLVER,0, + "AXMHEARD ",3,AXMHEARD,0, + "TELSTATUS ",3,SHOWTELNET,0, + "NRR ",1,NRRCMD,0, + "PING ",2,PING,0, + "AGWSTATUS ",3,SHOWAGW,0, + "ARP ",3,SHOWARP,0, + "NAT ",3,SHOWNAT,0, + "IPROUTE ",3,SHOWIPROUTE,0, + "..FLMSG ",7,FLMSG,0 +}; + +CMDX * CMD = NULL; + +int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(CMDX); + +char * ReplyPointer; // Pointer into reply buffer + +int DecodeNodeName(char * NodeName, char * ptr) +{ + // NodeName is TABLE ENTRY WITH AX25 CALL AND ALIAS + + // Copyies 20 byte 20 DECODED NAME IN FORM ALIAS:CALL to ptr + // Returns significant length of string + + int len; + char Normcall[10]; + char * alias = &NodeName[7]; + int n = 6; + char * start = ptr; + + memset(ptr, ' ', 20); + + len = ConvFromAX25(NodeName, Normcall); + + if (*(alias) > ' ') // Does alias start with a null or a space ? + { + while (*(alias) > ' ' && n--) + { + *ptr++ = *alias++; + } + *ptr++ = ':'; + } + + memcpy(ptr, Normcall, len); + ptr += len; + + return (int)(ptr - start); +} + +char * SetupNodeHeader(struct DATAMESSAGE * Buffer) +{ + char Header[20]; + int len; + + char * ptr = &Buffer->L2DATA[0]; + + len = DecodeNodeName(MYCALLWITHALIAS, Header); + + memcpy (ptr, Header, len); + ptr += len; + + (*ptr++) = HEADERCHAR; + (*ptr++) = ' '; + + return ptr; +} + +VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len) +{ + if (Len == (4 + sizeof(void *))) // Null Packet + { + ReleaseBuffer((UINT *)Buffer); + return; + } + + Buffer->LENGTH = Len; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); + + PostDataAvailable(Session); +} + + +VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) +{ + // ignore frames with single NULL (Keepalive) + + if (Buffer->LENGTH == sizeof(void *) + 5 && Buffer->L2DATA[0] == 0) + { + ReleaseBuffer(Buffer); + return; + } + + if (Buffer->LENGTH > 100) + { +// Debugprintf("BPQ32 command too long %s", Buffer->L2DATA); + ReleaseBuffer(Buffer); + return; + } + +InnerLoop: + + InnerCommandHandler(Session, Buffer); + +// See if any more commands in buffer + + if (Session->PARTCMDBUFFER) + { + char * ptr1, * ptr2; + int len; + + Buffer = Session->PARTCMDBUFFER; + + // Check that message has a CR, if not save buffer and exit + + len = Buffer->LENGTH - (4 + sizeof(void *)); + ptr1 = &Buffer->L2DATA[0]; + + ptr2 = memchr(ptr1, 13, len); + + if (ptr2 == NULL) + return; + + Session->PARTCMDBUFFER = NULL; + + goto InnerLoop; + } +} + + +VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) +{ + char * ptr1, * ptr2, *ptr3; + int len, oldlen, newlen, rest, n; + struct DATAMESSAGE * OldBuffer; + struct DATAMESSAGE * SaveBuffer; + char c; + + // If a partial command is stored, append this data to it. + + if (Session->PARTCMDBUFFER) + { + len = Buffer->LENGTH - (sizeof(void *) + 4); + ptr1 = &Buffer->L2DATA[0]; + + OldBuffer = Session->PARTCMDBUFFER; // Old Data + + if (OldBuffer == Buffer) + { + // something has gone horribly wrong + + Session->PARTCMDBUFFER = NULL; + return; + } + + oldlen = OldBuffer->LENGTH; + + newlen = len + oldlen; + + if (newlen > 200) + { + // Command far too long - ignore previous + + OldBuffer->LENGTH = oldlen = sizeof(void *) + 4; + } + + OldBuffer->LENGTH += len; + memcpy(&OldBuffer->L2DATA[oldlen - (sizeof(void *) + 4)], Buffer->L2DATA, len); + + ReleaseBuffer((UINT *)Buffer); + + Buffer = OldBuffer; + + Session->PARTCMDBUFFER = NULL; + } + + // Check that message has a CR, if not save buffer and exit + + len = Buffer->LENGTH - (sizeof(void *) + 4); + ptr1 = &Buffer->L2DATA[0]; + + // Check for sending YAPP to Node + + if (len == 2 && ptr1[0] == 5 && ptr1[1] == 1) + { + ptr1[0] = 0x15; // NAK + + ptr1[1] = sprintf(&ptr1[2], "Node doesn't support YAPP Transfers"); + + Buffer->LENGTH += ptr1[1]; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); + PostDataAvailable(Session); + return; + } + + + ptr2 = memchr(ptr1, ';', len); + + if (ptr2 == 0) + { + ptr2 = memchr(ptr1, 13, len); + + if (ptr2 == 0) + { + // No newline + + Session->PARTCMDBUFFER = Buffer; + return; + } + } + + ptr2++; + + rest = len - (int)(ptr2 - ptr1); + + if (rest) + { + // there are chars beyond the cr in the buffer + + // see if LF after CR + + if ((*ptr2) == 10) // LF + { + ptr2++; + rest--; + } + + if (rest) // May only have had LF + { + // Get a new buffer, and copy extra data to it. + + SaveBuffer = (struct DATAMESSAGE *)GetBuff(); + + if (SaveBuffer) //`Just ignore if no buffers + { + SaveBuffer->LENGTH = rest + MSGHDDRLEN + 1; + SaveBuffer->PID = 0xf0; + memcpy(&SaveBuffer->L2DATA[0], ptr2, rest); + Session->PARTCMDBUFFER = SaveBuffer; + } + } + } + + // GET PACLEN FOR THIS CONNECTION + + CMDPACLEN = Session->SESSPACLEN; + + if (CMDPACLEN == 0) + CMDPACLEN = PACLEN; // Use default if no Session PACLEN + + // If sesion is in UNPROTO Mode, send message as a UI message + + if (Session->UNPROTO) + { + DIGIMESSAGE Msg; + int Port = Session->UNPROTO; + int Len = Buffer->LENGTH - (MSGHDDRLEN -1); // Need PID + + // First check for UNPROTO exit - ctrl/z or /ex + + if (Buffer->L2DATA[0] == 26 || (Len == 6 && _memicmp(&Buffer->L2DATA[0], "/ex", 3) == 0)) // CTRL/Z or /ex + { + REPLYBUFFER = Buffer; + + Session->UNPROTO = 0; + memset(Session->UADDRESS, 0, 64); + + // SET UP HEADER + + Buffer->PID = 0xf0; + ptr1 = SetupNodeHeader(Buffer); + memcpy(ptr1, OKMSG, 3); + ptr1 += 3; + SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); + + return; + } + + memset(&Msg, 0, sizeof(Msg)); + + Msg.PORT = Port; + Msg.CTL = 3; // UI + memcpy(Msg.DEST, Session->UADDRESS, 7); + memcpy(Msg.ORIGIN, Session->L4USER, 7); + memcpy(Msg.DIGIS, &Session->UADDRESS[7], Session->UAddrLen - 7); + memcpy(&Msg.PID, &Buffer->PID, Len); + + Send_AX_Datagram(&Msg, Len, Port); // Len is Payload - CTL, PID and Data + +// SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO); + + ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply + + return; + } + + memset(COMMANDBUFFER, 32, 80); // Clear to spaces + + ptr1 = &Buffer->L2DATA[0]; + ptr2 = &COMMANDBUFFER[0]; + ptr3 = &OrigCmdBuffer[0]; + + memset(OrigCmdBuffer, 0, 80); + n = 80; + + while (n--) + { + c = *(ptr1++) & 0x7f; // Mask paritu + + if (c == 13 || c == ';') + break; // CR + + *(ptr3++) = c; // Original Case + + c = toupper(c); + *(ptr2++) = c; + } + + + // USE INPUT MESSAGE _BUFFER FOR REPLY + + REPLYBUFFER = Buffer; + + // SET UP HEADER + + Buffer->PID = 0xf0; + ptr1 = SetupNodeHeader(Buffer); + + ReplyPointer = ptr1; + + ALIASINVOKED = 0; // Clear "Invoked by APPL ALIAS flag" + + DoTheCommand(Session); // We also call DotheCommand when we need to reprocess - eg for alias handling +} + +VOID DoTheCommand(TRANSPORTENTRY * Session) +{ + struct DATAMESSAGE * Buffer = REPLYBUFFER; + char * ptr1, * ptr2; + int n; + + ptr1 = &COMMANDBUFFER[0]; // + + n = 10; + + while ((*ptr1 == ' ' || *ptr1 == 0) && n--) + ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) + + if (n == -1) + { + // Null command + + ReleaseBuffer((UINT *)Buffer); + return; + } + + ptr2 = ptr1; // Save + + + CMD = &COMMANDS[0]; + n = 0; + + for (n = 0; n < NUMBEROFCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + ptr1 = ptr2; + + CMDPTR = CMD; + + if (n == APPL1) // First APPL command + { + APPLMASK = 1; // FOR APPLICATION ATTACH REQUESTS + ALIASPTR = &CMDALIAS[0][0]; + } + + // ptr1 is input command + + if (memcmp(CMD->String, ptr1, CL) == 0) + { + // Found match so far - check rest + + char * ptr2 = &CMD->String[CL]; + + ptr1 += CL; + + if (*(ptr1) != ' ') + { + while(*(ptr1) == *ptr2 && *(ptr1) != ' ') + { + ptr1++; + ptr2++; + } + } + + if (*(ptr1) == ' ') + { + Session->BADCOMMANDS = 0; // RESET ERROR COUNT + + // SEE IF SYSOP COMMAND, AND IF SO IF PASSWORD HAS BEEN ENTERED + + if (n < PASSCMD) + { + //NEEDS PASSWORD FOR SYSOP COMMANDS + + if (Session->PASSWORD != 0xFFFF) + { + ptr1 = ReplyPointer; + + memcpy(ptr1, PASSWORDMSG, LPASSMSG); + ptr1 += LPASSMSG; + + SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); + return; + } + } +// VALNODESFLAG = 0; // NOT VALID NODES COMMAND + + ptr1++; // Skip space + + CMD->CMDPROC(Session, ReplyPointer, ptr1, CMD); + return; + } + } + + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + + CMD++; + + } + Session->BADCOMMANDS++; + + if (Session->BADCOMMANDS > 6) // TOO MANY ERRORS + { + ReleaseBuffer((UINT *)Buffer); + Session->STAYFLAG = 0; + CLOSECURRENTSESSION(Session); + return; + } + + ptr1 = ReplyPointer; + + memcpy(ptr1, CMDERRMSG, CMDERRLEN); + ptr1 += CMDERRLEN; + + SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); +} + + +VOID StatsTimer() +{ + struct PORTCONTROL * PORT = PORTTABLE; + int sum; + + while(PORT) + { + sum = PORT->SENDING / 11; + PORT->AVSENDING = sum; + + sum = (PORT->SENDING + PORT->ACTIVE) /11; + PORT->AVACTIVE = sum; + + PORT->SENDING = 0; + PORT->ACTIVE = 0; + + PORT = PORT->PORTPOINTER; + } +} + + + +extern struct AXIPPORTINFO * Portlist[]; + +#define TCPConnected 4 + + +VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY AXIP Resolver info + + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + struct AXIPPORTINFO * AXPORT; + char Normcall[11]; + char Flags[10]; + struct arp_table_entry * arp; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + AXPORT = Portlist[Port]; + + if (AXPORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Resolver info for Port %d\r", Port); + + while (index < AXPORT->arp_table_len) + { + arp = &AXPORT->arp_table[index]; + + if (arp->ResolveFlag && arp->error != 0) + { + // resolver error - Display Error Code + sprintf(AXPORT->hostaddr, "Error %d", arp->error); + } + else + { + if (arp->IPv6) + Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, AXPORT->hostaddr, TRUE); + else + Format_Addr((unsigned char *)&arp->destaddr.sin_addr, AXPORT->hostaddr, FALSE); + } + + ConvFromAX25(arp->callsign, Normcall); + + Flags[0] = 0; + + if (arp->BCFlag) + strcat(Flags, "B "); + + if (arp->TCPState == TCPConnected) + strcat(Flags, "C "); + + if (arp->AutoAdded) + strcat(Flags, "A"); + + if (arp->port == arp->SourcePort) + Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d = %-.42s %s\r", + Normcall, + arp->hostname, + arp->port, + AXPORT->hostaddr, + Flags); + + else + Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d<%d = %-.42s %s\r", + Normcall, + arp->hostname, + arp->port, + arp->SourcePort, + AXPORT->hostaddr, + Flags); + + index++; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY AXIP Mheard info + + int Port = 0, index = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + struct AXIPPORTINFO * AXPORT; + int n = MHENTRIES; + char Normcall[11]; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + AXPORT = Portlist[Port]; + + if (AXPORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Mheard for Port %d\r", Port); + + while (index < MaxMHEntries) + { + if (AXPORT->MHTable[index].proto != 0) + { + char Addr[80]; + + Format_Addr((unsigned char *)&AXPORT->MHTable[index].ipaddr6, Addr, AXPORT->MHTable[index].IPv6); + + Normcall[ConvFromAX25(AXPORT->MHTable[index].callsign, Normcall)] = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-15s %c %-6d %-25s%c\r", Normcall, + Addr, + AXPORT->MHTable[index].proto, + AXPORT->MHTable[index].port, + asctime(gmtime( &AXPORT->MHTable[index].LastHeard )), + (AXPORT->MHTable[index].Keepalive == 0) ? ' ' : 'K'); + + Bufferptr[-3] = ' '; // Clear CR returned by asctime + } + + index++; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +#pragma pack() + +extern struct TNCINFO * TNCInfo[41]; + +extern char WL2KCall[10]; +extern char WL2KLoc[7]; + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + +VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + + char LastUpdated[100]; + char Name[100] = ""; + char Addr1[100] = ""; + char Addr2[100] = ""; + char City[100] = ""; + char State[100] = ""; + char Country[100] = ""; + char PostCode[100] = ""; + char Email[100] = ""; + char Website[100] = ""; + char Phone[100] = ""; + char Data[100] = ""; + char LOC[100] = ""; + BOOL Exists = TRUE; + time_t LastUpdateSecs = 0; + char * ptr1, * ptr2; + + SOCKET sock; + + int Len; + char Message[2048]; + + if (WL2KCall[0] < 33) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Winlink reporting is not configured\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to connect to WL2K Database\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (strstr(_REPLYBUFFER, "\"ErrorMessage\":")) + Exists = FALSE; + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Name); + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Addr1); + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Addr2); + GetJSONValue(_REPLYBUFFER, "\"City\":", City); + GetJSONValue(_REPLYBUFFER, "\"State\":", State); + GetJSONValue(_REPLYBUFFER, "\"Country\":", Country); + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", PostCode); + GetJSONValue(_REPLYBUFFER, "\"Email\":", Email); + GetJSONValue(_REPLYBUFFER, "\"Website\":", Website); + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Phone); + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Data); + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", LOC); + GetJSONValue(_REPLYBUFFER, "\"Timestamp\":", LastUpdated); + + ptr1 = strchr(LastUpdated, '('); + + if (ptr1) + { + ptr2 = strchr(++ptr1, ')'); + + if (ptr2) + { + *(ptr2 - 3) = 0; // remove millisecs + LastUpdateSecs = atoi(ptr1); + + FormatTime3(LastUpdated, LastUpdateSecs); + } + } + + if (_memicmp(CmdTail, "SET ", 4) == 0) + { + if (Exists) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Record already exists in WL2K Database\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Set New Values. Any other params are values to set, separated by | + +// ptr1 = strtok_s(&CmdTail[4], ",", &Context); + +// if (ptr1 == NULL) +// goto DoReplace; + +// strcpy(Name, ptr1); + +//DoReplace: + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\",", + + WL2KCall, WL2KLoc, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + SendHTTPRequest(sock, "api.winlink.org", 80, + "/sysop/add", Message, Len, NULL); + + closesocket(sock); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Database Updated\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Exists) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "\rWL2K SYSOP Info for %s\r", WL2KCall); + Bufferptr = Cmdprintf(Session, Bufferptr, "Grid Square: %s\r", LOC); + Bufferptr = Cmdprintf(Session, Bufferptr, "Name: %s\r", Name); + Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 1: %s\r", Addr1); + Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 2: %s\r", Addr2); + Bufferptr = Cmdprintf(Session, Bufferptr, "City: %s\r", City); + Bufferptr = Cmdprintf(Session, Bufferptr, "State: %s\r", State); + Bufferptr = Cmdprintf(Session, Bufferptr, "Country: %s\r", Country); + Bufferptr = Cmdprintf(Session, Bufferptr, "PostCode: %s\r", PostCode); + Bufferptr = Cmdprintf(Session, Bufferptr, "Email Address: %s\r", Email); + Bufferptr = Cmdprintf(Session, Bufferptr, "Website: %s\r", Website); + Bufferptr = Cmdprintf(Session, Bufferptr, "Phone: %s\r", Phone); + Bufferptr = Cmdprintf(Session, Bufferptr, "Additional Data: %s\r", Data); + Bufferptr = Cmdprintf(Session, Bufferptr, "Last Updated: %s\r", LastUpdated); + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, "No SYSOP record for %s\r", WL2KCall); + + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID CloseKISSPort(struct PORTCONTROL * PortVector); +int OpenConnection(struct PORTCONTROL * PortVector); + +VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + + struct TNCINFO * TNC; + struct TCPINFO * TCP; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + TNC = TNCInfo[portno]; + + if (!TNC || !TNC->TCPInfo) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TCP = TNC->TCPInfo; + + TCP->CMS = 0; + TCP->CMSOK = FALSE; +#ifndef LINBPQ + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); + SetWindowText(TCP->hCMSWnd, "CMS Off"); +#endif + Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Disabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + + struct TNCINFO * TNC; + struct TCPINFO * TCP; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + TNC = TNCInfo[portno]; + + if (!TNC || !TNC->TCPInfo) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TCP = TNC->TCPInfo; + TCP->CMS = 1; +#ifndef LINBPQ + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); +#endif + CheckCMS(TNC); + + Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + struct KISSINFO * KISS; + + if (PORT->PORTSTOPCODE) + { + // Port has Close Routine + + PORT->PortStopped = TRUE; + + if (PORT->PORTSTOPCODE(PORT)) + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Close Failed\r"); + + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + if (PORT->PORTTYPE != 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *) PORT; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + CloseKISSPort(PORT); + PORT->PortStopped = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + + if (portno) + { + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + struct KISSINFO * KISS; + + if (PORT->PORTSTARTCODE) + { + // Port has Open Routine + + PORT->PortStopped = FALSE; + + if (PORT->PORTSTARTCODE(PORT)) + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + if (PORT->PORTTYPE != 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *) PORT; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (OpenConnection(PORT), TRUE) + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); + + PORT->PortStopped = FALSE; + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + } + } + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + + +#define FEND 0xC0 +int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count); + + +VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno = 0; + int cmd = 0, val = 0; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + + // Send KISS Command to TNC + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + { + cmd = atoi (ptr); + ptr = strtok_s(NULL, " ", &Context); + + if (ptr) + val = atoi (ptr); + } + } + + if (portno == 0 || cmd == 0) + { + strcpy(Bufferptr, BADMSG); + Bufferptr += (int)strlen(BADMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + while (n--) + { + if (PORT->PORTNUMBER == portno) + { + struct KISSINFO * KISS; + UCHAR ENCBUFF[16]; + unsigned char * ptr = ENCBUFF; + + if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *) PORT; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Send Command + + *(ptr++) = FEND; + *(ptr++) = KISS->OURCTRL | cmd; + *(ptr++) = (UCHAR)val; + *(ptr++) = FEND; + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q + + PORT->Session = Session; + PORT->LastKISSCmdTime = time(NULL); + + ASYSEND(PORT, ENCBUFF, 4); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + PORT = PORT->PORTPOINTER; + } + + + // Bad port + + strcpy(Bufferptr, BADPORT); + Bufferptr += (int)strlen(BADPORT); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + +VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + FindLostBuffers(); + +#ifdef WIN32 + Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to Debugview\r"); +#else + Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to syslog\r"); +#endif + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) +{ + // Telnet Connection from FLMSG + CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link + ReleaseBuffer((UINT *)REPLYBUFFER); +} + +BOOL CheckExcludeList(UCHAR * Call) +{ + UCHAR * ptr1 = ExcludeList; + + while (*ptr1) + { + if (memcmp(Call, ptr1, 6) == 0) + return FALSE; + + ptr1 += 7; + } + + return TRUE; +} + + +void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + + UCHAR * ptr = ExcludeList; + char Normcall[10] = ""; + UCHAR AXCall[8] = ""; + + if (*CmdTail == ' ') + goto DISPLIST; + + if (*CmdTail == 'Z') + { + // CLEAR LIST + + memset(ExcludeList, 0, 70); + goto DISPLIST; + } + + ConvToAX25(CmdTail, AXCall); + + if (strlen(ExcludeList) < 70) + strcat(ExcludeList, AXCall); + +DISPLIST: + + while (*ptr) + { + Normcall[ConvFromAX25(ptr, Normcall)] = 0; + Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); + ptr += 7; + } + + *(Bufferptr++) = '\r'; + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr) +{ + if (Session->PASSWORD != 0xFFFF) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return FALSE; + } + + return TRUE; +} + + + + + + + diff --git a/CmdLineAuth.c b/CmdLineAuth.c new file mode 100644 index 0000000..24b93e8 --- /dev/null +++ b/CmdLineAuth.c @@ -0,0 +1,62 @@ +// CmdLineAuth.cpp : Defines the entry point for the console application. +// + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#define _USE_32BIT_TIME_T + +#include + +#include +#include + +#include "md5.c" + + +VOID CreateOneTimePassword(char * KeyPhrase) +{ + // Create a time dependent One Time Password from the KeyPhrase + + time_t NOW = time(NULL); + unsigned char Hash[16]; + char Password[20]; + char Key[1000]; + int i, chr; + long long Val; + int PassCode; + + NOW = NOW/30; // Only Change every 30 secs + + sprintf(Key, "%s%x", KeyPhrase, NOW); + + md5(Key, Hash); + + for (i=0; i<16; i++) + { + chr = (Hash[i] & 31); + if (chr > 9) chr += 7; + + Password[i] = chr + 48; + } + + Password[16] = 0; + + memcpy(&Val, Password, 8); + PassCode = Val % 1000000; + printf("Passcode is %06d\n", PassCode); + + return; +} + +int main(int argc, char * argv[]) +{ + if (argc < 2) + { + printf ("Need to supply KeyPhrase\n"); + return 0; + } + CreateOneTimePassword(argv[1]); + return 0; +} + diff --git a/CmdLineAuth.cpp b/CmdLineAuth.cpp new file mode 100644 index 0000000..1e08882 --- /dev/null +++ b/CmdLineAuth.cpp @@ -0,0 +1,68 @@ +// CmdLineAuth.cpp : Defines the entry point for the console application. +// + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#define _USE_32BIT_TIME_T + +#include + +#include +#include + +#include "MD5.c" + +int LastNow; +int PassCode; + +VOID CreateOneTimePassword(char * KeyPhrase) +{ + // Create a time dependent One Time Password from the KeyPhrase + + time_t NOW = time(NULL); + unsigned char Hash[16]; + char Password[20]; + char Key[1000]; + int i, chr; + long long Val; + + NOW = NOW/30; // Only Change every 30 secs + + if (NOW == LastNow) + return; + + LastNow = NOW; + + sprintf(Key, "%s%x", KeyPhrase, NOW); + + md5(Key, Hash); + + for (i=0; i<16; i++) + { + chr = (Hash[i] & 31); + if (chr > 9) chr += 7; + + Password[i] = chr + 48; + } + + Password[16] = 0; + + memcpy(&Val, Password, 8); + PassCode = Val %= 1000000; + printf("Passcode is %d\n", PassCode); + + return; +} + +int main(int argc, char * argv[]) +{ + if (argc < 2) + { + printf ("Need to supply KeyPhrase\n"); + return 0; + } + CreateOneTimePassword(argv[1]); + return 0; +} + diff --git a/CommonCode.c b/CommonCode.c new file mode 100644 index 0000000..dfecee3 --- /dev/null +++ b/CommonCode.c @@ -0,0 +1,4667 @@ +/* +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 +*/ + + + +// General C Routines common to bpq32 and linbpq. Mainly moved from BPQ32.c + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "CHeaders.h" +#include "tncinfo.h" +#include "configstructs.h" + +extern struct CONFIGTABLE xxcfg; + +#define LIBCONFIG_STATIC +#include "libconfig.h" + +#ifndef LINBPQ + +//#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. + +#include "commctrl.h" +#include "Commdlg.h" + +#endif + +struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +extern int ReportTimer; + +Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port); +TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask); +int Check_Timer(); +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg); +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +VOID COMSetDTR(HANDLE fd); +VOID COMClearDTR(HANDLE fd); +VOID COMSetRTS(HANDLE fd); +VOID COMClearRTS(HANDLE fd); + +VOID WriteMiniDump(); +void printStack(void); +char * FormatMH(PMHSTRUC MH, char Format); +void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode); +extern BOOL LogAllConnects; + +// Read/Write length field in a buffer header + +// Needed for Big/LittleEndian and ARM5 (unaligned operation problem) portability + + +VOID PutLengthinBuffer(PDATAMESSAGE buff, USHORT datalen) +{ + if (datalen <= sizeof(void *) + 4) + datalen = sizeof(void *) + 4; // Protect + + memcpy(&buff->LENGTH, &datalen, 2); +} + +int GetLengthfromBuffer(PDATAMESSAGE buff) +{ + USHORT Length; + + memcpy(&Length, &buff->LENGTH, 2); + return Length; +} + +BOOL CheckQHeadder(UINT * Q) +{ +#ifdef WIN32 + UINT Test; + + __try + { + Test = *Q; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + Debugprintf("Invalid Q Header %p", Q); + printStack(); + return FALSE; + } +#endif + return TRUE; +} + +// Get buffer from Queue + + +VOID * _Q_REM(VOID **PQ, char * File, int Line) +{ + void ** Q; + void ** first; + VOID * next; + PMESSAGE Test; + + // PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (Semaphore.Flag == 0) + Debugprintf("Q_REM called without semaphore from %s Line %d", File, Line); + + if (CheckQHeadder((UINT *) Q) == 0) + return(0); + + first = Q[0]; + + if (first == 0) + return (0); // Empty + + next = first[0]; // Address of next buffer + + Q[0] = next; + + // Make sure guard zone is zeros + + Test = (PMESSAGE)first; + + if (Test->GuardZone != 0) + { + Debugprintf("Q_REM %p GUARD ZONE CORRUPT %x Called from %s Line %d", first, Test->GuardZone, File, Line); + printStack(); + } + + return first; +} + +// Non=pool version (for IPGateway) + +VOID * _Q_REM_NP(VOID *PQ, char * File, int Line) +{ + void ** Q; + void ** first; + void * next; + + // PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (CheckQHeadder((UINT *)Q) == 0) + return(0); + + first = Q[0]; + + if (first == 0) return (0); // Empty + + next = first[0]; // Address of next buffer + + Q[0] = next; + + return first; +} + +// Return Buffer to Free Queue + +extern VOID * BUFFERPOOL; +extern void ** Bufferlist[1000]; +void printStack(void); + +void _CheckGuardZone(char * File, int Line) +{ + int n = 0, i, offset = 0; + PMESSAGE Test; + UINT CodeDump[8]; + unsigned char * ptr; + + n = NUMBEROFBUFFERS; + + while (n--) + { + Test = (PMESSAGE)Bufferlist[n]; + + if (Test && Test->GuardZone) + { + Debugprintf("CheckGuardZone %p GUARD ZONE CORRUPT %d Called from %s Line %d", Test, Test->Process, File, Line); + + offset = 0; + ptr = (unsigned char *)Test; + + while (offset < 400) + { + memcpy(CodeDump, &ptr[offset], 32); + + for (i = 0; i < 8; i++) + CodeDump[i] = htonl(CodeDump[i]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + &ptr[offset], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + offset += 32; + } + WriteMiniDump(); +#ifdef MDIKERNEL + CloseAllNeeded = 1; +#endif + } + + } +} + +UINT _ReleaseBuffer(VOID *pBUFF, char * File, int Line) +{ + void ** pointer, ** BUFF = pBUFF; + int n = 0; + void ** debug; + PMESSAGE Test; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore from %s Line %d", File, Line); + + // Make sure address is within pool + + Test = (PMESSAGE)pBUFF; + + if (Test->GuardZone != 0) + { + Debugprintf("_ReleaseBuffer %p GUARD ZONE CORRUPT %x Called from %s Line %d", pBUFF, Test->GuardZone, File, Line); + } + + while (n <= NUMBEROFBUFFERS) + { + if (BUFF == Bufferlist[n++]) + goto BOK1; + } + + Debugprintf("ReleaseBuffer %X not in Pool called from %s Line %d", BUFF, File, Line); + printStack(); + + return 0; + +BOK1: + + n = 0; + + // See if already on free Queue + + pointer = FREE_Q; + + while (pointer) + { + if (pointer == BUFF) + { + Debugprintf("Trying to free buffer %p when already on FREE_Q", BUFF); +// WriteMiniDump(); + + return 0; + } + +// if (pointer[0] && pointer == pointer[0]) +// { +// Debugprintf("Buffer chained to itself"); +// return 0; +// } + + debug = pointer; + pointer = pointer[0]; + n++; + + if (n > 1000) + { + Debugprintf("Loop searching free chain - pointer = %p %p", debug, pointer); + return 0; + } + } + + pointer = FREE_Q; + + *BUFF = pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return 0; +} + +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line) +{ + void ** Q; + void ** BUFF = PBUFF; + void ** next; + PMESSAGE Test; + + + int n = 0; + +// PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (Semaphore.Flag == 0) + Debugprintf("C_Q_ADD called without semaphore from %s Line %d", File, Line); + + if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable + return(0); + + // Make sure guard zone is zeros + + Test = (PMESSAGE)PBUFF; + + if (Test->GuardZone != 0) + { + Debugprintf("C_Q_ADD %p GUARD ZONE CORRUPT %x Called from %s Line %d", PBUFF, Test->GuardZone, File, Line); + } + + Test = (PMESSAGE)Q; + + + + // Make sure address is within pool + + while (n <= NUMBEROFBUFFERS) + { + if (BUFF == Bufferlist[n++]) + goto BOK2; + } + + Debugprintf("C_Q_ADD %X not in Pool called from %s Line %d", BUFF, File, Line); + printStack(); + + return 0; + +BOK2: + + BUFF[0] = 0; // Clear chain in new buffer + + if (Q[0] == 0) // Empty + { + Q[0]=BUFF; // New one on front + return(0); + } + + next = Q[0]; + + while (next[0] != 0) + { + next = next[0]; // Chain to end of queue + } + next[0] = BUFF; // New one on end + + return(0); +} + +// Non-pool version + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF) +{ + void ** Q; + void ** BUFF = PBUFF; + void ** next; + int n = 0; + +// PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable + return(0); + + BUFF[0]=0; // Clear chain in new buffer + + if (Q[0] == 0) // Empty + { + Q[0]=BUFF; // New one on front +// memcpy(PQ, &BUFF, 4); + return 0; + } + next = Q[0]; + + while (next[0] != 0) + next=next[0]; // Chain to end of queue + + next[0] = BUFF; // New one on end + + return(0); +} + + +int C_Q_COUNT(VOID *PQ) +{ + void ** Q; + int count = 0; + +// PQ may not be word aligned, so copy as bytes (for ARM5) + + Q = PQ; + + if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable + return(0); + + // SEE HOW MANY BUFFERS ATTACHED TO Q HEADER + + while (*Q) + { + count++; + if ((count + QCOUNT) > MAXBUFFS) + { + Debugprintf("C_Q_COUNT Detected corrupt Q %p len %d", PQ, count); + return count; + } + Q = *Q; + } + + return count; +} + +VOID * _GetBuff(char * File, int Line) +{ + UINT * Temp; + MESSAGE * Msg; + char * fptr = 0; + unsigned char * byteaddr; + + Temp = Q_REM(&FREE_Q); + +// FindLostBuffers(); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore from %s Line %d", File, Line); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + + Msg = (MESSAGE *)Temp; + fptr = File + (int)strlen(File); + while (*fptr != '\\' && *fptr != '/') + fptr--; + fptr++; + + // Buffer Length is BUFFLEN, but buffers are allocated 512 + // So add file info in gap between + + byteaddr = (unsigned char *)Msg; + + + memset(&byteaddr[400], 0, 64); // simplify debugging lost buffers + sprintf(&byteaddr[400], "%s %d", fptr, Line); + + Msg->Process = (short)GetCurrentProcessId(); + Msg->Linkptr = NULL; + } + else + Debugprintf("Warning - Getbuff returned NULL"); + + return Temp; +} + +void * zalloc(int len) +{ + // malloc and clear + + void * ptr; + + ptr=malloc(len); + + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer) +{ + UCHAR Type = L4->L4CIRCUITTYPE; + struct PORTCONTROL * PORT; + struct _LINKTABLE * LINK; + BPQVECSTRUC * VEC; + struct DEST_LIST * DEST; + + char Normcall[20] = ""; // Could be alias:call + char Normcall2[11] = ""; + char Alias[11] = ""; + + Buffer[0] = 0; + + switch (Type) + { + case PACTOR+UPLINK: + + PORT = L4->L4TARGET.PORT; + + ConvFromAX25(L4->L4USER, Normcall); + strlop(Normcall, ' '); + + if (PORT) + sprintf(Buffer, "%s %d/%d(%s)", "TNC Uplink Port", PORT->PORTNUMBER, L4->KAMSESSION, Normcall); + + return; + + + case PACTOR+DOWNLINK: + + PORT = L4->L4TARGET.PORT; + + if (PORT) + sprintf(Buffer, "%s %d/%d", "Attached to Port", PORT->PORTNUMBER, L4->KAMSESSION); + return; + + + case L2LINK+UPLINK: + + LINK = L4->L4TARGET.LINK; + + ConvFromAX25(L4->L4USER, Normcall); + strlop(Normcall, ' '); + + if (LINK &&LINK->LINKPORT) + sprintf(Buffer, "%s %d(%s)", "Uplink", LINK->LINKPORT->PORTNUMBER, Normcall); + + return; + + case L2LINK+DOWNLINK: + + LINK = L4->L4TARGET.LINK; + + if (LINK == NULL) + return; + + ConvFromAX25(LINK->OURCALL, Normcall); + strlop(Normcall, ' '); + + ConvFromAX25(LINK->LINKCALL, Normcall2); + strlop(Normcall2, ' '); + + sprintf(Buffer, "%s %d(%s %s)", "Downlink", LINK->LINKPORT->PORTNUMBER, Normcall, Normcall2); + return; + + case BPQHOST + UPLINK: + case BPQHOST + DOWNLINK: + + // if the call has a Level 4 address display ALIAS:CALL, else just Call + + if (FindDestination(L4->L4USER, &DEST)) + Normcall[DecodeNodeName(DEST->DEST_CALL, Normcall)] = 0; // null terminate + else + Normcall[ConvFromAX25(L4->L4USER, Normcall)] = 0; + + VEC = L4->L4TARGET.HOST; + sprintf(Buffer, "%s%02d(%s)", "Host", (int)(VEC - BPQHOSTVECTOR) + 1, Normcall); + return; + + case SESSION + DOWNLINK: + case SESSION + UPLINK: + + ConvFromAX25(L4->L4USER, Normcall); + strlop(Normcall, ' '); + + DEST = L4->L4TARGET.DEST; + + if (DEST == NULL) + return; + + ConvFromAX25(DEST->DEST_CALL, Normcall2); + strlop(Normcall2, ' '); + + memcpy(Alias, DEST->DEST_ALIAS, 6); + strlop(Alias, ' '); + + sprintf(Buffer, "Circuit(%s:%s %s)", Alias, Normcall2, Normcall); + + return; + } +} + +VOID CheckForDetach(struct TNCINFO * TNC, int Stream, struct STREAMINFO * STREAM, + VOID TidyCloseProc(), VOID ForcedCloseProc(), VOID CloseComplete()) +{ + void ** buffptr; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Node has disconnected - clear any connection + + if (STREAM->Disconnecting) + { + // Already detected the detach, and have started to close + + STREAM->DisconnectingTimeout--; + + if (STREAM->DisconnectingTimeout) + return; // Give it a bit longer + + // Close has timed out - force a disc, and clear + + ForcedCloseProc(TNC, Stream); // Send Tidy Disconnect + + goto NotConnected; + } + + // New Disconnect + + Debugprintf("New Disconnect Port %d Q %x", TNC->Port, STREAM->BPQtoPACTOR_Q); + + if (STREAM->Connected || STREAM->Connecting) + { + char logmsg[120]; + time_t Duration; + + // Need to do a tidy close + + STREAM->Connecting = FALSE; + STREAM->Disconnecting = TRUE; + STREAM->DisconnectingTimeout = 300; // 30 Secs + + if (Stream == 0) + SetWindowText(TNC->xIDC_TNCSTATE, "Disconnecting"); + + // Create a traffic record + + if (STREAM->Connected) + { + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; // Or will get divide by zero error + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + if (STREAM->BPQtoPACTOR_Q) // Still data to send? + return; // Will close when all acked + +// if (STREAM->FramesOutstanding && TNC->Hardware == H_UZ7HO) +// return; // Will close when all acked + + TidyCloseProc(TNC, Stream); // Send Tidy Disconnect + + return; + } + + // Not connected +NotConnected: + + STREAM->Disconnecting = FALSE; + STREAM->Attached = FALSE; + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; + + if (Stream == 0) + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + + STREAM->FramesQueued = 0; + STREAM->FramesOutstanding = 0; + + CloseComplete(TNC, Stream); + + while(STREAM->BPQtoPACTOR_Q) + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + } + + while(STREAM->PACTORtoBPQ_Q) + { + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + ReleaseBuffer(buffptr); + } + } +} + +char * CheckAppl(struct TNCINFO * TNC, char * Appl) +{ + APPLCALLS * APPL; + BPQVECSTRUC * PORTVEC; + int Allocated = 0, Available = 0; + int App, Stream; + struct TNCINFO * APPLTNC; + +// Debugprintf("Checking if %s is running", Appl); + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + + if (_memicmp(APPL->APPLCMD, Appl, 12) == 0) + { + int _APPLMASK = 1 << App; + + // If App has an alias, assume it is running , unless a CMS alias - then check CMS + + if (APPL->APPLHASALIAS) + { + if (_memicmp(APPL->APPLCMD, "RELAY ", 6) == 0) + return APPL->APPLCALL_TEXT; // Assume people using RELAY know what they are doing + + if (APPL->APPLPORT && (_memicmp(APPL->APPLCMD, "RMS ", 4) == 0)) + { + APPLTNC = TNCInfo[APPL->APPLPORT]; + { + if (APPLTNC) + { + if (APPLTNC->TCPInfo && !APPLTNC->TCPInfo->CMSOK && !APPLTNC->TCPInfo->FallbacktoRelay) + return NULL; + } + } + } + return APPL->APPLCALL_TEXT; + } + + // See if App is running + + PORTVEC = &BPQHOSTVECTOR[0]; + + for (Stream = 0; Stream < 64; Stream++) + { + if (PORTVEC->HOSTAPPLMASK & _APPLMASK) + { + Allocated++; + + if (PORTVEC->HOSTSESSION == 0 && (PORTVEC->HOSTFLAGS & 3) == 0) + { + // Free and no outstanding report + + return APPL->APPLCALL_TEXT; // Running + } + } + PORTVEC++; + } + } + } + + return NULL; // Not Running +} + +VOID SetApplPorts() +{ + // If any appl has an alias, get port number + + struct APPLCONFIG * App; + APPLCALLS * APPL; + + char C[80]; + char Port[80]; + char Call[80]; + + int i, n; + + App = &xxcfg.C_APPL[0]; + + for (i=0; i < NumberofAppls; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLHASALIAS) + { + n = sscanf(App->CommandAlias, "%s %s %s", &C[0], &Port[0], &Call[0]); + if (n == 3) + APPL->APPLPORT = atoi(Port); + } + App++; + } +} + + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + + +char Modenames[19][10] = {"WINMOR", "SCS", "KAM", "AEA", "HAL", "TELNET", "TRK", + "V4", "UZ7HO", "MPSK", "FLDIGI", "UIARQ", "ARDOP", "VARA", + "SERIAL", "KISSHF", "WINRPR", "HSMODEM", "FREEDATA"}; + +BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT) +{ + return ProcessIncommingConnectEx(TNC, Call, Stream, SENDCTEXT, FALSE); +} + +BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR) +{ + TRANSPORTENTRY * Session; + int Index = 0; + PMSGWITHLEN buffptr; + int Totallen = 0; + UCHAR * ptr; + struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; + + // Stop Scanner + + if (Stream == 0 || TNC->Hardware == H_UZ7HO) + { + char Msg[80]; + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + UpdateMH(TNC, Call, '+', 'I'); + } + + Session = L4TABLE; + + // Find a free Circuit Entry + + while (Index < MAXCIRCUITS) + { + if (Session->L4USER[0] == 0) + break; + + Session++; + Index++; + } + + if (Index == MAXCIRCUITS) + return FALSE; // Tables Full + + memset(Session, 0, sizeof(TRANSPORTENTRY)); + + memcpy(TNC->Streams[Stream].RemoteCall, Call, 9); // Save Text Callsign + + if (AllowTR) + ConvToAX25Ex(Call, Session->L4USER); // Allow -T and -R SSID's for MPS + else + ConvToAX25(Call, Session->L4USER); + ConvToAX25(MYNODECALL, Session->L4MYCALL); + Session->CIRCUITINDEX = Index; + Session->CIRCUITID = NEXTID; + NEXTID++; + if (NEXTID == 0) NEXTID++; // Keep non-zero + + TNC->PortRecord->ATTACHEDSESSIONS[Stream] = Session; + TNC->Streams[Stream].Attached = TRUE; + + Session->L4TARGET.EXTPORT = TNC->PortRecord; + + Session->L4CIRCUITTYPE = UPLINK+PACTOR; + Session->L4WINDOW = L4DEFAULTWINDOW; + Session->L4STATE = 5; + Session->SESSIONT1 = L4T1; + Session->SESSPACLEN = TNC->PortRecord->PORTCONTROL.PORTPACLEN; + Session->KAMSESSION = Stream; + + TNC->Streams[Stream].Connected = TRUE; // Subsequent data to data channel + + if (LogAllConnects) + { + if (TNC->TargetCall[0]) + WriteConnectLog(Call, TNC->TargetCall, Modenames[TNC->Hardware - 1]); + else + WriteConnectLog(Call, MYNODECALL, Modenames[TNC->Hardware - 1]); + } + + if (SENDCTEXT == 0) + return TRUE; + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN > 0) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + else + return TRUE; + + while (Totallen > 0) + { + int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + + if (sendLen == 0) + sendLen = 80; + + if (Totallen < sendLen) + sendLen = Totallen; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return TRUE; // No buffers + + buffptr->Len = sendLen; + memcpy(&buffptr->Data[0], ptr, sendLen); + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + Totallen -= sendLen; + ptr += sendLen; + } + return TRUE; +} + +char * Config; +static char * ptr1, * ptr2; + +BOOL ReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + if (TNCInfo[Port]) // If restarting, free old config + free(TNCInfo[Port]); + + TNCInfo[Port] = NULL; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + // Empty Config File - OK for most types + + struct TNCINFO * TNC = TNCInfo[Port] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(2); + TNC->InitScript[0] = 0; + + return TRUE; + } + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1 + 1); + buf[ptr2 - ptr1 + 1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} +int GetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} +VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg) +{ + USHORT Mask=PORTVEC->DIGIMASK; + int i; + + for (i=1; i<=NUMBEROFPORTS; i++) + { + if (Mask & 1) + { + // Block includes the Msg Header (7/11 bytes), Len Does not! + + Msg->PORT = i; + Send_AX((UCHAR *)&Msg, Msg->LENGTH - MSGHDDRLEN, i); + Mask>>=1; + } + } +} + +int CompareAlias(struct DEST_LIST ** a, struct DEST_LIST ** b) +{ + return memcmp(a[0]->DEST_ALIAS, b[0]->DEST_ALIAS, 6); + /* strcmp functions works exactly as expected from comparison function */ +} + + +int CompareNode(struct DEST_LIST ** a, struct DEST_LIST ** b) +{ + return memcmp(a[0]->DEST_CALL, b[0]->DEST_CALL, 7); +} + +DllExport int APIENTRY CountFramesQueuedOnStream(int Stream) +{ + BPQVECSTRUC * PORTVEC = &BPQHOSTVECTOR[Stream-1]; // API counts from 1 + TRANSPORTENTRY * L4 = PORTVEC->HOSTSESSION; + + int Count = 0; + + if (L4) + { + if (L4->L4CROSSLINK) // CONNECTED? + Count = CountFramesQueuedOnSession(L4->L4CROSSLINK); + else + Count = CountFramesQueuedOnSession(L4); + } + return Count; +} + +DllExport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall) +{ + // Equivalent to "*** linked to" command + + memcpy(BPQHOSTVECTOR[Stream-1].HOSTSESSION->L4USER, AXCall, 7); + return (0); +} + +DllExport int APIENTRY ChangeSessionPaclen(int Stream, int Paclen) +{ + BPQHOSTVECTOR[Stream-1].HOSTSESSION->SESSPACLEN = Paclen; + return (0); +} + +DllExport int APIENTRY ChangeSessionIdletime(int Stream, int idletime) +{ + if (BPQHOSTVECTOR[Stream-1].HOSTSESSION) + BPQHOSTVECTOR[Stream-1].HOSTSESSION->L4LIMIT = idletime; + return (0); +} + +DllExport int APIENTRY Get_APPLMASK(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLMASK; +} +DllExport int APIENTRY GetStreamPID(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].STREAMOWNER; +} + +DllExport int APIENTRY GetApplFlags(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLFLAGS; +} + +DllExport int APIENTRY GetApplNum(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLNUM; +} + +DllExport int APIENTRY GetApplMask(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTAPPLMASK; +} + +DllExport BOOL APIENTRY GetAllocationState(int Stream) +{ + return BPQHOSTVECTOR[Stream-1].HOSTFLAGS & 0x80; +} + +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); + +extern int InitDone; +extern int SemHeldByAPI; +extern char pgm[256]; // Uninitialised so per process +extern int BPQHOSTAPI(); + + +VOID POSTSTATECHANGE(BPQVECSTRUC * SESS) +{ + // Post a message if requested +#ifndef LINBPQ + if (SESS->HOSTHANDLE) + PostMessage(SESS->HOSTHANDLE, BPQMsg, SESS->HOSTSTREAM, 4); +#endif + return; +} + + +DllExport int APIENTRY SessionControl(int stream, int command, int Mask) +{ + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return (0); + + SESS = &BPQHOSTVECTOR[stream]; + + // Send Session Control command (BPQHOST function 6) + //; CL=0 CONNECT USING APPL MASK IN DL + //; CL=1, CONNECT. CL=2 - DISCONNECT. CL=3 RETURN TO NODE + + if (command > 1) + { + // Disconnect + + if (SESS->HOSTSESSION == 0) + { + SESS->HOSTFLAGS |= 1; // State Change + POSTSTATECHANGE(SESS); + return 0; // NOT CONNECTED + } + + if (command == 3) + SESS->HOSTFLAGS |= 0x20; // Set Stay + + SESS->HOSTFLAGS |= 0x40; // SET 'DISC REQ' FLAG + + return 0; + } + + // 0 or 1 - connect + + if (SESS->HOSTSESSION) // ALREADY CONNECTED + { + SESS->HOSTFLAGS |= 1; // State Change + POSTSTATECHANGE(SESS); + return 0; + } + + // SET UP A SESSION FOR THE CONSOLE + + SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + + if (command == 1) // Zero is mask supplied by caller + Mask = SESS->HOSTAPPLMASK; // SO WE GET CORRECT CALLSIGN + + L4 = SetupSessionFromHost(SESS, Mask); + + if (L4 == 0) // tables Full + { + SESS->HOSTFLAGS |= 3; // State Change + POSTSTATECHANGE(SESS); + return 0; + } + + SESS->HOSTSESSION = L4; + L4->L4CIRCUITTYPE = BPQHOST | UPLINK; + L4->Secure_Session = AuthorisedProgram; // Secure Host Session + + SESS->HOSTFLAGS |= 1; // State Change + POSTSTATECHANGE(SESS); + return 0; // ALREADY CONNECTED +} + +int FindFreeStreamEx(int GetSem); + +int FindFreeStreamNoSem() +{ + return FindFreeStreamEx(0); +} + +DllExport int APIENTRY FindFreeStream() +{ + return FindFreeStreamEx(1); +} + +int FindFreeStreamEx(int GetSem) +{ + int stream, n; + BPQVECSTRUC * PORTVEC; + +// Returns number of first unused BPQHOST stream. If none available, +// returns 255. See API function 13. + + // if init has not yet been run, wait. + + while (InitDone == 0) + { + Debugprintf("Waiting for init to complete"); + Sleep(1000); + } + + if (InitDone == -1) // Init failed + exit(0); + + if (GetSem) + GetSemaphore(&Semaphore, 9); + + stream = 0; + n = 64; + + while (n--) + { + PORTVEC = &BPQHOSTVECTOR[stream++]; + if ((PORTVEC->HOSTFLAGS & 0x80) == 0) + { + PORTVEC->STREAMOWNER=GetCurrentProcessId(); + PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others + memcpy(&PORTVEC->PgmName[0], pgm, 31); + if (GetSem) + FreeSemaphore(&Semaphore); + return stream; + } + } + + if (GetSem) + FreeSemaphore(&Semaphore); + + return 255; +} + +DllExport int APIENTRY AllocateStream(int stream) +{ +// Allocate stream. If stream is already allocated, return nonzero. +// Otherwise allocate stream, and return zero. + + BPQVECSTRUC * PORTVEC = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + if ((PORTVEC->HOSTFLAGS & 0x80) == 0) + { + PORTVEC->STREAMOWNER=GetCurrentProcessId(); + PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others + memcpy(&PORTVEC->PgmName[0], pgm, 31); + FreeSemaphore(&Semaphore); + return 0; + } + + return 1; // Already allocated +} + + +DllExport int APIENTRY DeallocateStream(int stream) +{ + BPQVECSTRUC * PORTVEC; + UINT * monbuff; + BOOL GotSem = Semaphore.Flag; + +// Release stream. + + stream--; + + if (stream < 0 || stream > 63) + return (0); + + PORTVEC=&BPQHOSTVECTOR[stream]; + + PORTVEC->STREAMOWNER=0; + PORTVEC->PgmName[0] = 0; + PORTVEC->HOSTAPPLFLAGS=0; + PORTVEC->HOSTAPPLMASK=0; + PORTVEC->HOSTHANDLE=0; + + // Clear Trace Queue + + if (PORTVEC->HOSTSESSION) + SessionControl(stream + 1, 2, 0); + + if (GotSem == 0) + GetSemaphore(&Semaphore, 0); + + while (PORTVEC->HOSTTRACEQ) + { + monbuff = Q_REM((void *)&PORTVEC->HOSTTRACEQ); + ReleaseBuffer(monbuff); + } + + if (GotSem == 0) + FreeSemaphore(&Semaphore); + + PORTVEC->HOSTFLAGS &= 0x60; // Clear Allocated. Must leave any DISC Pending bits + + return(0); +} +DllExport int APIENTRY SessionState(int stream, int * state, int * change) +{ + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + Check_Timer(); // In case Appl doesnt call it often ehough + + GetSemaphore(&Semaphore, 20); + + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + // HOSTFLAGS = Bit 80 = Allocated + // Bit 40 = Disc Request + // Bit 20 = Stay Flag + // Bit 02 and 01 State Change Bits + + if ((HOST->HOSTFLAGS & 3) == 0) + // No Chaange + *change = 0; + else + *change = 1; + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bitd + + FreeSemaphore(&Semaphore); + return 0; +} + +DllExport int APIENTRY SessionStateNoAck(int stream, int * state) +{ + // Get current Session State. Dont ACK any change + // See BPQHOST function 4 + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + Check_Timer(); // In case Appl doesnt call it often ehough + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + return 0; +} + +DllExport int APIENTRY SendMsg(int stream, char * msg, int len) +{ + // Send message to stream (BPQHOST Function 2) + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + TRANSPORTENTRY * Partner; + PDATAMESSAGE MSG; + + Check_Timer(); + + if (len > 256) + return 0; // IGNORE + + if (stream == 0) + { + // Send UNPROTO - SEND FRAME TO ALL RADIO PORTS + + // COPY DATA TO A BUFFER IN OUR SEGMENTS - SIMPLFIES THINGS LATER + + if (QCOUNT < 50) + return 0; // Dont want to run out + + GetSemaphore(&Semaphore, 10); + + if ((MSG = GetBuff()) == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + MSG->PID = 0xF0; // Normal Data PID + + memcpy(&MSG->L2DATA[0], msg, len); + MSG->LENGTH = (len + MSGHDDRLEN + 1); + + SENDUIMESSAGE(MSG); + ReleaseBuffer(MSG); + FreeSemaphore(&Semaphore); + return 0; + } + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + if (L4 == 0) + return 0; + + GetSemaphore(&Semaphore, 22); + + SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + + if (QCOUNT < 40) // PLENTY FREE? + { + FreeSemaphore(&Semaphore); + return 1; + } + + // Dont allow massive queues to form + + if (QCOUNT < 100) + { + int n = CountFramesQueuedOnStream(stream + 1); + + if (n > 100) + { + Debugprintf("Stream %d QCOUNT %d Q Len %d - discarding", stream, QCOUNT, n); + FreeSemaphore(&Semaphore); + return 1; + } + } + + if ((MSG = GetBuff()) == 0) + { + FreeSemaphore(&Semaphore); + return 1; + } + + MSG->PID = 0xF0; // Normal Data PID + + memcpy(&MSG->L2DATA[0], msg, len); + MSG->LENGTH = len + MSGHDDRLEN + 1; + + // IF CONNECTED, PASS MESSAGE TO TARGET CIRCUIT - FLOW CONTROL AND + // DELAYED DISC ONLY WORK ON ONE SIDE + + Partner = L4->L4CROSSLINK; + + L4->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + + if (Partner && Partner->L4STATE > 4) // Partner and link up + { + // Connected + + Partner->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + C_Q_ADD(&Partner->L4TX_Q, MSG); + PostDataAvailable(Partner); + } + else + C_Q_ADD(&L4->L4RX_Q, MSG); + + FreeSemaphore(&Semaphore); + return 0; +} +DllExport int APIENTRY SendRaw(int port, char * msg, int len) +{ + struct PORTCONTROL * PORT; + MESSAGE * MSG; + + Check_Timer(); + + // Send Raw (KISS mode) frame to port (BPQHOST function 10) + + if (len > (MAXDATA - (MSGHDDRLEN + 8))) + return 0; + + if (QCOUNT < 50) + return 1; + + // GET A BUFFER + + PORT = GetPortTableEntryFromSlot(port); + + if (PORT == 0) + return 0; + + GetSemaphore(&Semaphore, 24); + + MSG = GetBuff(); + + if (MSG == 0) + { + FreeSemaphore(&Semaphore); + return 1; + } + + memcpy(MSG->DEST, msg, len); + + MSG->LENGTH = len + MSGHDDRLEN; + + if (PORT->PROTOCOL == 10) // PACTOR/WINMOR Style + { + // Pactor Style. Probably will only be used for Tracker uneless we do APRS over V4 or WINMOR + + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + C_Q_ADD(&EXTPORT->UI_Q, MSG); + + FreeSemaphore(&Semaphore); + return 0; + } + + MSG->PORT = PORT->PORTNUMBER; + + PUT_ON_PORT_Q(PORT, MSG); + + FreeSemaphore(&Semaphore); + return 0; +} + +DllExport time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count) +{ + time_t Stamp; + BPQVECSTRUC * SESS; + PMESSAGE MSG; + int Msglen; + + Check_Timer(); + + *len = 0; + *count = 0; + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + + GetSemaphore(&Semaphore, 26); + + if (SESS->HOSTTRACEQ == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + MSG = Q_REM((void *)&SESS->HOSTTRACEQ); + + Msglen = MSG->LENGTH; + + if (Msglen < 0 || Msglen > 350) + { + FreeSemaphore(&Semaphore); + return 0; + } + + Stamp = MSG->Timestamp; + + memcpy(msg, MSG, Msglen); + + *len = Msglen; + + ReleaseBuffer(MSG); + + *count = C_Q_COUNT(&SESS->HOSTTRACEQ); + FreeSemaphore(&Semaphore); + + return Stamp; +} + +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ) +{ +// Get message from stream. Returns length, and count of frames +// still waiting to be collected. (BPQHOST function 3) +// AH = 3 Receive frame into buffer at ES:DI, length of frame returned +// in CX. BX returns the number of outstanding frames still to +// be received (ie. after this one) or zero if no more frames +// (ie. this is last one). +// + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + PDATAMESSAGE MSG; + int Msglen; + + Check_Timer(); + + *len = 0; + *count = 0; + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + GetSemaphore(&Semaphore, 25); + + if (L4 == 0 || L4->L4TX_Q == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + L4->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + + if(L4->L4CROSSLINK) + L4->L4CROSSLINK->L4KILLTIMER = 0; + + MSG = Q_REM((void *)&L4->L4TX_Q); + + Msglen = MSG->LENGTH - (MSGHDDRLEN + 1); // Dont want PID + + if (Msglen < 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + if (Msglen > 256) + Msglen = 256; + + memcpy(msg, &MSG->L2DATA[0], Msglen); + + *len = Msglen; + + ReleaseBuffer(MSG); + + *count = C_Q_COUNT(&L4->L4TX_Q); + FreeSemaphore(&Semaphore); + + return 0; +} + + +DllExport int APIENTRY RXCount(int stream) +{ +// Returns count of packets waiting on stream +// (BPQHOST function 7 (part)). + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + if (L4 == 0) + return 0; // NOT CONNECTED + + return C_Q_COUNT(&L4->L4TX_Q); +} + +DllExport int APIENTRY TXCount(int stream) +{ +// Returns number of packets on TX queue for stream +// (BPQHOST function 7 (part)). + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + if (L4 == 0) + return 0; // NOT CONNECTED + + L4 = L4->L4CROSSLINK; + + if (L4 == 0) + return 0; // NOTHING ro Q on + + return (CountFramesQueuedOnSession(L4)); +} + +DllExport int APIENTRY MONCount(int stream) +{ +// Returns number of monitor frames available +// (BPQHOST function 7 (part)). + + BPQVECSTRUC * SESS; + + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + + return C_Q_COUNT(&SESS->HOSTTRACEQ); +} + + +DllExport int APIENTRY GetCallsign(int stream, char * callsign) +{ + // Returns call connected on stream (BPQHOST function 8 (part)). + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + TRANSPORTENTRY * Partner; + UCHAR Call[11] = "SWITCH "; + UCHAR * AXCall = NULL; + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + GetSemaphore(&Semaphore, 26); + + if (L4 == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + Partner = L4->L4CROSSLINK; + + if (Partner) + { + // CONNECTED OUT - GET TARGET SESSION + + if (Partner->L4CIRCUITTYPE & BPQHOST) + { + AXCall = &Partner->L4USER[0]; + } + else if (Partner->L4CIRCUITTYPE & L2LINK) + { + struct _LINKTABLE * LINK = Partner->L4TARGET.LINK; + + if (LINK) + AXCall = LINK->LINKCALL; + + if (Partner->L4CIRCUITTYPE & UPLINK) + { + // IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED + + AXCall = &Partner->L4USER[0]; + } + } + else if (Partner->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT; + + if (EXTPORT) + AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0]; + + } + else + { + // MUST BE NODE SESSION + + // ANOTHER NODE + + // IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL + + if (L4->L4CIRCUITTYPE & UPLINK) + { + struct DEST_LIST *DEST = Partner->L4TARGET.DEST; + + if (DEST) + AXCall = &DEST->DEST_CALL[0]; + } + else + AXCall = Partner->L4USER; + } + if (AXCall) + ConvFromAX25(AXCall, Call); + } + + memcpy(callsign, Call, 10); + + FreeSemaphore(&Semaphore); + return 0; +} + +DllExport int APIENTRY GetConnectionInfo(int stream, char * callsign, + int * port, int * sesstype, int * paclen, + int * maxframe, int * l4window) +{ + // Return the Secure Session Flag rather than not connected + + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4; + TRANSPORTENTRY * Partner; + UCHAR Call[11] = "SWITCH "; + UCHAR * AXCall; + Check_Timer(); + + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return 0; + + SESS = &BPQHOSTVECTOR[stream]; + L4 = SESS->HOSTSESSION; + + GetSemaphore(&Semaphore, 27); + + if (L4 == 0) + { + FreeSemaphore(&Semaphore); + return 0; + } + + Partner = L4->L4CROSSLINK; + + // Return the Secure Session Flag rather than not connected + + // AL = Radio port on which channel is connected (or zero) + // AH = SESSION TYPE BITS + // EBX = L2 paclen for the radio port + // ECX = L2 maxframe for the radio port + // EDX = L4 window size (if L4 circuit, or zero) or -1 if not connected + // ES:DI = CALLSIGN + + *port = 0; + *sesstype = 0; + *paclen = 0; + *maxframe = 0; + *l4window = 0; + if (L4->SESSPACLEN) + *paclen = L4->SESSPACLEN; + else + *paclen = 256; + + if (Partner) + { + // CONNECTED OUT - GET TARGET SESSION + + *l4window = Partner->L4WINDOW; + *sesstype = Partner->L4CIRCUITTYPE; + + if (Partner->L4CIRCUITTYPE & BPQHOST) + { + AXCall = &Partner->L4USER[0]; + } + else if (Partner->L4CIRCUITTYPE & L2LINK) + { + struct _LINKTABLE * LINK = Partner->L4TARGET.LINK; + + // EXTRACT PORT AND MAXFRAME + + *port = LINK->LINKPORT->PORTNUMBER; + *maxframe = LINK->LINKWINDOW; + *l4window = 0; + + AXCall = LINK->LINKCALL; + + if (Partner->L4CIRCUITTYPE & UPLINK) + { + // IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED + + AXCall = &Partner->L4USER[0]; + } + } + else if (Partner->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT; + + *port = EXTPORT->PORTCONTROL.PORTNUMBER; + AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0]; + + } + else + { + // MUST BE NODE SESSION + + // ANOTHER NODE + + // IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL + + if (L4->L4CIRCUITTYPE & UPLINK) + { + struct DEST_LIST *DEST = Partner->L4TARGET.DEST; + + AXCall = &DEST->DEST_CALL[0]; + } + else + AXCall = Partner->L4USER; + } + ConvFromAX25(AXCall, Call); + } + + memcpy(callsign, Call, 10); + + FreeSemaphore(&Semaphore); + + if (Partner) + return Partner->Secure_Session; + + return 0; +} + + +DllExport int APIENTRY SetAppl(int stream, int flags, int mask) +{ +// Sets Application Flags and Mask for stream. (BPQHOST function 1) +// AH = 1 Set application mask to value in EDX (or even DX if 16 +// applications are ever to be supported). +// +// Set application flag(s) to value in CL (or CX). +// whether user gets connected/disconnected messages issued +// by the node etc. + + + BPQVECSTRUC * PORTVEC; + stream--; + + if (stream < 0 || stream > 63) + return (0); + + PORTVEC=&BPQHOSTVECTOR[stream]; + + PORTVEC->HOSTAPPLFLAGS = flags; + PORTVEC->HOSTAPPLMASK = mask; + + // If either is non-zero, set allocated and Process. This gets round problem with + // stations that don't call allocate stream + + if (flags || mask) + { + if ((PORTVEC->HOSTFLAGS & 128) == 0) // Not allocated + { + PORTVEC->STREAMOWNER=GetCurrentProcessId(); + memcpy(&PORTVEC->PgmName[0], pgm, 31); + PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others + } + } + + return (0); +} + +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntry(int portslot) // Kept for Legacy apps +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + return PORTVEC; +} + +// Proc below renamed to avoid confusion with GetPortTableEntryFromPortNum + +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + return PORTVEC; +} + +struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + do + { + if (PORTVEC->PORTNUMBER == portnum) + return PORTVEC; + + PORTVEC=PORTVEC->PORTPOINTER; + } + while (PORTVEC); + + return NULL; +} + +DllExport UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + memcpy(Desc, PORTVEC->PORTDESCRIPTION, 30); + Desc[30]=0; + + return 0; +} + +// Standard serial port handling routines, used by lots of modules. + +int OpenCOMMPort(struct TNCINFO * conn, char * Port, int Speed, BOOL Quiet) +{ + if (conn->WEB_COMMSSTATE == NULL) + conn->WEB_COMMSSTATE = zalloc(100); + + if (Port == NULL) + return (FALSE); + + conn->hDevice = OpenCOMPort(Port, Speed, TRUE, TRUE, Quiet, 0); + + if (conn->hDevice == 0) + { + sprintf(conn->WEB_COMMSSTATE,"%s Open failed - Error %d", Port, GetLastError()); + if (conn->xIDC_COMMSSTATE) + SetWindowText(conn->xIDC_COMMSSTATE, conn->WEB_COMMSSTATE); + + return (FALSE); + } + + sprintf(conn->WEB_COMMSSTATE,"%s Open", Port); + + if (conn->xIDC_COMMSSTATE) + SetWindowText(conn->xIDC_COMMSSTATE, conn->WEB_COMMSSTATE); + + return TRUE; +} + + + +#ifdef WIN32 + +HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) +{ + char szPort[80]; + BOOL fRetVal ; + COMMTIMEOUTS CommTimeOuts ; + int Err; + char buf[100]; + HANDLE fd; + DCB dcb; + + // if Port Name starts COM, convert to \\.\COM or ports above 10 wont work + + if (_memicmp(pPort, "COM", 3) == 0) + { + char * pp = (char *)pPort; + int p = atoi(&pp[3]); + sprintf( szPort, "\\\\.\\COM%d", p); + } + else + strcpy(szPort, pPort); + + // open COMM device + + fd = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (fd == (HANDLE) -1) + { + if (Quiet == 0) + { + Debugprintf("%s could not be opened %d", pPort, GetLastError()); + } + return (FALSE); + } + + Err = GetFileType(fd); + + // setup device buffers + + SetupComm(fd, 4096, 4096 ) ; + + // purge any information in the buffer + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | + PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + + // set up for overlapped I/O + + CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ; + CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ; + CommTimeOuts.ReadTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ; +// CommTimeOuts.WriteTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutConstant = 500 ; + SetCommTimeouts(fd, &CommTimeOuts ) ; + + dcb.DCBlength = sizeof( DCB ) ; + + GetCommState(fd, &dcb ) ; + + dcb.BaudRate = speed; + dcb.ByteSize = 8; + dcb.Parity = 0; + dcb.StopBits = TWOSTOPBITS; + dcb.StopBits = Stopbits; + + // setup hardware flow control + + dcb.fOutxDsrFlow = 0; + dcb.fDtrControl = DTR_CONTROL_DISABLE ; + + dcb.fOutxCtsFlow = 0; + dcb.fRtsControl = RTS_CONTROL_DISABLE ; + + // setup software flow control + + dcb.fInX = dcb.fOutX = 0; + dcb.XonChar = 0; + dcb.XoffChar = 0; + dcb.XonLim = 100 ; + dcb.XoffLim = 100 ; + + // other various settings + + dcb.fBinary = TRUE ; + dcb.fParity = FALSE; + + fRetVal = SetCommState(fd, &dcb); + + if (fRetVal) + { + if (SetDTR) + EscapeCommFunction(fd, SETDTR); + else + EscapeCommFunction(fd, CLRDTR); + + if (SetRTS) + EscapeCommFunction(fd, SETRTS); + else + EscapeCommFunction(fd, CLRRTS); + } + else + { + sprintf(buf,"%s Setup Failed %d ", pPort, GetLastError()); + + WritetoConsoleLocal(buf); + OutputDebugString(buf); + CloseHandle(fd); + return 0; + } + + return fd; + +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength) +{ + BOOL Error; + return ReadCOMBlockEx(fd, Block, MaxLength, &Error); +} + +// version to pass read error back to caller + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error) +{ + BOOL fReadStat ; + COMSTAT ComStat ; + DWORD dwErrorFlags; + DWORD dwLength; + BOOL ret; + + if (fd == NULL) + return 0; + + // only try to read number of bytes in queue + + ret = ClearCommError(fd, &dwErrorFlags, &ComStat); + + if (ret == 0) + { + int Err = GetLastError(); + *Error = TRUE; + return 0; + } + + + dwLength = min((DWORD) MaxLength, ComStat.cbInQue); + + if (dwLength > 0) + { + fReadStat = ReadFile(fd, Block, dwLength, &dwLength, NULL) ; + + if (!fReadStat) + { + dwLength = 0 ; + ClearCommError(fd, &dwErrorFlags, &ComStat ) ; + } + } + + *Error = FALSE; + + return dwLength; +} + + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + BOOL fWriteStat; + DWORD BytesWritten; + DWORD ErrorFlags; + COMSTAT ComStat; + + fWriteStat = WriteFile(fd, Block, BytesToWrite, + &BytesWritten, NULL ); + + if ((!fWriteStat) || (BytesToWrite != BytesWritten)) + { + int Err = GetLastError(); + ClearCommError(fd, &ErrorFlags, &ComStat); + return FALSE; + } + return TRUE; +} + +VOID CloseCOMPort(HANDLE fd) +{ + if (fd == NULL) + return; + + SetCommMask(fd, 0); + + // drop DTR + + COMClearDTR(fd); + + // purge any outstanding reads/writes and close device handle + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + + CloseHandle(fd); + fd = NULL; +} + + +VOID COMSetDTR(HANDLE fd) +{ + EscapeCommFunction(fd, SETDTR); +} + +VOID COMClearDTR(HANDLE fd) +{ + EscapeCommFunction(fd, CLRDTR); +} + +VOID COMSetRTS(HANDLE fd) +{ + EscapeCommFunction(fd, SETRTS); +} + +VOID COMClearRTS(HANDLE fd) +{ + EscapeCommFunction(fd, CLRRTS); +} + + +#else + +static struct speed_struct +{ + int user_speed; + speed_t termios_speed; +} speed_table[] = { + {300, B300}, + {600, B600}, + {1200, B1200}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {-1, B0} +}; + + +HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) +{ + char Port[256]; + char buf[100]; + + // Linux Version. + + int fd; + int hwflag = 0; + u_long param=1; + struct termios term; + struct speed_struct *s; + + // As Serial ports under linux can have all sorts of odd names, the code assumes that + // they are symlinked to a com1-com255 in the BPQ Directory (normally the one it is started from + + if ((UINT)pPort < 256) + sprintf(Port, "%s/com%d", BPQDirectory, (int)pPort); + else + strcpy(Port, pPort); + + if ((fd = open(Port, O_RDWR | O_NDELAY)) == -1) + { + if (Quiet == 0) + { + perror("Com Open Failed"); + sprintf(buf," %s could not be opened \n", Port); + WritetoConsoleLocal(buf); + Debugprintf(buf); + } + return 0; + } + + // Validate Speed Param + + for (s = speed_table; s->user_speed != -1; s++) + if (s->user_speed == speed) + break; + + if (s->user_speed == -1) + { + fprintf(stderr, "tty_speed: invalid speed %d\n", speed); + return FALSE; + } + + if (tcgetattr(fd, &term) == -1) + { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfmakeraw(&term); + cfsetispeed(&term, s->termios_speed); + cfsetospeed(&term, s->termios_speed); + + if (tcsetattr(fd, TCSANOW, &term) == -1) + { + perror("tty_speed: tcsetattr"); + return FALSE; + } + + ioctl(fd, FIONBIO, ¶m); + + Debugprintf("LinBPQ Port %s fd %d", Port, fd); + + if (SetDTR) + { + COMSetDTR(fd); + } + else + { + COMClearDTR(fd); + } + + if (SetRTS) + { + COMSetRTS(fd); + } + else + { + COMClearRTS(fd); + } + return fd; +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength) +{ + BOOL Error; + return ReadCOMBlockEx(fd, Block, MaxLength, &Error); +} + +// version to pass read error back to caller + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error) +{ + int Length; + + if (fd == 0) + { + *Error = 1; + return 0; + } + + errno = 22222; // to catch zero read (?? file closed ??) + + Length = read(fd, Block, MaxLength); + + *Error = 0; + + if (Length == 0 && errno == 22222) // seems to be result of unpluging USB + { +// printf("KISS read returned zero len and no errno\n"); + *Error = 1; + return 0; + } + + if (Length < 0) + { + if (errno != 11 && errno != 35) // Would Block + { + perror("read"); + printf("Handle %d Errno %d Len %d\n", fd, errno, Length); + *Error = errno; + } + return 0; + } + + return Length; +} + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + // Some systems seem to have a very small max write size + + int ToSend = BytesToWrite; + int Sent = 0, ret; + + while (ToSend) + { + ret = write(fd, &Block[Sent], ToSend); + + if (ret >= ToSend) + return TRUE; + + if (ret == -1) + { + if (errno != 11 && errno != 35) // Would Block + return FALSE; + + usleep(10000); + ret = 0; + } + + Sent += ret; + ToSend -= ret; + } + return TRUE; +} + +VOID CloseCOMPort(HANDLE fd) +{ + if (fd == 0) + return; + + close(fd); + fd = 0; +} + +VOID COMSetDTR(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status |= TIOCM_DTR; + ioctl(fd, TIOCMSET, &status); +} + +VOID COMClearDTR(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status &= ~TIOCM_DTR; + ioctl(fd, TIOCMSET, &status); +} + +VOID COMSetRTS(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status |= TIOCM_RTS; + ioctl(fd, TIOCMSET, &status); +} + +VOID COMClearRTS(HANDLE fd) +{ + int status; + + ioctl(fd, TIOCMGET, &status); + status &= ~TIOCM_RTS; + ioctl(fd, TIOCMSET, &status); +} + +#endif + + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +FILE *file; + +int DoRoutes() +{ + char digis[30] = ""; + int count, len; + char Normcall[10], Portcall[10]; + char line[80]; + + for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + len=ConvFromAX25(Routes->NEIGHBOUR_CALL,Normcall); + Normcall[len]=0; + + if (Routes->NEIGHBOUR_DIGI1[0] != 0) + { + memcpy(digis," VIA ",5); + + len=ConvFromAX25(Routes->NEIGHBOUR_DIGI1,Portcall); + Portcall[len]=0; + strcpy(&digis[5],Portcall); + + if (Routes->NEIGHBOUR_DIGI2[0] != 0) + { + len=ConvFromAX25(Routes->NEIGHBOUR_DIGI2,Portcall); + Portcall[len]=0; + strcat(digis," "); + strcat(digis,Portcall); + } + } + else + digis[0] = 0; + + len=sprintf(line, + "ROUTE ADD %s %d %d %s %d %d %d %d %d\n", + Normcall, + Routes->NEIGHBOUR_PORT, + Routes->NEIGHBOUR_QUAL, digis, + Routes->NBOUR_MAXFRAME, + Routes->NBOUR_FRACK, + Routes->NBOUR_PACLEN, + Routes->INP3Node | (Routes->NoKeepAlive << 2), + Routes->OtherendsRouteQual); + + fputs(line, file); + } + + Routes+=1; + } + + return (0); +} + +int DoNodes() +{ + int count, len, cursor, i; + char Normcall[10], Portcall[10]; + char line[80]; + char Alias[7]; + + Dests-=1; + + for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0) + continue; + + { + len=ConvFromAX25(Dests->DEST_CALL,Normcall); + Normcall[len]=0; + + memcpy(Alias,Dests->DEST_ALIAS,6); + + Alias[6]=0; + + for (i=0;i<6;i++) + { + if (Alias[i] == ' ') + Alias[i] = 0; + } + + cursor=sprintf(line,"NODE ADD %s:%s ", Alias,Normcall); + + if (Dests->NRROUTE[0].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[0].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25( + Dests->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dests->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dests->NRROUTE[0].ROUT_QUALITY); + + cursor+=len; + + if (Dests->NRROUTE[0].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + + if (Dests->NRROUTE[1].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[1].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25( + Dests->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dests->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dests->NRROUTE[1].ROUT_QUALITY); + + cursor+=len; + + if (Dests->NRROUTE[1].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + + if (Dests->NRROUTE[2].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[2].ROUT_NEIGHBOUR->INP3Node == 0) + { + len=ConvFromAX25( + Dests->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d ", + Portcall, + Dests->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dests->NRROUTE[2].ROUT_QUALITY); + + cursor+=len; + + if (Dests->NRROUTE[2].ROUT_OBSCOUNT > 127) + { + len=sprintf(&line[cursor],"! "); + cursor+=len; + } + } + + if (cursor > 30) + { + line[cursor++]='\n'; + line[cursor++]=0; + fputs(line, file); + } + } + } + return (0); +} + +void SaveMH() +{ + char FN[250]; + struct PORTCONTROL * PORT = PORTTABLE; + FILE *file; + + if (BPQDirectory[0] == 0) + { + strcpy(FN, "MHSave.txt"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"MHSave.txt"); + } + + if ((file = fopen(FN, "w")) == NULL) + return; + + while (PORT) + { + int Port = 0; + char * ptr; + + MHSTRUC * MH = PORT->PORTMHEARD; + + int count = MHENTRIES; + int n; + char Normcall[20]; + char From[10]; + char DigiList[100]; + char * Output; + int len; + char Digi = 0; + + + // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find + // how many digis there are + + if (MH == NULL) + continue; + + fprintf(file, "Port:%d\n", PORT->PORTNUMBER); + + while (count--) + { + if (MH->MHCALL[0] == 0) + break; + + Digi = 0; + + len = ConvFromAX25(MH->MHCALL, Normcall); + Normcall[len] = 0; + + n = 8; // Max number of digi-peaters + + ptr = &MH->MHCALL[6]; // End of Address bit + + Output = &DigiList[0]; + + if ((*ptr & 1) == 0) + { + // at least one digi + + strcpy(Output, "via "); + Output += 4; + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + Output += sprintf((char *)Output, "%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + { + *(Output++) = '*'; + Digi = '*'; + } + + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + { + *(Output++) = '*'; // No, so need * + Digi = '*'; + } + + + } + *(Output++) = ','; + } + *(--Output) = 0; // remove last comma + } + else + *(Output) = 0; + + // if we used a digi set * on call and display via string + + + if (Digi) + Normcall[len++] = Digi; + else + DigiList[0] = 0; // Dont show list if not used + + Normcall[len++] = 0; + + ptr = FormatMH(MH, 'U'); + + ptr[15] = 0; + + if (MH->MHDIGI) + fprintf(file, "%d %6d %-10s%c %s %s|%s|%s\n", (int)MH->MHTIME, MH->MHCOUNT, Normcall, MH->MHDIGI, ptr, DigiList, MH->MHLocator, MH->MHFreq); + else + fprintf(file, "%d %6d %-10s%c %s %s|%s|%s\n", (int)MH->MHTIME, MH->MHCOUNT, Normcall, ' ', ptr, DigiList, MH->MHLocator, MH->MHFreq); + + MH++; + } + PORT = PORT->PORTPOINTER; + } + + fclose(file); + + return; +} + + +int APIENTRY SaveNodes () +{ + char FN[250]; + + Routes = NEIGHBOURS; + RouteLen = ROUTE_LEN; + MaxRoutes = MAXNEIGHBOURS; + + Dests = DESTS; + NodeLen = DEST_LIST_LEN; + MaxNodes = MAXDESTS; + + // Set up pointer to BPQNODES file + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"BPQNODES.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"BPQNODES.dat"); + } + + if ((file = fopen(FN, "w")) == NULL) + return FALSE; + + DoRoutes(); + DoNodes(); + + fclose(file); + + return (0); +} + +DllExport int APIENTRY ClearNodes () +{ + char FN[250]; + + // Set up pointer to BPQNODES file + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"BPQNODES.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"BPQNODES.dat"); + } + + if ((file = fopen(FN, "w")) == NULL) + return FALSE; + + fclose(file); + + return (0); +} +char * FormatUptime(int Uptime) + { + struct tm * TM; + static char UPTime[50]; + time_t szClock = Uptime * 60; + + TM = gmtime(&szClock); + + sprintf(UPTime, "Uptime (Days Hours Mins) %.2d:%.2d:%.2d\r", + TM->tm_yday, TM->tm_hour, TM->tm_min); + + return UPTime; + } + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + +char * FormatMH(PMHSTRUC MH, char Format) +{ + struct tm * TM; + static char MHTime[50]; + time_t szClock; + char LOC[7]; + + memcpy(LOC, MH->MHLocator, 6); + LOC[6] = 0; + + if (Format == 'U' || Format =='L') + szClock = MH->MHTIME; + else + szClock = time(NULL) - MH->MHTIME; + + if (Format == 'L') + TM = localtime(&szClock); + else + TM = gmtime(&szClock); + + if (Format == 'U' || Format =='L') + sprintf(MHTime, "%s %02d %.2d:%.2d:%.2d %s %s", + month[TM->tm_mon], TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec, MH->MHFreq, LOC); + else + sprintf(MHTime, "%.2d:%.2d:%.2d:%.2d %s %s", + TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, MH->MHFreq, LOC); + + return MHTime; + +} + + +Dll VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset) +{ + // Create a time dependent One Time Password from the KeyPhrase + // TimeOffset is used when checking to allow for slight variation in clocks + + time_t NOW = time(NULL); + UCHAR Hash[16]; + char Key[1000]; + int i, chr; + + NOW = NOW/30 + TimeOffset; // Only Change every 30 secs + + sprintf(Key, "%s%x", KeyPhrase, (int)NOW); + + md5(Key, Hash); + + for (i=0; i<16; i++) + { + chr = (Hash[i] & 31); + if (chr > 9) chr += 7; + + Password[i] = chr + 48; + } + + Password[16] = 0; + return; +} + +Dll BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase) +{ + char CheckPassword[17]; + int Offsets[10] = {0, -1, 1, -2, 2, -3, 3, -4, 4}; + int i, Pass; + + if (strlen(Password) < 16) + Pass = atoi(Password); + + for (i = 0; i < 9; i++) + { + CreateOneTimePassword(CheckPassword, KeyPhrase, Offsets[i]); + + if (strlen(Password) < 16) + { + // Using a numeric extract + + long long Val; + + memcpy(&Val, CheckPassword, 8); + Val = Val %= 1000000; + + if (Pass == Val) + return TRUE; + } + else + if (memcmp(Password, CheckPassword, 16) == 0) + return TRUE; + } + + return FALSE; +} + + +DllExport BOOL ConvToAX25Ex(unsigned char * callsign, unsigned char * ax25call) +{ + // Allows SSID's of 'T and 'R' + + int i; + + memset(ax25call,0x40,6); // in case short + ax25call[6]=0x60; // default SSID + + for (i=0;i<7;i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + + if (callsign[i+1] == 'T') + { + ax25call[6]=0x42; + return TRUE; + } + + if (callsign[i+1] == 'R') + { + ax25call[6]=0x44; + return TRUE; + } + i = atoi(&callsign[i+1]); + + if (i < 16) + { + ax25call[6] |= i<<1; + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + + +DllExport BOOL ConvToAX25(unsigned char * callsign, unsigned char * ax25call) +{ + int i; + + memset(ax25call,0x40,6); // in case short + ax25call[6]=0x60; // default SSID + + for (i=0;i<7;i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i+1]); + + if (i < 16) + { + ax25call[6] |= i<<1; + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + + +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall) +{ + int in,out=0; + unsigned char chr; + + memset(outcall,0x20,10); + + for (in=0;in<6;in++) + { + chr=incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++]=chr; + } + + chr=incall[6]; // ssid + + if (chr == 0x42) + { + outcall[out++]='-'; + outcall[out++]='T'; + return out; + } + + if (chr == 0x44) + { + outcall[out++]='-'; + outcall[out++]='R'; + return out; + } + + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++]='-'; + if (chr > 9) + { + chr-=10; + outcall[out++]='1'; + } + chr+=48; + outcall[out++]=chr; + } + return (out); +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +SOCKADDR_IN reportdest = {0}; + +SOCKET ReportSocket = 0; + +SOCKADDR_IN Chatreportdest = {0}; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + + +VOID SendReportMsg(char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ReportSocket, buff, txlen, 0, (LPSOCKADDR)&reportdest, sizeof(reportdest)); + +} +VOID SendLocation() +{ + MESSAGE AXMSG = {0}; + PMESSAGE AXPTR = &AXMSG; + char Msg[512]; + int Len; + + Len = sprintf(Msg, "%s %s
%s", LOCATOR, VersionString, MAPCOMMENT); + +#ifdef LINBPQ + Len = sprintf(Msg, "%s L%s
%s", LOCATOR, VersionString, MAPCOMMENT); +#endif +#ifdef MACBPQ + Len = sprintf(Msg, "%s M%s
%s", LOCATOR, VersionString, MAPCOMMENT); +#endif +#ifdef FREEBSD + Len = sprintf(Msg, "%s F%s
%s", LOCATOR, VersionString, MAPCOMMENT); +#endif + + if (Len > 256) + Len = 256; + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16); + + return; + +} + + + + +VOID SendMH(struct TNCINFO * TNC, char * call, char * freq, char * LOC, char * Mode) +{ + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[100]; + int Len; + + if (ReportSocket == 0 || LOCATOR[0] == 0) + return; + + Len = sprintf(Msg, "MH %s,%s,%s,%s", call, freq, LOC, Mode); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + if (TNC->PortRecord->PORTCONTROL.PORTCALL[0]) + memcpy(AXPTR->ORIGIN, TNC->PortRecord->PORTCONTROL.PORTCALL, 7); + else + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; + + return; + +} + +time_t TimeLastNRRouteSent = 0; + +char NRRouteMessage[256]; +int NRRouteLen = 0; + + +VOID SendNETROMRoute(struct PORTCONTROL * PORT, unsigned char * axcall) +{ + // Called to update Link Map when a NODES Broadcast is received + // Batch to reduce Load + + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300]; + int Len; + char Call[10]; + char Report[16]; + time_t Now = time(NULL); + int NeedSend = FALSE; + + + if (ReportSocket == 0 || LOCATOR[0] == 0) + return; + + Call[ConvFromAX25(axcall, Call)] = 0; + + sprintf(Report, "%s,%d,", Call, PORT->PORTTYPE); + + if (Now - TimeLastNRRouteSent > 60) + NeedSend = TRUE; + + if (strstr(NRRouteMessage, Report) == 0) // reported recently + strcat(NRRouteMessage, Report); + + if (strlen(NRRouteMessage) > 230 || NeedSend) + { + Len = sprintf(Msg, "LINK %s", NRRouteMessage); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; + + TimeLastNRRouteSent = Now; + NRRouteMessage[0] = 0; + } + + return; + +} + +DllExport char * APIENTRY GetApplCall(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return NULL; + + return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLCALL_TEXT); +} +DllExport char * APIENTRY GetApplAlias(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return NULL; + + return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT); +} + +DllExport int32_t APIENTRY GetApplQual(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return 0; + + return (APPLCALLTABLE[Appl-1].APPLQUAL); +} + +char * GetApplCallFromName(char * App) +{ + int i; + char PaddedAppl[13] = " "; + + memcpy(PaddedAppl, App, (int)strlen(App)); + + for (i = 0; i < NumberofAppls; i++) + { + if (memcmp(&APPLCALLTABLE[i].APPLCMD, PaddedAppl, 12) == 0) + return &APPLCALLTABLE[i].APPLCALL_TEXT[0]; + } + return NULL; +} + + +DllExport char * APIENTRY GetApplName(int Appl) +{ + if (Appl < 1 || Appl > NumberofAppls ) return NULL; + + return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLCMD); +} + +DllExport int APIENTRY GetNumberofPorts() +{ + return (NUMBEROFPORTS); +} + +DllExport int APIENTRY GetPortNumber(int portslot) +{ + struct PORTCONTROL * PORTVEC=PORTTABLE; + + if (portslot>NUMBEROFPORTS) + portslot=NUMBEROFPORTS; + + while (--portslot > 0) + PORTVEC=PORTVEC->PORTPOINTER; + + return PORTVEC->PORTNUMBER; + +} + +DllExport char * APIENTRY GetVersionString() +{ +// return ((char *)&VersionStringWithBuild); + return ((char *)&VersionString); +} + +#ifdef MACBPQ + +//Fiddle till I find a better solution + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1060 +int __sync_lock_test_and_set(int * ptr, int val) +{ + *ptr = val; + return 0; +} +#endif // __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ +#endif // MACBPQ + + + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // + + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } + +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + +#ifdef WIN32 + + { + if (InterlockedExchange(&Semaphore->Flag, 1) != 0) // Failed to get it + goto loop1; // try again;; + } + +#else + + if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0) + + // Failed to get it + goto loop1; // try again; + +#endif + + //Ok. got it + + Semaphore->Gets++; + Semaphore->SemProcessID = GetCurrentProcessId(); + Semaphore->SemThreadID = GetCurrentThreadId(); + SemHeldByAPI = ID; + + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + if (Semaphore->Flag == 0) + Debugprintf("Free Semaphore Called when Sem not held"); + + Semaphore->Rels++; + Semaphore->Flag = 0; + + return; +} + +#ifdef WIN32 + +#include "DbgHelp.h" +/* +USHORT WINAPI RtlCaptureStackBackTrace( + __in ULONG FramesToSkip, + __in ULONG FramesToCapture, + __out PVOID *BackTrace, + __out_opt PULONG BackTraceHash +); +*/ +#endif + +void printStack(void) +{ +#ifdef WIN32 +#ifdef _DEBUG // So we can use on 98/2K + + unsigned int i; + void * stack[ 100 ]; + unsigned short frames; + SYMBOL_INFO * symbol; + HANDLE process; + + Debugprintf("Stack Backtrace"); + + process = GetCurrentProcess(); + + SymInitialize( process, NULL, TRUE ); + + frames = RtlCaptureStackBackTrace( 0, 60, stack, NULL ); + symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 ); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof( SYMBOL_INFO ); + + for( i = 0; i < frames; i++ ) + { + SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol ); + + Debugprintf( "%i: %s - %p", frames - i - 1, symbol->Name, symbol->Address ); + } + + free(symbol); + +#endif +#endif +} + +pthread_t ResolveUpdateThreadId = 0; + +char NodeMapServer[80] = "update.g8bpq.net"; +char ChatMapServer[80] = "chatupdate.g8bpq.net"; + +VOID ResolveUpdateThread(void * Unused) +{ + struct hostent * HostEnt1; + struct hostent * HostEnt2; + + ResolveUpdateThreadId = GetCurrentThreadId(); + + while (TRUE) + { + if (pthread_equal(ResolveUpdateThreadId, GetCurrentThreadId()) == FALSE) + { + Debugprintf("Resolve Update thread %x redundant - closing", GetCurrentThreadId()); + return; + } + + // Resolve name to address + + Debugprintf("Resolving %s", NodeMapServer); + HostEnt1 = gethostbyname (NodeMapServer); +// HostEnt1 = gethostbyname ("192.168.1.64"); + + if (HostEnt1) + memcpy(&reportdest.sin_addr.s_addr,HostEnt1->h_addr,4); + + Debugprintf("Resolving %s", ChatMapServer); + HostEnt2 = gethostbyname (ChatMapServer); +// HostEnt2 = gethostbyname ("192.168.1.64"); + + if (HostEnt2) + memcpy(&Chatreportdest.sin_addr.s_addr,HostEnt2->h_addr,4); + + if (HostEnt1 && HostEnt2) + { + Sleep(1000 * 60 * 30); + continue; + } + + Debugprintf("Resolve Failed for update.g8bpq.net or chatmap.g8bpq.net"); + Sleep(1000 * 60 * 5); + } +} + + +VOID OpenReportingSockets() +{ + u_long param=1; + BOOL bcopt=TRUE; + + if (LOCATOR[0]) + { + // Enable Node Map Reports + + ReportTimer = 600; + + ReportSocket = socket(AF_INET,SOCK_DGRAM,0); + + if (ReportSocket == INVALID_SOCKET) + { + Debugprintf("Failed to create Reporting socket"); + ReportSocket = 0; + return; + } + + ioctlsocket (ReportSocket, FIONBIO, ¶m); + setsockopt (ReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); + + reportdest.sin_family = AF_INET; + reportdest.sin_port = htons(81); + ConvToAX25("DUMMY-1", ReportDest); + } + + // Set up Chat Report even if no LOCATOR reportdest.sin_family = AF_INET; + // Socket must be opened in MailChat Process + + Chatreportdest.sin_family = AF_INET; + Chatreportdest.sin_port = htons(81); + + _beginthread(ResolveUpdateThread, 0, NULL); +} + +VOID WriteMiniDumpThread(); + +time_t lastMiniDump = 0; + +void WriteMiniDump() +{ +#ifdef WIN32 + + _beginthread(WriteMiniDumpThread, 0, 0); + Sleep(3000); +} + +VOID WriteMiniDumpThread() +{ + HANDLE hFile; + BOOL ret; + char FN[256]; + struct tm * TM; + time_t Now = time(NULL); + + if (lastMiniDump == Now) // Not more than one per second + { + Debugprintf("minidump suppressed"); + return; + } + + lastMiniDump = Now; + + TM = gmtime(&Now); + + sprintf(FN, "%s/Logs/MiniDump%d%02d%02d%02d%02d%02d.dmp", BPQDirectory, + TM->tm_year + 1900, TM->tm_mon +1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + +// UI Util Code + +#pragma pack(1) + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEX * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + UCHAR PADDING[56]; // In case he have Digis + +}MESSAGEX, *PMESSAGEX; + +#pragma pack() + + +int PortNum[33] = {0}; // Tab nunber to port + +char * UIUIDigi[33]= {0}; +char * UIUIDigiAX[33] = {0}; // ax.25 version of digistring +int UIUIDigiLen[33] = {0}; // Length of AX string + +char UIUIDEST[33][11] = {0}; // Dest for Beacons + +char UIAXDEST[33][7] = {0}; + + +UCHAR FN[33][256]; // Filename +int Interval[33]; // Beacon Interval (Mins) +int MinCounter[33]; // Interval Countdown + +BOOL SendFromFile[33]; +char Message[33][1000]; // Beacon Text + +VOID SendUIBeacon(int Port); + +BOOL RunUI = TRUE; + +VOID UIThread(void * Unused) +{ + int Port, MaxPorts = GetNumberofPorts(); + + Sleep(60000); + + while (RunUI) + { + int sleepInterval = 60000; + + for (Port = 1; Port <= MaxPorts; Port++) + { + if (MinCounter[Port]) + { + MinCounter[Port]--; + + if (MinCounter[Port] == 0) + { + MinCounter[Port] = Interval[Port]; + SendUIBeacon(Port); + + // pause beteen beacons but adjust sleep interval to suit + + Sleep(10000); + sleepInterval -= 10000; + } + } + } + + while (sleepInterval <= 0) // just in case we have a crazy config + sleepInterval += 60000; + + Sleep(sleepInterval); + } +} + +int UIRemoveLF(char * Message, int len) +{ + // Remove lf chars + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + + +VOID UISend_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue) +{ + MESSAGEX AXMSG; + PMESSAGEX AXPTR = &AXMSG; + int DataLen = Len; + struct PORTCONTROL * PORT = GetPortTableEntryFromSlot(Port); + + // Block includes the Msg Header (7 or 11 bytes), Len Does not! + + memcpy(AXPTR->DEST, HWADDR, 7); + + // Get BCALL or PORTCALL if set + + if (PORT && PORT->PORTBCALL[0]) + memcpy(AXPTR->ORIGIN, PORT->PORTBCALL, 7); + else if (PORT && PORT->PORTCALL[0]) + memcpy(AXPTR->ORIGIN, PORT->PORTCALL, 7); + else + memcpy(AXPTR->ORIGIN, MYCALL, 7); + + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + if (UIUIDigi[Port]) + { + // This port has a digi string + + int DigiLen = UIUIDigiLen[Port]; + UCHAR * ptr; + + memcpy(&AXPTR->CTL, UIUIDigiAX[Port], DigiLen); + + ptr = (UCHAR *)AXPTR; + ptr += DigiLen; + AXPTR = (PMESSAGEX)ptr; + + Len += DigiLen; + } + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->DATA, Msg, DataLen); + +// if (Queue) +// QueueRaw(Port, &AXMSG, Len + 16); +// else + SendRaw(Port, (char *)&AXMSG.DEST, Len + 16); + + return; + +} + + + +VOID SendUIBeacon(int Port) +{ + char UIMessage[1024]; + int Len = (int)strlen(Message[Port]); + int Index = 0; + + if (SendFromFile[Port]) + { + FILE * hFile; + + hFile = fopen(FN[Port], "rb"); + + if (hFile == 0) + return; + + Len = (int)fread(UIMessage, 1, 1024, hFile); + + fclose(hFile); + + } + else + strcpy(UIMessage, Message[Port]); + + Len = UIRemoveLF(UIMessage, Len); + + while (Len > 256) + { + UISend_AX_Datagram(&UIMessage[Index], 256, Port, UIAXDEST[Port], TRUE); + Index += 256; + Len -= 256; + Sleep(2000); + } + UISend_AX_Datagram(&UIMessage[Index], Len, Port, UIAXDEST[Port], TRUE); +} + +#ifndef LINBPQ + +typedef struct tag_dlghdr +{ + HWND hwndTab; // tab control + HWND hwndDisplay; // current child dialog box + RECT rcDisplay; // display rectangle for the tab control + + DLGTEMPLATE *apRes[33]; + +} DLGHDR; + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName); + +#endif + +HWND hwndDlg; +int PageCount; +int CurrentPage=0; // Page currently on show in tabbed Dialog + + +VOID WINAPI OnSelChanged(HWND hwndDlg); +VOID WINAPI OnChildDialogInit(HWND hwndDlg); + +#define ICC_STANDARD_CLASSES 0x00004000 + +HWND hwndDisplay; + +#define ID_TEST 102 +#define IDD_DIAGLOG1 103 +#define IDC_FROMFILE 1022 +#define IDC_EDIT1 1054 +#define IDC_FILENAME 1054 +#define IDC_EDIT2 1055 +#define IDC_MESSAGE 1055 +#define IDC_EDIT3 1056 +#define IDC_INTERVAL 1056 +#define IDC_EDIT4 1057 +#define IDC_UIDEST 1057 +#define IDC_FILE 1058 +#define IDC_TAB1 1059 +#define IDC_UIDIGIS 1059 +#define IDC_PORTNAME 1060 + +extern HKEY REGTREE; +HBRUSH bgBrush; + +VOID SetupUI(int Port) +{ + char DigiString[100], * DigiLeft; + + ConvToAX25(UIUIDEST[Port], &UIAXDEST[Port][0]); + + UIUIDigiLen[Port] = 0; + + if (UIUIDigi[Port]) + { + UIUIDigiAX[Port] = zalloc(100); + strcpy(DigiString, UIUIDigi[Port]); + DigiLeft = strlop(DigiString,','); + + while(DigiString[0]) + { + ConvToAX25(DigiString, &UIUIDigiAX[Port][UIUIDigiLen[Port]]); + UIUIDigiLen[Port] += 7; + + if (DigiLeft) + { + memmove(DigiString, DigiLeft, (int)strlen(DigiLeft) + 1); + DigiLeft = strlop(DigiString,','); + } + else + DigiString[0] = 0; + } + } +} + +#ifndef LINBPQ + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + +#endif + +config_t cfg; + +VOID SaveUIConfig() +{ + config_setting_t *root, *group, *UIGroup; + int Port, MaxPort = GetNumberofPorts(); + char ConfigName[256]; + + if (BPQDirectory[0] == 0) + { + strcpy(ConfigName,"UIUtil.cfg"); + } + else + { + strcpy(ConfigName,BPQDirectory); + strcat(ConfigName,"/"); + strcat(ConfigName,"UIUtil.cfg"); + } + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + UIGroup = config_setting_add(group, "UIUtil", CONFIG_TYPE_GROUP); + + for (Port = 1; Port <= MaxPort; Port++) + { + char Key[20]; + + sprintf(Key, "Port%d", Port); + group = config_setting_add(UIGroup, Key, CONFIG_TYPE_GROUP); + + SaveStringValue(group, "UIDEST", &UIUIDEST[Port][0]); + SaveStringValue(group, "FileName", &FN[Port][0]); + SaveStringValue(group, "Message", &Message[Port][0]); + SaveStringValue(group, "Digis", UIUIDigi[Port]); + + SaveIntValue(group, "Interval", Interval[Port]); + SaveIntValue(group, "SendFromFile", SendFromFile[Port]); + } + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + +VOID GetUIConfig() +{ +#ifdef LINBPQ + + char Key[100]; + char CfgFN[256]; + char Digis[100]; + struct stat STAT; + + config_t cfg; + config_setting_t *group; + int Port, MaxPort = GetNumberofPorts(); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if (BPQDirectory[0] == 0) + { + strcpy(CfgFN,"UIUtil.cfg"); + } + else + { + strcpy(CfgFN,BPQDirectory); + strcat(CfgFN,"/"); + strcat(CfgFN,"UIUtil.cfg"); + } + + if (stat(CfgFN, &STAT) == -1) + { + Debugprintf("UIUtil Config File not found\n"); + return; + } + + if(!config_read_file(&cfg, CfgFN)) + { + fprintf(stderr, "UI Util Config Error Line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group) + { + for (Port = 1; Port <= MaxPort; Port++) + { + sprintf(Key, "main.UIUtil.Port%d", Port); + + group = config_lookup (&cfg, Key); + + if (group) + { + GetStringValue(group, "UIDEST", &UIUIDEST[Port][0]); + GetStringValue(group, "FileName", &FN[Port][0]); + GetStringValue(group, "Message", &Message[Port][0]); + GetStringValue(group, "Digis", Digis); + UIUIDigi[Port] = _strdup(Digis); + + Interval[Port] = GetIntValue(group, "Interval"); + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = GetIntValue(group, "SendFromFile"); + + SetupUI(Port); + } + } + } + +#else + + int retCode, Vallen, Type, i; + char Key[80]; + char Size[80]; + HKEY hKey; + RECT Rect; + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil"); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom); + + RegCloseKey(hKey); + } + + for (i=1; i<=32; i++) + { + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", i); + + retCode = RegOpenKeyEx (REGTREE, + Key, + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=0; + RegQueryValueEx(hKey,"Digis",0, + (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if (Vallen) + { + UIUIDigi[i] = malloc(Vallen); + RegQueryValueEx(hKey,"Digis",0, + (ULONG *)&Type, UIUIDigi[i], (ULONG *)&Vallen); + } + + Vallen=4; + retCode = RegQueryValueEx(hKey, "Interval", 0, + (ULONG *)&Type, (UCHAR *)&Interval[i], (ULONG *)&Vallen); + + MinCounter[i] = Interval[i]; + + Vallen=4; + retCode = RegQueryValueEx(hKey, "SendFromFile", 0, + (ULONG *)&Type, (UCHAR *)&SendFromFile[i], (ULONG *)&Vallen); + + + Vallen=10; + retCode = RegQueryValueEx(hKey, "UIDEST", 0, &Type, &UIUIDEST[i][0], &Vallen); + + Vallen=255; + retCode = RegQueryValueEx(hKey, "FileName", 0, &Type, &FN[i][0], &Vallen); + + Vallen=999; + retCode = RegQueryValueEx(hKey, "Message", 0, &Type, &Message[i][0], &Vallen); + + SetupUI(i); + + RegCloseKey(hKey); + } + } + + SaveUIConfig(); + +#endif + + _beginthread(UIThread, 0, NULL); + +} + +#ifndef LINBPQ + +INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ +// This processes messages from controls on the tab subpages + int Command; + + int retCode, disp; + char Key[80]; + HKEY hKey; + BOOL OK; + OPENFILENAME ofn; + char Digis[100]; + + int Port = PortNum[CurrentPage]; + + + switch (message) + { + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hDlg); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + case WM_INITDIALOG: + OnChildDialogInit( hDlg); + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + if (Command == 2002) + return TRUE; + + switch (Command) + { + case IDC_FILE: + + memset(&ofn, 0, sizeof (OPENFILENAME)); + ofn.lStructSize = sizeof (OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = &FN[Port][0]; + ofn.nMaxFile = 250; + ofn.lpstrTitle = "File to send as beacon"; + ofn.lpstrInitialDir = BPQDirectory; + + if (GetOpenFileName(&ofn)) + SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]); + + break; + + + case IDOK: + + GetDlgItemText(hDlg, IDC_UIDEST, &UIUIDEST[Port][0], 10); + + if (UIUIDigi[Port]) + { + free(UIUIDigi[Port]); + UIUIDigi[Port] = NULL; + } + + if (UIUIDigiAX[Port]) + { + free(UIUIDigiAX[Port]); + UIUIDigiAX[Port] = NULL; + } + + GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99); + + UIUIDigi[Port] = _strdup(Digis); + + GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255); + GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000); + + Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE); + + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE); + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIUIDEST[Port][0], (int)strlen(&UIUIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], (int)strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], (int)strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, (int)strlen(Digis)); + + RegCloseKey(hKey); + } + + SetupUI(Port); + + SaveUIConfig(); + + return (INT_PTR)TRUE; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case ID_TEST: + + SendUIBeacon(Port); + return TRUE; + + } + break; + + } + return (INT_PTR)FALSE; +} + + + +VOID WINAPI OnTabbedDialogInit(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); + DWORD dwDlgBase = GetDialogBaseUnits(); + int cxMargin = LOWORD(dwDlgBase) / 4; + int cyMargin = HIWORD(dwDlgBase) / 8; + + TC_ITEM tie; + RECT rcTab; + + int i, pos, tab = 0; + INITCOMMONCONTROLSEX init; + + char PortNo[60]; + struct _EXTPORTDATA * PORTVEC; + + hwndDlg = hDlg; // Save Window Handle + + // Save a pointer to the DLGHDR structure. + +#define GWL_USERDATA (-21) + + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); + + // Create the tab control. + + + init.dwICC = ICC_STANDARD_CLASSES; + init.dwSize=sizeof(init); + i=InitCommonControlsEx(&init); + + pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, 100, 100, hwndDlg, NULL, hInstance, NULL); + + if (pHdr->hwndTab == NULL) { + + // handle error + + } + + // Add a tab for each of the child dialog boxes. + + tie.mask = TCIF_TEXT | TCIF_IMAGE; + + tie.iImage = -1; + + for (i = 1; i <= NUMBEROFPORTS; i++) + { + // Only allow UI on ax.25 ports + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + if (PORTVEC->PORTCONTROL.UICAPABLE == 0) + continue; + + wsprintf(PortNo, "Port %2d", GetPortNumber(i)); + PortNum[tab] = i; + + tie.pszText = PortNo; + TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie); + + pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE"); + } + + PageCount = tab; + + // Determine the bounding rectangle for all child dialog boxes. + + SetRectEmpty(&rcTab); + + for (i = 0; i < PageCount; i++) + { + if (pHdr->apRes[i]->cx > rcTab.right) + rcTab.right = pHdr->apRes[i]->cx; + + if (pHdr->apRes[i]->cy > rcTab.bottom) + rcTab.bottom = pHdr->apRes[i]->cy; + + } + + MapDialogRect(hwndDlg, &rcTab); + +// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; + +// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; + + // Calculate how large to make the tab control, so + + // the display area can accomodate all the child dialog boxes. + + TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); + + OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); + + // Calculate the display rectangle. + + CopyRect(&pHdr->rcDisplay, &rcTab); + + TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); + + // Set the size and position of the tab control, buttons, + + // and dialog box. + + SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); + + // Move the Buttons to bottom of page + + pos=rcTab.left+cxMargin; + + + // Size the dialog box. + + SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), + rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), + SWP_NOMOVE | SWP_NOZORDER); + + // Simulate selection of the first item. + + OnSelChanged(hwndDlg); + +} + +// DoLockDlgRes - loads and locks a dialog template resource. + +// Returns a pointer to the locked resource. + +// lpszResName - name of the resource + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) +{ + HRSRC hrsrc = FindResource(hInstance, lpszResName, RT_DIALOG); + HGLOBAL hglb = LoadResource(hInstance, hrsrc); + + return (DLGTEMPLATE *) LockResource(hglb); +} + +//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. + +// OnSelChanged - processes the TCN_SELCHANGE notification. + +// hwndDlg - handle of the parent dialog box + +VOID WINAPI OnSelChanged(HWND hwndDlg) +{ + char PortDesc[40]; + int Port; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); + + // Destroy the current child dialog box, if any. + + if (pHdr->hwndDisplay != NULL) + + DestroyWindow(pHdr->hwndDisplay); + + // Create the new child dialog box. + + pHdr->hwndDisplay = CreateDialogIndirect(hInstance, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); + + hwndDisplay = pHdr->hwndDisplay; // Save + + Port = PortNum[CurrentPage]; + // Fill in the controls + + GetPortDescription(PortNum[CurrentPage], PortDesc); + + SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc); + + CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]); + + SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE); + + SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIUIDEST[Port][0]); + SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIUIDigi[Port]); + + + + SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]); + SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]); + + ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); + +} + + +//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. + +// OnChildDialogInit - Positions the child dialog box to fall + +// within the display area of the tab control. + +VOID WINAPI OnChildDialogInit(HWND hwndDlg) +{ + HWND hwndParent = GetParent(hwndDlg); + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); + + SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + HKEY hKey=0; + + switch (message) { + + case WM_INITDIALOG: + OnTabbedDialogInit(hWnd); + return (INT_PTR)TRUE; + + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hWnd); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)bgBrush; + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) { + + case IDOK: + + return TRUE; + + default: + + return 0; + } + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_RESTORE: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case SC_MINIMIZE: + + if (MinimizetoTray) + return ShowWindow(hWnd, SW_HIDE); + else + return (DefWindowProc(hWnd, message, wParam, lParam)); + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_CLOSE: + return(DestroyWindow(hWnd)); + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + + return (0); +} + +#endif + +extern struct DATAMESSAGE * REPLYBUFFER; +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); +void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + char FN[250]; + FILE *hFile; + struct stat STAT; + struct PORTCONTROL * PORT = PORTTABLE; + char PortList[256] = ""; + + while (PORT) + { + if (PORT->CTEXT) + { + free(PORT->CTEXT); + PORT->CTEXT = 0; + } + + if (BPQDirectory[0] == 0) + sprintf(FN, "Port%dCTEXT.txt", PORT->PORTNUMBER); + else + sprintf(FN, "%s/Port%dCTEXT.txt", BPQDirectory, PORT->PORTNUMBER); + + if (stat(FN, &STAT) == -1) + { + PORT = PORT->PORTPOINTER; + continue; + } + + hFile = fopen(FN, "rb"); + + if (hFile) + { + char * ptr; + + PORT->CTEXT = zalloc(STAT.st_size + 1); + fread(PORT->CTEXT , 1, STAT.st_size, hFile); + fclose(hFile); + + // convert CRLF or LF to CR + + while (ptr = strstr(PORT->CTEXT, "\r\n")) + memmove(ptr, ptr + 1, strlen(ptr)); + + // Now has LF + + while (ptr = strchr(PORT->CTEXT, '\n')) + *ptr = '\r'; + + + sprintf(PortList, "%s,%d", PortList, PORT->PORTNUMBER); + } + + PORT = PORT->PORTPOINTER; + } + + if (Session) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "CTEXT Read for ports %s\r", &PortList[1]); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + } + else + Debugprintf("CTEXT Read for ports %s\r", &PortList[1]); +} + + + + + diff --git a/ConfigDirewolf.c b/ConfigDirewolf.c new file mode 100644 index 0000000..3858570 --- /dev/null +++ b/ConfigDirewolf.c @@ -0,0 +1,131 @@ + +// +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + + +#include +#include +#include +#include + + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +int CaptureCount = 0; +int PlaybackCount = 0; + +int CaptureIndex = -1; // Card number +int PlayBackIndex = -1; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + +void main(int argc, char * argv[]) +{ + int i; + FILE *file; + + + if (argc < 3) + return; + + _strupr(argv[1]); + _strupr(argv[2]); + + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * CaptureCount); + CaptureDevices[0] = 0; + + printf("Capture Devices\r\n"); + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCaps((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPS)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + printf("%d %s\r\n", i, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + printf("\r\n"); + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + printf("Playback Devices\r\n"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + printf("%i %s\r\n", i, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], argv[1])) + { + CaptureIndex = i; + break; + } + } + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], argv[2])) + { + PlayBackIndex = i; + break; + } + } + + CopyFile("direwolf.in", "direwolf.conf", 0); + + + if ((file = fopen("direwolf.conf", "ab")) == NULL) + return; + + + printf("ADEVICE %d %d\r\n", CaptureIndex, PlayBackIndex); + fprintf(file, "ADEVICE %d %d\r\n", CaptureIndex, PlayBackIndex); + + fclose(file); + + printf("File updated"); + + + +} + + + + + diff --git a/ConfigDirewolf.vcxproj b/ConfigDirewolf.vcxproj new file mode 100644 index 0000000..6f13490 --- /dev/null +++ b/ConfigDirewolf.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {271FDA0A-4F41-4F35-8227-9F2F29AA5A25} + Win32Proj + ConsoleApplication2 + 10.0.17763.0 + ConfigDirewolf + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + true + true + c:\devprogs\bpq32\ConfigDirewolf.exe + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/ConfigDirewolf.vcxproj.user b/ConfigDirewolf.vcxproj.user new file mode 100644 index 0000000..b7d26ea --- /dev/null +++ b/ConfigDirewolf.vcxproj.user @@ -0,0 +1,11 @@ + + + + Old Old + WindowsLocalDebugger + + + Old Old + WindowsLocalDebugger + + \ No newline at end of file diff --git a/ConfigSDR.c b/ConfigSDR.c new file mode 100644 index 0000000..74788c4 --- /dev/null +++ b/ConfigSDR.c @@ -0,0 +1,121 @@ + +// +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + + +#include +#include +#include +#include + + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +int CaptureCount = 0; +int PlaybackCount = 0; + +int IndexA = -1; // Card number +int IndexB = -1; // Card number +int IndexC = -1; // Card number + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + +void main(int argc, char * argv[]) +{ + int i; + FILE *infile; + FILE *file; + + char line[1024] = ""; + char index[16]; + char * ptr; + + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + printf("Playback Devices\r\n"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + printf("%i %s\r\n", i, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + + + printf("\r\n"); + + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], "CABLE-A")) + IndexA = i; + else if(strstr(&PlaybackNames[i][0], "CABLE-B")) + IndexB = i; + else if (strstr(&PlaybackNames[i][0], "CABLE-C")) + IndexC = i; + } + + if ((infile = fopen("C:\\Users\\johnw\\AppData\\Roaming\\SDRplay\\SDRuno.in", "rb")) == NULL) + return; + + if ((file = fopen("C:\\Users\\johnw\\AppData\\Roaming\\SDRplay\\SDRuno.ini", "wb")) == NULL) + return; + + while ((fgets(line, 1023, infile))) + { + if (ptr = strstr(line, "CABLE-A")) + { + *ptr = 0; + sprintf(index, "%d\r\n", IndexA); + strcat(line, index); + } + if (ptr = strstr(line, "CABLE-B")) + { + *ptr = 0; + sprintf(index, "%d\r\n", IndexB); + strcat(line, index); + } + if (ptr = strstr(line, "CABLE-C")) + { + *ptr = 0; + sprintf(index, "%d\r\n", IndexC); + strcat(line, index); + } + fprintf(file, line); + } + + + fclose(file); + fclose(infile); + + printf("File updated"); + + + +} + diff --git a/ConfigWinRPR.c b/ConfigWinRPR.c new file mode 100644 index 0000000..a47a7ea --- /dev/null +++ b/ConfigWinRPR.c @@ -0,0 +1,137 @@ + +// +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + + +#include +#include +#include +#include + + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +int CaptureCount = 0; +int PlaybackCount = 0; + +int CaptureIndex = -1; // Card number +int PlayBackIndex = -1; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + +void main(int argc, char * argv[]) +{ + int i; + FILE *file; + + + if (argc < 3) + return; + + _strupr(argv[1]); + _strupr(argv[2]); + + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * CaptureCount); + CaptureDevices[0] = 0; + + printf("Capture Devices\r\n"); + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCaps((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPS)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + printf("%d %s\r\n", i + 1, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + printf("\r\n"); + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + printf("Playback Devices\r\n"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + printf("%i %s\r\n", i + 1, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], argv[1])) + { + CaptureIndex = i; + break; + } + } + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], argv[2])) + { + PlayBackIndex = i; + break; + } + } + + + + printf("RX:%d\r\nTX:%d\r\n", CaptureIndex + 1, PlayBackIndex + 1); + + + CopyFile("WinRPR_Config.in", "WinRPR_Config.txt", 0); + + + if ((file = fopen("WinRPR_Config.txt", "ab")) == NULL) + return; + + fprintf(file, "[AUDIO]\r\n"); + + fprintf(file, "RX:%d\r\nTX:%d\r\n", CaptureIndex + 1, PlayBackIndex + 1); + + fprintf(file, "[END]\r\n"); + fclose(file); + + printf("File updated"); + + + +} + + + + + diff --git a/ConfigWinRPR.vcxproj b/ConfigWinRPR.vcxproj new file mode 100644 index 0000000..9e66f98 --- /dev/null +++ b/ConfigWinRPR.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {2AD91A25-DF1B-462E-8CCB-CD1ABC627AA5} + Win32Proj + ConsoleApplication2 + 10.0.17763.0 + ConfigWinRPR + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + true + true + c:\devprogs\bpq32\ConfigWinRPR.exe + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + pch.h + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/ConfigWinRPR.vcxproj.user b/ConfigWinRPR.vcxproj.user new file mode 100644 index 0000000..6e2aec7 --- /dev/null +++ b/ConfigWinRPR.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/DOSAPI.c b/DOSAPI.c new file mode 100644 index 0000000..49db0f1 --- /dev/null +++ b/DOSAPI.c @@ -0,0 +1,406 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Implements the DOS register based API. + +// Called via an assmbler glue that puts registers into C variables. + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" + +#include "CHeaders.h" + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern int MAJORVERSION; +extern int MINORVERSION; +extern char pgm[256]; // Uninitialised so per process + +VOID PostDataAvailable(TRANSPORTENTRY * Session); +DllExport int APIENTRY SendMsg(int stream, char * msg, int len); +DllExport int APIENTRY AllocateStream(int stream); +DllExport int APIENTRY SendRaw(int port, char * msg, int len); +DllExport time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +VOID SENDNODESMSG(); + +int BTLENGTH; +char BTEXTFLD[256]; +int REALTIMETICKS; + +VOID CHOSTAPI(ULONG * pEAX, ULONG * pEBX, ULONG * pECX, ULONG * pEDX, VOID ** pESI, VOID ** pEDI) +{ + ULONG EAX = *pEAX; + ULONG EBX = *pEBX; + ULONG ECX = *pECX; + ULONG EDX = *pEDX; + VOID * ESI = *pESI; + VOID * EDI = *pEDI; + + int Command; + int Stream; + int n; + int Temp; + PBPQVECSTRUC HostVec; + TRANSPORTENTRY * Session; + +/* +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH = major version number and AL = minor version number, +; and user's buffer pointed to by ES:ESI is set to the text +; string normally output by the USERS command, eg: +; "G8BPQ Packet Switch Version 4.01 Dev". CX is set to the +; length of the text string. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:ESI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:ESI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:EDI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:ESI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:ESI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:EDI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:ESI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer +; + +*/ + + Command = (EAX & 0xFFFF) >> 8; + + Stream = (EAX & 0xFF); + n = Stream - 1; // API Numbers Streams 1-64 + + if (n < 0 || n > 63) + n = 64; + + HostVec = &BPQHOSTVECTOR[n]; + Session = HostVec->HOSTSESSION; + + switch (Command) + { + case 0: // Check Loaded/Get Version + + EAX = ('P' << 8) | 'B'; + EBX = ('Q' << 8) | ' '; + + EDX = (MAJORVERSION << 8) | MINORVERSION; + break; + + case 1: // Set Appl mAsk + + HostVec->HOSTAPPLMASK = EDX; // APPL MASK + HostVec->HOSTAPPLFLAGS = (UCHAR)ECX; // APPL FLAGS + + // If either is non-zero, set allocated and Process. This gets round problem with + // stations that don't call allocate stream + + if (ECX || EBX) + { + HostVec->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + HostVec->STREAMOWNER = GetCurrentProcessId(); + + // Set Program Name + + memcpy(&HostVec->PgmName, pgm, 31); + } + break; + + case 2: // Send Frame + + // ES:ESI = MESSAGE, CX = LENGTH, BX = VECTOR + + EAX = SendMsg(Stream, ESI, ECX); + break; + + case 3: + + // AH = 3 Receive frame into buffer at ES:EDI, length of frame returned + // in CX. BX returns the number of outstanding frames still to + // be received (ie. after this one) or zero if no more frames + // (ie. this is last one). + + EAX = GetMsg(Stream, EDI, &ECX, &EBX); + break; + + case 4: + + // AH = 4 Get stream status. Returns: + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + ECX = EDX = 0; + + if (HostVec->HOSTFLAGS & 3) //STATE CHANGE BITS + EDX = 1; + + if (Session) + ECX = 1; + + break; + + case 5: + + // AH = 5 Ack stream status change + + HostVec->HOSTFLAGS &= 0xFC; // Clear Chnage Bits + break; + + case 6: + + // AH = 6 Session control. + + // CX = 0 Conneect - APPLMASK in DL + // CX = 1 connect + // CX = 2 disconnect + // CX = 3 return user to node + + SessionControl(Stream, ECX, EDX); + break; + + case 7: + + // AH = 7 Get buffer counts for stream. Returns: + + // AX = number of status change messages to be received + // BX = number of frames queued for receive + // CX = number of un-acked frames to be sent + // DX = number of buffers left in node + // SI = number of trace frames queued for receive + + + ECX = 0; // unacked frames + EDX = QCOUNT; + + ESI = (void *)MONCount(Stream); + EBX = RXCount(Stream); + ECX = TXCount(Stream); + + EAX = 0; // Is this right ??? + + break; + + case 8: + + // AH = 8 Port control/information. Called with a stream number + // in AL returns: + // + // AL = Radio port on which channel is connected (or zero) + // AH = SESSION TYPE BITS + // BX = L2 paclen for the radio port + // CX = L2 maxframe for the radio port + // DX = L4 window size (if L4 circuit, or zero) + // ES:EDI = CALLSIGN + + + GetConnectionInfo(Stream, EDI, &EAX, &Temp, &EBX, &ECX, &EDX); // Return the Secure Session Flag rather than not connected + EAX |= Temp <<8; + + break; + + + case 9: + + // Not Implemented + + break; + + case 10: + + // AH = 10 Unproto transmit frame. Data pointed to by ES:ESI, of + // length CX, is transmitted as a HDLC frame on the radio + // port (not stream) in AL. + + EAX = SendRaw(EAX, ESI, ECX); + return; + + case 11: + + // AH = 11 Get Trace (RAW Data) Frame into ES:EDI, + // Length to CX, Timestamp to AX + + EAX = GetRaw(Stream, EDI, &ECX, &EBX); + break; + + case 12: + + // Update Switch + + if (EDX == 2) + { + SENDNODESMSG(); + break; + } + if (EDX == 2) + { + // UPDATE BT + + BTLENGTH = ECX; + memcpy(BTEXTFLD, ESI, ECX + 7); + } + + break; + + case 13: + + // BPQALLOC + + // AL = 0 = Find Free + // AL != 0 Alloc or Release + + if (EAX == 0) + { + EAX = FindFreeStream(); + break; + } + + if (ECX == 1) // Allocate + { + EAX = AllocateStream(Stream); + break; + } + + DeallocateStream(Stream); + break; + + case 14: + + // AH = 14 Internal Interface for IP Router + + // Send frame - to NETROM L3 if DL=0 + // to L2 Session if DL<>0 + + break; // Shouldn't be needed + + case 15: + + // GETTIME + + EAX = REALTIMETICKS; + EBX = 0; + +#ifdef EXCLUDEBITS + + EBX = (ULONG)ExcludeList; + +#endif + break; + + } + + *pEAX = EAX; + *pEBX = EBX; + *pECX = ECX; + *pEDX = EDX; + *pESI = ESI; + *pEDI = EDI; + + return; +} + diff --git a/DRATS.c b/DRATS.c new file mode 100644 index 0000000..c0251be --- /dev/null +++ b/DRATS.c @@ -0,0 +1,673 @@ +/* +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 +*/ + +// DRATS support code + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +#include "bpq32.h" +#include "telnetserver.h" + + +/* +The header is the first 23 bytes of of the frame. The payload is the rest of the frame. + +Byte 1 is a "magic" number. It is 0xDD if the payload is zlib compressed before being yencoded. +Bytes 2 and 3 is a 16 bit sequence number. +Byte 4 is a session number. +Byte 5 is a type. Still don't know the types. +Bytes 6 and 7, 16 bits, is the checksum of the data after any compression. +Bytes 8 and 9, 16 bits, is the length of the data. +bytes 10-18, 8 bits, are the source call sign. +bytes 19-25, 8 bits, are the destination call sign. +If a call sign is less than 8 characters, it is padded to fill the space with the "~" tilde character. + +Frame types: (Some from sessions/chat.py) + +0 - T_DEF +1 - T_PING_REQ - Ping request (Used in Test frame) +2 - T_PING_RSP - Ping response +3 - T_PING_ERS - Ping error status? +4 - T_STATUS - Status frame +5 - File Transfer +8 - Used in Test frame +254 - Apparently a warm up frame. + + T_DEF = 0 + T_PNG_REQ = 1 + T_PNG_RSP = 2 + T_PNG_ERQ = 3 + T_PNG_ERS = 4 + T_STATUS = 5 +For a station status frame, the first byte of the message is an ASCII station status value. + +'0' - Unknown +'1' - Online +'2' - Unattended +'9' - Offline +*/ + +#define T_DEF 0 +#define T_PNG_REQ 1 +#define T_PNG_RSP 2 +#define T_PNG_ERQ 3 +#define T_PNG_ERS 4 +#define T_STATUS 5 + +#pragma pack(1) + +// shorts are big-endian + +struct DRATSHeader +{ + unsigned char Magic; + unsigned short Seq; + unsigned char Sessno; + unsigned char Type; + unsigned short CheckSum; + unsigned short Length; + char CallFrom[8]; + char CallTo[8]; + unsigned char Message[2048]; +}; + +#pragma pack() + +struct DRATSSession +{ + struct ConnectionInfo * sockptr; + unsigned int Seq; + unsigned int Sessno; + char CallFrom[8]; + char CallTo[8]; + int Stream; // BPQ Stream + int StreamState; + struct DRATSQueue * Queue; + struct DRATSSession * Next; +}; + +struct DRATSQueue +{ + // Queue of messages to be sent to node from background (ie not under semaphore) + + int Stream; + int Len; + unsigned char * Msg; + struct DRATSQueue * Next; +}; + + +struct DRATSSession * DRATSSessions = NULL; + + +char peer0_2[] = { /* Packet 17 */ +0x5b, 0x53, 0x4f, 0x42, 0x5d, 0xdd, 0x3d, 0x40, +0x3d, 0x40, 0x01, 0x05, 0x45, 0x78, 0x3d, 0x40, +0x18, 0x47, 0x38, 0x42, 0x50, 0x51, 0x7e, 0x7e, +0x7e, 0x43, 0x51, 0x43, 0x51, 0x43, 0x51, 0x7e, +0x7e, 0x78, 0xda, 0x33, 0xf4, 0xcf, 0xcb, 0xc9, +0xcc, 0x4b, 0x55, 0xd0, 0x70, 0xd1, 0x0d, 0x72, +0x0c, 0x09, 0xd6, 0x04, 0x3d, 0x40, 0x2a, 0x8c, +0x04, 0xb3, 0x5b, 0x45, 0x4f, 0x42, 0x5d }; + + +void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr); + +int testDRATS() +{ +// processDRATSFrame(peer0_1, sizeof(peer0_1), 0); +// processDRATSFrame(peer0_2, sizeof(peer0_2), 0); +// processDRATSFrame(peer1_1, sizeof(peer1_1), 0); +// processDRATSFrame(peer1_20, sizeof(peer1_20)); +// processDRATSFrame(peer0_20, sizeof(peer0_20)); +// processDRATSFrame(peer1_21, sizeof(peer1_21)); + + return 0; +} + + +extern char pgm[256]; +extern char TextVerstring[50]; + +int HeaderLen = offsetof(struct DRATSHeader, Message); + +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); +int dratscrc(unsigned char *ptr, int count); +int FindFreeStreamNoSem(); +void sendDRATSFrame(struct ConnectionInfo * sockptr, struct DRATSHeader * Header); +int yEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); + + +int AllocateDRATSStream(struct DRATSSession * Sess) +{ + int Stream; + + strcpy(pgm, "DRATS"); + + Stream = FindFreeStreamNoSem(); + + strcpy(pgm, "bpq32.exe"); + + if (Stream == 255) return 0; + + if (memcmp(Sess->CallTo, "NODE", 6) == 0) + { + // Just connect to command level on switch + } + + return Stream; +} + +void ProcessDRATSPayload(struct DRATSHeader * Header, struct DRATSSession * Sess) +{ + struct DRATSQueue * QEntry; + BPQVECSTRUC * HOST; + + if (Sess->Stream == 0) + { + Sess->Stream = AllocateDRATSStream(Sess); + } + + if (Sess->StreamState == 0) + { + unsigned char AXCall[10]; + + Connect(Sess->Stream); // Connect + ConvToAX25(Sess->CallFrom, AXCall); + ChangeSessionCallsign(Sess->Stream, AXCall); // Prevent triggering incoming connect code + + // Clear State Changed bits (cant use SessionState under semaphore) + + HOST = &BPQHOSTVECTOR[Sess->Stream -1]; // API counts from 1 + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits + Sess->StreamState = 1; + } + + strcat(Header->Message, "\r"); + + // Need to Queue to Background as we can't use SendMsg under semaphore + + QEntry = zalloc(sizeof(struct DRATSQueue)); + QEntry->Len = strlen(Header->Message); + QEntry->Msg = malloc(QEntry->Len); + memcpy(QEntry->Msg, Header->Message, QEntry->Len); + + // Add to queue + + if (Sess->Queue) + { + struct DRATSQueue * End = Sess->Queue; + + // Add on end + while (End->Next) + End = End->Next; + + End->Next = QEntry; + } + else + Sess->Queue = QEntry; + +} + +// Called under semaphore + + +void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr) +{ + unsigned char * Payload; + unsigned char * ptr; + unsigned char dest[2048]; + struct DRATSHeader * Header; + int outLen; + struct DRATSSession * Sess = DRATSSessions; + unsigned short crc, savecrc; + char CallFrom[10] = ""; + char CallTo[10] = ""; + + Message[Len] = 0; + Debugprintf(Message); + + Payload = strstr(Message, "[SOB]"); + + if (Payload == 0) + return; + + ptr = strstr(Message, "[EOB]"); + + if (ptr == 0) + return; + + ptr[0] = 0; + + Payload += 5; + + Header = (struct DRATSHeader *)Payload; + + // Undo = transparency + + ptr = Payload; + + while (ptr = strchr(ptr, '=')) + { + memmove(ptr, ptr + 1, Len); + ptr[0] -= 64; + ptr++; + } + + // Check CRC + + savecrc = htons(Header->CheckSum); + Header->CheckSum = 0; + + crc = dratscrc(Payload, htons(Header->Length) + HeaderLen); + + if (crc != savecrc) + { + Debugprintf(" DRARS CRC Error %x %x", crc, savecrc); // Good CRC + return; + } + + Header->Length = htons(Header->Length); // convert to machine order + + if (Header->Magic == 0xdd) // Zlib compressed + { + doinflate(Header->Message, dest, Header->Length, 2048, &outLen); + memcpy(Header->Message, dest, outLen + 1); + Header->Length = outLen; + } + Debugprintf(Header->Message); + + // Look for a matching From/To/Session + + memcpy(CallFrom, Header->CallFrom, 8); + memcpy(CallTo, Header->CallTo, 8); + + strlop(CallFrom, '~'); + strlop(CallTo, '~'); + + if (Header->Type == T_STATUS) + { + // Status frame ?? What to do with it ?? + + return; + } + + if (Header->Type == T_PNG_REQ) + { + // "Ping Request" + + // if to "NODE" reply to it + + if (strcmp(CallTo, "NODE") == 0) + { + // Reuse incoming message + + strcpy(Header->CallFrom, CallTo); + strcpy(Header->CallTo, CallFrom); + Header->Type = T_PNG_RSP; + Header->Length = sprintf(Header->Message, "Running BPQ32 Version %s", TextVerstring); + + sendDRATSFrame(sockptr, Header); + return; + } + + // Not to us - do we route it ?? + + return; + } + + if (Header->Type == T_PNG_RSP) + { + // Reponse is PNG_RSP then Status - 1Online (D-RATS) + // "Running D-RATS 0.3.9 (Windows 8->10 (6, 2, 9200, 2, ''))" + // "Running D-RATS 0.3.10 beta 4 (Linux - Raspbian GNU/Linux 9)" + + return; + } + + if (Header->Type != T_DEF) + { + return; + } + + // ?? Normal Data + + if (strcmp(CallTo, "NODE") != 0) + { + // Not not Node - should we route it ?? + + return; + } + + while (Sess) + { + if (Sess->Sessno == Header->Sessno && memcmp(Sess->CallFrom, CallFrom, 8) == 0 + && memcmp(Sess->CallTo, CallTo, 8) == 0 && Sess->sockptr == sockptr) + { + ProcessDRATSPayload(Header, Sess); + return; + } + Sess = Sess->Next; + } + + // Allocate a new one + + Sess = zalloc(sizeof(struct DRATSSession)); + + Sess->Sessno = Header->Sessno; + memcpy(Sess->CallFrom, CallFrom, 8); + memcpy(Sess->CallTo, CallTo, 8); + Sess->sockptr = sockptr; + + if (DRATSSessions) + { + // Add to front of Chain + + Sess->Next = DRATSSessions; + } + + DRATSSessions = Sess; + + ProcessDRATSPayload(Header, Sess); + return; + +} + +void DRATSPoll() +{ + struct DRATSSession * Sess = DRATSSessions; + int Stream, state, change; + int count; + struct DRATSHeader Header; + struct DRATSQueue * QEntry; + struct DRATSQueue * Save; + + while (Sess) + { + Stream = Sess->Stream; + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected - do we need anything ?? + } + else + { + // Send a disconnected message + + char From[10] = "~~~~~~~~~"; + char To[10] = "~~~~~~~~~"; + + Sess->StreamState = 0; + + Header.Length = sprintf(Header.Message, "*** Disconnected from Node"); + + + memcpy(To, Sess->CallFrom, strlen(Sess->CallFrom)); + memcpy(From, Sess->CallTo, strlen(Sess->CallTo)); + + memcpy(Header.CallFrom, From, 8); + memcpy(Header.CallTo, To, 8); + + Header.Magic = 0x22; + Header.Type = 0; + Header.Seq = 0; + Header.Sessno = Sess->Sessno; + + sendDRATSFrame(Sess->sockptr, &Header); + + + } + } + + do + { + int Len; + + GetMsg(Stream, (char *)Header.Message, &Len, &count); + Header.Length = Len; + + if (Header.Length) + { + char From[10] = "~~~~~~~~~"; + char To[10] = "~~~~~~~~~"; + + memcpy(To, Sess->CallFrom, strlen(Sess->CallFrom)); + memcpy(From, Sess->CallTo, strlen(Sess->CallTo)); + + memcpy(Header.CallFrom, From, 8); + memcpy(Header.CallTo, To, 8); + + Header.Magic = 0x22; + Header.Type = 0; + Header.Seq = 0; + Header.Sessno = Sess->Sessno; + + sendDRATSFrame(Sess->sockptr, &Header); + } + } + while (count > 0); + + // See if anything to send to node + + QEntry = Sess->Queue; + + while (QEntry) + { + SendMsg(Sess->Stream, QEntry->Msg, QEntry->Len); + Save = QEntry; + QEntry = QEntry->Next; + free(Save->Msg); + free(Save); + } + + Sess->Queue = 0; + Sess = Sess->Next; + } +} + +unsigned char BANNED[] = {'=', 0x11, 0x13, 0x1A, 0xFD, 0xFE, 0xFF, 0}; + + +void sendDRATSFrame(struct ConnectionInfo * sockptr, struct DRATSHeader * Header) +{ + unsigned short crc; + int len; + unsigned char out[2048] = "[SOB]"; + int packetLen = Header->Length + HeaderLen; + + // Length is in host order + + Header->Length = htons(Header->Length); + + Header->CheckSum = 0; + + crc = dratscrc((unsigned char *)Header, packetLen); + Header->CheckSum = htons(crc); + + len = yEncode((unsigned char *)Header, out + 5, packetLen, BANNED); + + memcpy(&out[len + 5], "[EOB]", 5); + Debugprintf(out); + send(sockptr->socket, out, len + 10, 0); +} + +void DRATSConnectionLost(struct ConnectionInfo * sockptr) +{ + // Disconnect any sessions, then free Stream and Sess record + + struct DRATSSession * Sess = DRATSSessions; + struct DRATSSession * Save = 0; + BPQVECSTRUC * HOST; + + while (Sess) + { + if (Sess->sockptr == sockptr) + { + if (Sess->StreamState == 1) // COnnected + { + Disconnect(Sess->Stream); + HOST = &BPQHOSTVECTOR[Sess->Stream -1]; // API counts from 1 + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits + } + DeallocateStream(Sess->Stream); + + // We must unhook from chain + + if (Save) + Save->Next = Sess->Next; + else + DRATSSessions = Sess->Next; + + // Should really Free any Queue, but unlikely to be any + + free(Sess); + + if (Save) + Sess = Save->Next; + else + Sess = DRATSSessions; + } + else + { + Save = Sess; + Sess = Sess->Next; + } + } +} + + + + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include "zlib.h" + + +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen) +{ + int ret; + z_stream strm; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + ret = inflateInit(&strm); + if (ret != Z_OK) + return ret; + + strm.avail_in = Len; + strm.next_in = source; + + strm.avail_out = destlen; + strm.next_out = dest; + + ret = inflate(&strm, Z_NO_FLUSH); + + inflateEnd(&strm); + + dest[strm.total_out] = 0; + + *outLen = strm.total_out; + + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + +// No idea what this CRC is, but it works! (converted from DRATS python code) + +int update_crc(int c, int crc) +{ + int i; + int v; + + for (i = 0; i < 8; i++) + { + if ((c & 0x80)) + v = 1; + else + v = 0; + + if (crc & 0x8000) + { + crc <<= 1; + crc += v; + crc ^= 0x1021; + } + else + { + crc <<= 1; + crc += v; + } + + c <<= 1; + } + + crc &= 0xFFFF; + return crc; + +} + +int dratscrc(unsigned char *ptr, int count) +{ + int i; + int checksum = 0; + + for (i = 0; i < count; i++) + checksum = update_crc(ptr[i], checksum); + + checksum = update_crc(0, checksum); + checksum = update_crc(0, checksum); + return checksum; +} + +#define OFFSET 64 + +int yEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c + OFFSET) & 0xFF; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + + diff --git a/Dragon.c b/Dragon.c new file mode 100644 index 0000000..9160356 --- /dev/null +++ b/Dragon.c @@ -0,0 +1,2257 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// P4Dragon support Module. Extracted from SCSPactor + + + +//#ifdef WIN32 +//#define WRITELOG +//#endif + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE +#define _USE_32BIT_TIME_T + + +#include +#include +#include "time.h" + +#define MaxStreams 10 // First is used for Pactor, even though Pactor uses channel 31 + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#ifndef WIN32 +#ifndef MACBPQ +#include +#include +#endif +#endif + +static char ClassName[]="DRAGONSTATUS"; +static char WindowTitle[] = "P4Dragon"; +static int RigControlRow = 185; + + +#define NARROWMODE 12 // PI/II +#define WIDEMODE 16 // PIII only + +extern UCHAR BPQDirectory[]; + +extern char * PortConfig[33]; +extern BOOL RIG_DEBUG; + +static RECT Rect; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd +extern char * RigConfigMsg[35]; + +VOID __cdecl Debugprintf(const char * format, ...); + +char NodeCall[11]; // Nodecall, Null Terminated + +unsigned long _beginthread( void( *start_address )(), unsigned stack_size, int arglist); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + +VOID DragonSuspentPort(struct TNCINFO * TNC); +VOID DragonReleasePort(struct TNCINFO * TNC); + + + +static ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + BPQport = Port; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + TNC->Dragon = TRUE; + + 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, "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, "PACKETCHANNELS", 14) == 0) // Packet Channels + TNC->PacketChannels = atoi(&buf[14]); + + else + if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + + else + if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + + else + if (_memicmp(buf, "USEAPPLCALLS", 12) == 0 && buf[12] != 'F' && buf[12] != 'f') + TNC->UseAPPLCalls = TRUE; + else + if (_memicmp(buf, "USEAPPLCALLSFORPACTOR", 21) == 0) + TNC->UseAPPLCallsforPactor = TRUE; + else + if (_memicmp(buf, "DRAGON", 6) == 0) + { + if (_memicmp(&buf[7], "SINGLE", 6) == 0) + TNC->DragonSingle = TRUE; + } + else + if (_memicmp(buf, "FORCE ROBUST", 12) == 0) + TNC->ForceRobust = TNC->RobustDefault = TRUE; + else + if (_memicmp(buf, "MAXLEVEL", 8) == 0) // Maximum Pactor Level to use. + TNC->MaxLevel = atoi(&buf[8]); + else + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); + +} + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + TNC->Timeout = 20; // 2 secs + return TRUE; +} + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn);static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +void SCSCheckRX(struct TNCINFO * TNC); +VOID SCSPoll(int Port); +VOID CRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); +unsigned short int compute_crc(unsigned char *buf,int len); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +VOID ExitHost(struct TNCINFO * TNC); +VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); +int Switchmode(struct TNCINFO * TNC, int Mode); +VOID SwitchToPacketOnly(struct TNCINFO * TNC); + + +char status[8][8]; +char ModeText[8][14]; +char PactorLevelText[5][14]; +char PleveltoMode[5]; + +#ifdef WRITELOG +static HANDLE LogHandle[32] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; +#endif + +//char * Logs[4] = {"1", "2", "3", "4"}; + +static char BaseDir[MAX_PATH]="c:\\"; + +static VOID CloseLogFile(int Flags) +{ +#ifdef WRITELOG + CloseHandle(LogHandle[Flags]); + LogHandle[Flags] = INVALID_HANDLE_VALUE; +#endif +} + +static BOOL OpenLogFile(int Flags) +{ +#ifdef WRITELOG + UCHAR FN[MAX_PATH]; + + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\SCSLog_%02d%02d_%d.txt", BPQDirectory, tm->tm_mon + 1, tm->tm_mday, Flags); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +#endif + return 0; +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +#ifdef WRITELOG + int cnt; + WriteFile(LogHandle[Flags], Msg , MsgLen, &cnt, NULL); + WriteFile(LogHandle[Flags], "\r\n" , 2, &cnt, NULL); +#endif +} + + + +static int ExtProc(int fn, int port,unsigned char * buff) +{ + int txlen = 0; + UINT * buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + int Param; + int Stream = 0; + struct STREAMINFO * STREAM; + char PLevel; + 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; + + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + if (TNC->hDevice == 0) + return 0; + +#ifndef WIN32 +#ifndef MACBPQ + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + Debugprintf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif + } +ok: + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SCSCheckRX(TNC); + SCSPoll(port); + + return 0; + + case 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff[4] = Stream; + + return -1; + } + } + + if (TNC->EnterExit) + return 0; // Switching to Term mode to change bandwidth + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + + datalen=buffptr[1]; + + buff[4] = Stream; + buff[7] = 0xf0; + memcpy(&buff[8],buffptr+2,datalen); // Data goes to +7, but we have an extra byte + datalen+=8; + + PutLengthinBuffer(buff, datalen); + + // buff[5]=(datalen & 0xff); + // buff[6]=(datalen >> 8); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + Stream = buff[4]; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr[1] = 36; + memcpy(buffptr+2, "No Connection to PACTOR TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer(buff) - 8; + + buffptr[1] = txlen; + memcpy(buffptr+2, &buff[8], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + TNC->Streams[Stream].FramesOutstanding++; + + return (0); + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)buff; + + STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + if (STREAM->FramesOutstanding > 4) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); + } + else + { + if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + + case 4: // reinit + + // Ensure in Pactor + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x1; + TNC->TXBuffer[4] = 0x1; + memcpy(&TNC->TXBuffer[5], "PT", 2); + + CRCStuffAndSend(TNC, TNC->TXBuffer, 7); + + Sleep(25); + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) -1; + TNC->ReopenTimer = 250; + TNC->HostMode = FALSE; + + return (0); + + case 5: // Close + + // Ensure in Pactor + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x1; + TNC->TXBuffer[4] = 0x1; + memcpy(&TNC->TXBuffer[5], "PT", 2); + + CRCStuffAndSend(TNC, TNC->TXBuffer, 7); + + Sleep(25); + + ExitHost(TNC); + + Sleep(25); + + CloseCOMPort(TNCInfo[port]->hDevice); + + return (0); + + case 6: // Scan Interface + + Param = (int)buff; + + switch (Param) + { + case 1: // Request Permission + + if (TNC->TNCOK) + { + // If been in Sync a long time, or if using applcalls and + // Scan had been locked too long just let it change + + if (TNC->UseAPPLCallsforPactor) + { + if (TNC->PTCStatus == 6) // Sync + { + int insync = time(NULL) - TNC->TimeEnteredSYNCMode; + if (insync > 4) + { + Debugprintf("SCS Scan - in SYNC for %d Secs - allow change regardless", insync); + return 0; + } + } + else if (TNC->TimeScanLocked) + { + int timeLocked = time(NULL) - TNC->TimeScanLocked; + if (timeLocked > 4) + { + Debugprintf("SCS Scan - Scan Locked for %d Secs - allow change regardless", timeLocked); + TNC->TimeScanLocked = 0; + return 0; + } + } + } + + TNC->WantToChangeFreq = TRUE; + TNC->OKToChangeFreq = FALSE; + return TRUE; + } + return 0; // Don't lock scan if TNC isn't responding + + + case 2: // Check Permission + return TNC->OKToChangeFreq; + + case 3: // Release Permission + + TNC->WantToChangeFreq = FALSE; + + if (TNC->DontReleasePermission) // Disable connects during this interval? + { + TNC->DontReleasePermission = FALSE; + if (TNC->SyncSupported == FALSE) + TNC->TimeScanLocked = time(NULL) + 100; // Make sure doesnt time out + return 0; + } + + TNC->DontWantToChangeFreq = TRUE; + return 0; + + default: // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + PLevel = Scan->PMaxLevel; + + if (PLevel == 0 && (Scan->HFPacketMode || Scan->RPacketMode)) + { + // Switch to Packet for this Interval + + if (RIG_DEBUG) + Debugprintf("Dragon Switching to Packet, %d", TNC->HFPacket); + + if (TNC->HFPacket == FALSE) + SwitchToPacketOnly(TNC); + + return 0; + } + + if (PLevel > '0' && PLevel < '5') // 1 - 4 + { + if (TNC->Bandwidth != PLevel) + { + TNC->Bandwidth = PLevel; + TNC->MinLevel = Scan->PMinLevel - '0'; + Switchmode(TNC, PLevel - '0'); + } + + if (TNC->UseAPPLCallsforPactor && Scan->APPLCALL[0]) + { + // Switch callsign + + STREAM = &TNC->Streams[0]; + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + + strcpy(STREAM->MyCall, Scan->APPLCALL); + + sprintf(STREAM->CmdSet, "I%s\rI\r", STREAM->MyCall); + if (RIG_DEBUG) + Debugprintf("SCS Pactor APPLCALL Set to %s", STREAM->MyCall); + } + + else + { + if (TNC->HFPacket) + SwitchToPactor(TNC); + } + } + + if (Scan->RPacketMode) + if (TNC->RobustTime) + SwitchToPacket(TNC); // Always start in packet, switch to pactor after RobustTime ticks + + if (PLevel == '0') + TNC->DontReleasePermission = TRUE; // Dont allow connects in this interval + else + TNC->DontReleasePermission = FALSE; + + return 0; + } + } + return 0; +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "SCS Pactor Status

SCS 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], "", TNC->WEB_PACTORLEVEL); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Buffers%s
Traffic%s
Mode%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +UINT DragonExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int Stream = 0; + char * TempScript; + + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"Dragon %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return (int) ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_SCS; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 3; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->MaxLevel == 0) + TNC->MaxLevel = 3; + + // Set up DED addresses for streams (first stream (Pactor) = DED 31 + + TNC->Streams[0].DEDStream = 31; + + for (Stream = 1; Stream <= MaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; + } + + if (TNC->PacketChannels > MaxStreams) + TNC->PacketChannels = MaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = CONLOCK; // Scan Control 3 stage/conlock + + TNC->PortRecord = PortEntry; + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + TNC->SuspendPortProc = DragonSuspentPort; + TNC->ReleasePortProc = DragonReleasePort; + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // get NODECALL for RP tests + + memcpy(NodeCall, MYNODECALL, 10); + + ptr=strchr(NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + + // Set TONES to 4 + + TempScript = malloc(1000); + + strcpy(TempScript, "QUIT\r"); // In case in pac: mode + strcat(TempScript, "TONES 4\r"); // Tones may be changed but I want this as standard + strcat(TempScript, "MAXERR 30\r"); // Max retries + strcat(TempScript, "MODE 0\r"); // ASCII mode, no PTC II compression (Forwarding will use FBB Compression) + strcat(TempScript, "MAXSUM 20\r"); // Max count for memory ARQ + strcat(TempScript, "CWID 0 2\r"); // CW ID disabled + strcat(TempScript, "PTCC 0\r"); // Dragon out of PTC Compatibility Mode + strcat(TempScript, "VER\r"); // Try to determine Controller Type + + sprintf(msg, "MYLEVEL %d\r", TNC->MaxLevel); + strcat(TempScript, msg); // Default Level to MAXLEVEL + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "ADDLF 0\r"); // Auto Line Feed disabled + strcat(TNC->InitScript, "ARX 0\r"); // Amtor Phasing disabled + strcat(TNC->InitScript, "BELL 0\r"); // Disable Bell + strcat(TNC->InitScript, "BC 0\r"); // FEC reception is disabled + strcat(TNC->InitScript, "BKCHR 2\r"); // Breakin Char = 2 + strcat(TNC->InitScript, "CHOBELL 0\r"); // Changeover Bell off + strcat(TNC->InitScript, "CMSG 0\r"); // Connect Message Off + strcat(TNC->InitScript, "LFIGNORE 0\r"); // No insertion of Line feed + strcat(TNC->InitScript, "LISTEN 0\r"); // Pactor Listen disabled + strcat(TNC->InitScript, "MAIL 0\r"); // Disable internal mailbox reporting + strcat(TNC->InitScript, "REMOTE 0\r"); // Disable remote control + strcat(TNC->InitScript, "PAC CBELL 0\r"); // + strcat(TNC->InitScript, "PAC CMSG 0\r"); // + strcat(TNC->InitScript, "PAC PRBOX 0\r"); // Turn off Packet Radio Mailbox + + // Automatic Status must be enabled for BPQ32 + // Pactor must use Host Mode Chanel 31 + // PDuplex must be set. The Node code relies on automatic IRS/ISS changeover + // 5 second duplex timer + + strcat(TNC->InitScript, "STATUS 2\rPTCHN 31\rPDUPLEX 1\rPDTIMER 5\r"); + + sprintf(msg, "MYCALL %s\rPAC MYCALL %s\r", TNC->NodeCall, TNC->NodeCall); + strcat(TNC->InitScript, msg); + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_PACTORLEVEL = zalloc(100); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 235, 500); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Buffers", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->xIDC_PACTORLEVEL = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE,10,160,430,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 240; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + +#ifndef WIN32 +#ifndef MACBPQ + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + printf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif + + if (TNC->RobustDefault) + SwitchToPacket(TNC); + + WritetoConsole("\n"); + + return ((int)ExtProc); +} + +VOID DragonPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + int Stream = 0; + int nn; + struct STREAMINFO * STREAM; + + if (TNC->MinLevelTimer) + { + TNC->MinLevelTimer--; + + if (TNC->MinLevelTimer == 0) + { + // Failed to reach min level in 15 secs + + STREAM = &TNC->Streams[0]; + + if (STREAM->Connected) + { + UINT * buffptr; + + Debugprintf("Required Min Level not reached - disconnecting"); + + // Discard Queued Data, Send a Message, then a disconnect + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + STREAM->NeedDisc = 15; // 1 secs + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = sprintf((char *)&buffptr[2], + "This port only allows Pactor Level %d or above - Disconnecting\r\n", TNC->MinLevel); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } + } + } + + if (TNC->SwitchToPactor) + { + TNC->SwitchToPactor--; + + if (TNC->SwitchToPactor == 0) + SwitchToPactor(TNC); + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach + + // If Pactor, stop scanning and take out of listen mode. + + // Set call to connecting user's call + + // If Stream 0 Put in Pactor Mode so Busy Detect will work + + int calllen=0; + + TNC->Streams[Stream].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + if (Stream == 0) + { + // Release Scan Lock if it is held + + if (TNC->DontReleasePermission) + { + TNC->DontReleasePermission = FALSE; + TNC->DontWantToChangeFreq = TRUE; + } + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = malloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "I%s\r", "SCSPTC"); + + Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[Stream].CmdSet); + + SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanner + + sprintf(Status, "%d SCANSTOP", TNC->Port); + TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch + + Rig_Command(-1, Status); + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + TNC->Retries--; + + if(TNC->Retries) + { + WriteCommBlock(TNC); // Retransmit Block + return; + } + + // Retried out. + + if (TNC->HostMode == 0) + { + DoTermModeTimeout(TNC); + return; + } + + // Retried out in host mode - Clear any connection and reinit the TNC + + Debugprintf("PACTOR - Link to TNC Lost"); + TNC->TNCOK = FALSE; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + UINT * buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + TNC->HostMode = 0; + TNC->ReinitState = 0; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + // We delay clearing busy for BusyHold secs + + if (TNC->Busy) + if (TNC->Mode != 7) + TNC->Busy--; + + if (TNC->BusyDelay) // Waiting to send connect + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == 0) + { + // No, so send + + TNC->Streams[0].CmdSet = TNC->ConnectCmd; + 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); + + Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[0].CmdSet); + + TNC->BusyDelay = 0; + return; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + UINT * buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1]=39; + memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + free(TNC->ConnectCmd); + + } + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + STREAM->ReportDISC = TRUE; + + } + + if (TNC->Timeout) + return; // We've sent something + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + TNC->PollSent = FALSE; + + //If sending internal command list, send next element + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].CmdSet) + { + char * start, * end; + int len; + + start = TNC->Streams[Stream].CmdSet; + + if (*(start) == 0) // End of Script + { + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + } + else + { + if (*(start) == 1) + { + // This is UI data, not a command. Send it to channel 0 + + int uilen = strlen(&start[1]); + + Poll[2] = 0; // UI Channel + Poll[3] = 0; // Data + Poll[4] = uilen - 1; + memcpy(&Poll[5], &start[1], uilen); + + CRCStuffAndSend(TNC, Poll, uilen + 5); + + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + + return; + } + + end = strchr(start, 13); + len = ++end - start -1; // exclude cr + TNC->Streams[Stream].CmdSet = end; + + Poll[2] = TNC->Streams[Stream].DEDStream; // Channel + Poll[3] = 1; // Command + Poll[4] = len - 1; + memcpy(&Poll[5], start, len); + + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], len); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, len + 5); + + return; + } + } + } + // if Freq Change needed, check if ok to do it. + + if (TNC->TNCOK) + { + if (TNC->WantToChangeFreq) + { + Poll[2] = 31; // Command + Poll[3] = 1; // Command + Poll[4] = 2; // Len -1 + Poll[5] = '%'; + Poll[6] = 'W'; + Poll[7] = '0'; + + CRCStuffAndSend(TNC, Poll, 8); + + TNC->InternalCmd = TRUE; + TNC->WantToChangeFreq = FALSE; + + return; + } + + if (TNC->DontWantToChangeFreq) + { + Poll[2] = 31; // Command + Poll[3] = 1; // Command + Poll[4] = 2; // Len -1 + Poll[5] = '%'; + Poll[6] = 'W'; + Poll[7] = '1'; + + CRCStuffAndSend(TNC, Poll, 8); + + TNC->InternalCmd = TRUE; + TNC->DontWantToChangeFreq = FALSE; + TNC->OKToChangeFreq = FALSE; + + return; + } + } + + // Send Radio Command if avail + + if (TNC->TNCOK && TNC->BPQtoRadio_Q) + { + int datalen; + UINT * buffptr; + + buffptr=Q_REM(&TNC->BPQtoRadio_Q); + + datalen=buffptr[1]; + + Poll[2] = 253; // Radio Channel + Poll[3] = 0; // Data? + Poll[4] = datalen - 1; + + memcpy(&Poll[5], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + if (RIG_DEBUG) + { + Debugprintf("SCS Rig Command Queued, Len = %d", datalen ); + Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + Poll[5], Poll[6], Poll[7], Poll[8], Poll[9], Poll[10], Poll[11], Poll[12], + Poll[13], Poll[14], Poll[15], Poll[16], Poll[17], Poll[18], Poll[19], Poll[20]); + } + +// Debugprintf("SCS Sending Rig Command"); + + return; + } + + if (TNC->TNCOK && TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char CCMD[80] = "C"; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + datalen = buffptr->LENGTH - 7; + Buffer = &buffptr->DEST[0]; // Raw Frame + + Buffer[datalen] = 0; + + // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address + // before sending the beacon + + ConvFromAX25(Buffer, &Call[1]); // Dest + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 14; // Skip Origin + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + Poll[2] = 0; // UI Channel + Poll[3] = 1; // CMD + Poll[4] = strlen(CCMD) - 1; + strcpy(&Poll[5], CCMD); + CRCStuffAndSend(TNC, Poll, Poll[4] + 6); // Set Dest and Path + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400); + sprintf(TNC->Streams[0].CmdSet, "%c%s", 1, Buffer); // Flag CmdSet as Data + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + + // Check status Periodically + + if (TNC->TNCOK) + { + if (TNC->IntCmdDelay == 6) + { + Poll[2] = 254; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + + CRCStuffAndSend(TNC, Poll, 7); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay--; + + return; + } + + if (TNC->IntCmdDelay == 4) + { + Poll[2] = 31; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = '%'; // Bytes acked Status + Poll[6] = 'T'; + + CRCStuffAndSend(TNC, Poll, 7); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay--; + + return; + } + + if (TNC->IntCmdDelay <=0) + { + Poll[2] = 31; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = '@'; // Buffer Status + Poll[6] = 'B'; + + CRCStuffAndSend(TNC, Poll, 7); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay = 20; // Every 2 secs + + return; + } + else + TNC->IntCmdDelay--; + } + + // If busy, send status poll, send Data if avail + + // We need to start where we last left off, or a busy stream will lock out the others + + for (nn = 0; nn <= MaxStreams; nn++) + { + Stream = TNC->LastStream++; + + if (TNC->LastStream > MaxStreams) TNC->LastStream = 0; + + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + UINT * buffptr; + char * Buffer; + + // Dont send to Pactor if waiting for Min Level to be reached + + if (TNC->MinLevelTimer && Stream == 0) + continue; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + datalen=buffptr[1]; + Buffer = (char *)&buffptr[2]; // Data portion of frame + + Poll[2] = TNC->Streams[Stream].DEDStream; // Channel + + if (TNC->Streams[Stream].Connected) + { + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[3] = 0; // Data? + TNC->Streams[Stream].BytesTXed += datalen; + + Poll[4] = datalen - 1; + memcpy(&Poll[5], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], datalen); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + + // Command. Do some sanity checking and look for things to process locally + + Poll[3] = 1; // Command + datalen--; // Exclude CR + Buffer[datalen] = 0; // Null Terminate + _strupr(Buffer); + + if (_memicmp(Buffer, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if (memcmp(Buffer, "RADIO ", 6) == 0) + { + sprintf(&Buffer[40], "%d %s", TNC->Port, &Buffer[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &Buffer[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &Buffer[40]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(Buffer, "MYLEVEL ", 8) == 0) + { + Switchmode(TNC, Buffer[8] - '0'); + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (_memicmp(Buffer, "OVERRIDEBUSY", 12) == 0) + { + TNC->OverrideBusy = TRUE; + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if ((Stream == 0) && memcmp(Buffer, "RPACKET", 7) == 0) + { + TNC->HFPacket = TRUE; + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + return; + } + + if ((Stream == 0) && memcmp(Buffer, "PACTOR", 6) == 0) + { + TNC->HFPacket = FALSE; + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + return; + } + + if (Stream == 0 && Buffer[0] == 'C' && datalen > 2) // Pactor Connect + Poll[2] = TNC->Streams[0].DEDStream = 31; // Pactor Channel + + if (Stream == 0 && Buffer[0] == 'R' && Buffer[1] == 'C') // Robust Packet Connect + { + Poll[2] = TNC->Streams[0].DEDStream = 30; // Last Packet Channel + memmove(Buffer, &Buffer[1], datalen--); + } + + if (Buffer[0] == 'C' && datalen > 2) // Connect + { + if (*(++Buffer) == ' ') Buffer++; // Space isn't needed + + if ((memcmp(Buffer, "P1 ", 3) == 0) ||(memcmp(Buffer, "P2 ", 3) == 0)) + { + // Port Selector for Packet Connect convert to 2:CALL + + Buffer[0] = Buffer[1]; + Buffer[1] = ':'; + memmove(&Buffer[2], &Buffer[3], datalen--); + Buffer += 2; + } + + memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); + + TNC->Streams[Stream].Connecting = TRUE; + + if (Stream == 0) + { + // Send Call, Mode Command followed by connect + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); + + if (TNC->Streams[0].DEDStream == 30) + sprintf(TNC->Streams[0].CmdSet, "I%s\rPR\r%s\r", TNC->Streams[0].MyCall, (char *)buffptr+8); + else + if (TNC->Dragon) + sprintf(TNC->Streams[0].CmdSet, "I%s\r%s\r", TNC->Streams[0].MyCall, (char *)buffptr+8); + else + sprintf(TNC->Streams[0].CmdSet, "I%s\rPT\r%s\r", TNC->Streams[0].MyCall, (char *)buffptr+8); + + ReleaseBuffer(buffptr); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Send Mode Command now, save command, and wait up to 10 secs + // No, leave in Pactor, or Busy Detect won't work. Queue the whole conect sequence + + TNC->ConnectCmd = TNC->Streams[0].CmdSet; + TNC->Streams[0].CmdSet = NULL; + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; + TNC->Streams[Stream].Connecting = FALSE; // Not connecting Yet + + return; + } + } + + TNC->OverrideBusy = FALSE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[Stream].MyCall, TNC->Streams[Stream].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[Stream].CmdSet); + + TNC->Streams[0].InternalCmd = FALSE; + return; + } + } + + Poll[4] = datalen - 1; + memcpy(&Poll[5], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], datalen); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + return; + } + + // if frames outstanding, issue a poll + + if (TNC->Streams[Stream].FramesOutstanding) + { + Poll[2] = TNC->Streams[Stream].DEDStream; + Poll[3] = 0x1; // Command + Poll[4] = 0; // Len-1 + Poll[5] = 'L'; // Status + + CRCStuffAndSend(TNC, Poll, 6); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay--; + return; + } + + } + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + if (TNC->ReinitState == 3) + { + TNC->ReinitState = 0; + Poll[3] = 0x41; + } + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + CRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + + return; + +} +//#include "Mmsystem.h" + +static VOID ProcessIncomingCall(struct TNCINFO * TNC, struct STREAMINFO * STREAM, int Stream) +{ + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + char FreqAppl[10] = ""; // Frequecy-specific application + char DestCall[10]; + TRANSPORTENTRY * SESS; + struct WL2KInfo * WL2K = TNC->WL2K; + UCHAR * ptr; + UCHAR Buffer[80]; + UINT * buffptr; + + char * Call = STREAM->RemoteCall; + + if (Stream > 0 && Stream < 30) + ProcessIncommingConnectEx(TNC, Call, Stream, FALSE, TRUE); // No CTEXT + else + ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + if (SESS == NULL) + return; // Cant do much without one + + if (Stream > 0 && Stream < 30) + { + // Packet Connect. Much safer to process here, even though it means + // duplicating some code, or the Pactor/RP mode tests get very complicated + + strcpy(DestCall, STREAM->MyCall); + Debugprintf("PTC Packet Incoming Call - MYCALL = *%s*", DestCall); + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(DestCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + Debugprintf("Connect is to APPL %s", AppName); + + if (CheckAppl(TNC, AppName)) + { + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = MsgLen; + + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = strlen(Msg); + memcpy(&buffptr[2], Msg, strlen(Msg)); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->NeedDisc = 100; // 10 secs + } + return; + } + + // Not to a known appl - drop through to Node + + if (CTEXTLEN) + { + int Len = CTEXTLEN, CTPaclen = 100; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = CTPaclen; + memcpy(&buffptr[2], &CTEXTMSG[Next], CTPaclen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + Next += CTPaclen; + Len -= CTPaclen; + } + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = Len; + memcpy(&buffptr[2], &CTEXTMSG[Next], Len); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } + return; + } + + //Connect on HF port. May be Pactor or RP on some models + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", STREAM->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[0]->APPL[0]) + strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]); + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SESS->Mode = PleveltoMode[TNC->Streams[Stream].PTCStatus1]; + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->MinLevel > 1) + TNC->MinLevelTimer = 150; // Check we have reached right level + + // If an autoconnect APPL is defined, send it + + // See which application the connect is for + + strcpy(DestCall, STREAM->MyCall); + + Debugprintf("Pactor Incoming Call - MYCALL = *%s*", DestCall); + + if (TNC->UseAPPLCallsforPactor && strcmp(DestCall, TNC->NodeCall) != 0) // Not Connect to Node Call + { + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(DestCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + Debugprintf("Connect is to APPL %s", AppName); + + if (CheckAppl(TNC, AppName)) + { + int MsgLen = sprintf(Buffer, "%s\r", AppName); + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = strlen(Msg); + memcpy(&buffptr[2], Msg, strlen(Msg)); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->NeedDisc = 100; // 10 secs + } + return; + } + + // Not to a known appl - drop through to Node + + } + + if (TNC->HFPacket && TNC->UseAPPLCalls) + goto DontUseAPPLCmd; + + Debugprintf("Pactor Call is %s Freq Specific Appl is %s Freq is %s", + DestCall, FreqAppl, TNC->RIG->Valchar); + + if (FreqAppl[0]) // Frequency spcific APPL overrides TNC APPL + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + Debugprintf("Using Freq Specific Appl %s", FreqAppl); + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%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 + + Debugprintf("Using Default Appl %s", TNC->ApplCmd); + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + +DontUseAPPLCmd: + + if (FULL_CTEXT && CTEXTLEN && HFCTEXTLEN == 0) + { + int Len = CTEXTLEN, CTPaclen = 100; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = CTPaclen; + memcpy(&buffptr[2], &CTEXTMSG[Next], CTPaclen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + Next += CTPaclen; + Len -= CTPaclen; + } + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = Len; + memcpy(&buffptr[2], &CTEXTMSG[Next], Len); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } +} + +#pragma pack(1) + +typedef struct _MESSAGEY +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEY * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + + union + { /* array named screen */ + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + + }; + + UCHAR Padding[BUFFLEN - sizeof(time_t) - sizeof(VOID *) - 256 - 7 - 16]; + + time_t Timestamp; + VOID * Linkptr; // For ACKMODE processing + +}MESSAGEY; + +#pragma pack() + +static MESSAGEY Monframe; // I frames come in two parts. + +#define TIMESTAMP 352 + +MESSAGEY * AdjMsg; // Adjusted fir digis + + +static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + // Convert to ax.25 form and pass to monitor + + UCHAR * ptr, * starptr; + char * context; + + if (Msg[0] == 6) // Second part of I or UI + { + int len = Msg[1] +1; + + memcpy(AdjMsg->L2DATA, &Msg[2], len); + Monframe.LENGTH += len; + + time(&Monframe.Timestamp); + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + return; + } + + Monframe.LENGTH = 23; // Control Frame + Monframe.PORT = TNC->Port; + + AdjMsg = &Monframe; // Adjusted fir digis + ptr = strstr(Msg, "fm "); + + ConvToAX25(&ptr[3], Monframe.ORIGIN); + + ptr = strstr(ptr, "to "); + + ConvToAX25(&ptr[3], Monframe.DEST); + + ptr = strstr(ptr, "via "); + + if (ptr) + { + // We have digis + + char Save[100]; + char * fiddle; + + memcpy(Save, &ptr[4], 60); + + ptr = strtok_s(Save, " ", &context); +DigiLoop: + fiddle = (char *)AdjMsg; + fiddle += 7; + AdjMsg = (MESSAGEY *)fiddle; + + Monframe.LENGTH += 7; + + starptr = strchr(ptr, '*'); + if (starptr) + *(starptr) = 0; + + ConvToAX25(ptr, AdjMsg->ORIGIN); + + if (starptr) + AdjMsg->ORIGIN[6] |= 0x80; // Set end of address + + ptr = strtok_s(NULL, " ", &context); + + if (memcmp(ptr, "ctl", 3)) + goto DigiLoop; + } + + AdjMsg->ORIGIN[6] |= 1; // Set end of address + + ptr = strstr(Msg, "ctl "); + + if (memcmp(&ptr[4], "SABM", 4) == 0) + AdjMsg->CTL = 0x2f; + else + if (memcmp(&ptr[4], "DISC", 4) == 0) + AdjMsg->CTL = 0x43; + else + if (memcmp(&ptr[4], "UA", 2) == 0) + AdjMsg->CTL = 0x63; + else + if (memcmp(&ptr[4], "DM", 2) == 0) + AdjMsg->CTL = 0x0f; + else + if (memcmp(&ptr[4], "UI", 2) == 0) + AdjMsg->CTL = 0x03; + else + if (memcmp(&ptr[4], "RR", 2) == 0) + AdjMsg->CTL = 0x1 | (ptr[6] << 5); + else + if (memcmp(&ptr[4], "RNR", 3) == 0) + AdjMsg->CTL = 0x5 | (ptr[7] << 5); + else + if (memcmp(&ptr[4], "REJ", 3) == 0) + AdjMsg->CTL = 0x9 | (ptr[7] << 5); + else + if (memcmp(&ptr[4], "FRMR", 4) == 0) + AdjMsg->CTL = 0x87; + else + if (ptr[4] == 'I') + { + AdjMsg->CTL = (ptr[5] << 5) | (ptr[6] & 7) << 1 ; + } + + if (strchr(&ptr[4], '+')) + { + AdjMsg->CTL |= 0x10; + Monframe.DEST[6] |= 0x80; // SET COMMAND + } + + if (strchr(&ptr[4], '-')) + { + AdjMsg->CTL |= 0x10; + Monframe.ORIGIN[6] |= 0x80; // SET COMMAND + } + + if (Msg[0] == 5) // More to come + { + ptr = strstr(ptr, "pid "); + sscanf(&ptr[3], "%x", (int *)&AdjMsg->PID); + return; + } + + time(&Monframe.Timestamp); + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + +} +//1:fm G8BPQ to KD6PGI-1 ctl I11^ pid F0 +//fm KD6PGI-1 to G8BPQ ctl DISC+ + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // Queue it as we may have just sent data + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = malloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "D\r"); +} + + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + // Sending D twice should do a "Dirty Disconnect" + + // Try thst first. If it still doesn't disconnect maybe try restart + + unsigned char Resp[500] = ""; + char * Poll = &TNC->TXBuffer[0]; + int n; + + Debugprintf("Failed to disconnect TNC - trying a forced disconnect"); + + Poll[2] = 31; + Poll[3] = 1; + Poll[4] = 0; + Poll[5] = 'D'; + + CRCStuffAndSend(TNC, Poll, 6); + + // Wait for response before sending another + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + Poll[2] = 31; + Poll[3] = 1; + Poll[4] = 0; + Poll[5] = 'D'; + + CRCStuffAndSend(TNC, Poll, 6); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + // See if it worked + + Poll[2] = 254; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + + CRCStuffAndSend(TNC, Poll, 7); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + Debugprintf("PTC Status Now %x %x %x %x %x %x %x %x", + Resp[0], Resp[1], Resp[2], Resp[3], Resp[4], Resp[5], Resp[6], Resp[7]); + + TNC->Timeout = 0; + + return; + + // Maybe best just to restart the TNC + + if (TNC->PacketChannels == 0) // Not using packet + { + Debugprintf("Forced Disconnect Failed - restarting TNC"); + + // Ensure in Pactor + + if(TNC->Dragon == 0) + { + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x1; + TNC->TXBuffer[4] = 0x1; + memcpy(&TNC->TXBuffer[5], "PT", 2); + + CRCStuffAndSend(TNC, TNC->TXBuffer, 7); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + } + + Sleep(50); + ExitHost(TNC); + Sleep(50); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + TNC->Timeout = 0; + TNC->HostMode = FALSE; + TNC->ReinitState = 0; + + return; + } +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Status[80]; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + Debugprintf("SCS Pactor Close Complete - Stream = %d", Stream); + + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + + strcpy(STREAM->MyCall, TNC->NodeCall); + + if (Stream == 0 || TNC->HFPacket) + { + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + strcpy(TNC->WEB_TNCSTATE, "Free"); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + + if (TNC->HFPacket) + { + sprintf(STREAM->CmdSet, "I%s\rPR\r", TNC->NodeCall); + TNC->Streams[0].DEDStream = 30; // Packet Channel + Debugprintf("BPQ32 Session Closed - switch to Packet"); + } + else + { + if (TNC->Dragon) + sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); + else + sprintf(STREAM->CmdSet, "I%s\rPT\r", TNC->NodeCall); + + TNC->Streams[0].DEDStream = 31; // Pactor Channel + Debugprintf("BPQ32 Session Closed - switch to Pactor"); + } + } + else + sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); + + ReleaseOtherPorts(TNC); +} + +VOID DragonSuspentPort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); +} + +VOID DragonReleasePort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + + if (TNC->UseAPPLCallsforPactor && TNC->RIG && TNC->RIG != &TNC->DummyRig) + sprintf(STREAM->CmdSet, "I%s\r", TNC->RIG->FreqPtr[0]->APPLCALL); + else + sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); +} + + + + + + + +VOID SwitchToPacketOnly(struct TNCINFO * TNC) +{ + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); + sprintf(TNC->Streams[0].CmdSet, "PR\r"); + + TNC->HFPacket = TRUE; + TNC->Streams[0].DEDStream = 30; // Packet Channel + + TNC->SwitchToPactor = TNC->RobustTime; + + if (RIG_DEBUG) + Debugprintf("BPQ32 Scan - switch to Packet"); +} diff --git a/FBBRoutines.c b/FBBRoutines.c new file mode 100644 index 0000000..64886af --- /dev/null +++ b/FBBRoutines.c @@ -0,0 +1,2008 @@ +/* +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" + +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); + + +int MaxRXSize = 99999; +int MaxTXSize = 99999; + +struct FBBRestartData ** RestartData = NULL; +int RestartCount = 0; + +struct B2RestartData ** B2RestartRecs = NULL; +int B2RestartCount = 0; + +extern char ProperBaseDir[]; + +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)) + { + 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. Non'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 && CheckRejFilters(From, To, ATBBS, NULL, *Type)) + { + 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); + } + + 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 ((RestartRec->UserPointer == conn->UserPointer) + && (strcmp(RestartRec->TempMsg->bid, conn->TempMsg->bid) == 0)) + { + if (RestartRec->TempMsg->length <= offset) + { + conn->TempMsg->length = RestartRec->TempMsg->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--; + } + } + + // 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 + 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, 4); + 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 ((RestartRec->UserPointer == conn->UserPointer) + && (strcmp(RestartRec->TempMsg->bid, conn->TempMsg->bid) == 0)) + { + // Fund it, so reuse + + // If we have more data, reset retry count + + if (RestartRec->TempMsg->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->UserPointer = conn->UserPointer; + RestartRec->TempMsg = conn->TempMsg; + 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); + + WriteLogLine(conn, '|',Msg, len, LOG_BBS); +} + +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 ((RestartRec->UserPointer == conn->UserPointer) + && (strcmp(RestartRec->TempMsg->bid, FBBHeader->BID) == 0)) + { + char Msg[120]; + int len; + + RestartRec->Count++; + + if (RestartRec->Count > 3) + { + 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--; + return FALSE; + } + + len = sprintf_s(Msg, sizeof(Msg), "Restart Data found for %s - Requesting restart from %d", + FBBHeader->BID, RestartRec->TempMsg->length); + + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + return (RestartRec->TempMsg->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/FLDigi.c b/FLDigi.c new file mode 100644 index 0000000..0315b69 --- /dev/null +++ b/FLDigi.c @@ -0,0 +1,3861 @@ +/* +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 +*/ + +// +// FLARQ Emulator/FLDIGI Interface for BPQ32 +// + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + + +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define DLE 0x10 +#define SOH 1 +#define STX 2 +#define EOT 4 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; +int SemHeldByAPI; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static void ConnecttoFLDigiThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoFLDigi(); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len); +VOID ProcessFLDigiKISSPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); +VOID FLReleaseTNC(struct TNCINFO * TNC); +unsigned int CalcCRC(UCHAR * ptr, int Len); +VOID ARQTimer(struct TNCINFO * TNC); +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char *Input); +VOID SendXMLPoll(struct TNCINFO * TNC); +static int ProcessXMLData(int port); +VOID CheckFLDigiData(struct TNCINFO * TNC); +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType); +VOID FLSlowTimer(struct TNCINFO * TNC); +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg); + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + +char * strlop(char * buf, char delim); + +extern UCHAR BPQDirectory[]; + +#define MAXBPQPORTS 32 +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port +//static int MPSKtoBPQ_Q[MAXBPQPORTS+1]; // Frames for BPQ, indexed by BPQ Port +//static int BPQtoMPSK_Q[MAXBPQPORTS+1]; // Frames for MPSK. indexed by MPSK port. Only used it TCP session is blocked + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + +static char WindowTitle[] = "FLDIGI"; +static char ClassName[] = "FLDIGISTATUS"; +static int RigControlRow = 165; + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +int Blocksizes[10] = {0,2,4,8,16,32,64,128,256,512}; + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + +// for (Stream = 0; Stream <= 1; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + TNC->FLInfo->RAW = FALSE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + SuspendOtherPorts(TNC); // Dont allow connects on interlocked ports + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Cmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +/* len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + SendPacket(TNC->TCPDataSock, Cmd, len, 0); +*/ + } + } + + switch (fn) + { + case 7: + + // 100 mS Timer. + + // See if waiting for busy to clear before sending a connect + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send connect + + struct ARQINFO * ARQ = TNC->ARQInfo; + int SendLen; + char Reply[80]; + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + STREAM->Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + UINT * buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr[1]=39; + memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + TidyClose(TNC, 0); + } + } + + ARQTimer(TNC); + SendXMLPoll(TNC); + + TNC->SlowTimer--; + + if (TNC->SlowTimer < 0) + { + TNC->SlowTimer = 100; + FLSlowTimer(TNC); // 10 Secs + } + + return 0; + + case 1: // poll + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE && TNC->FLInfo->KISSMODE == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoFLDigi(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&readfs); + + if (TNC->CONNECTED || TNC->FLInfo->KISSMODE) + FD_SET(TNC->TCPDataSock,&readfs); + + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTED) + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED || TNC->FLInfo->KISSMODE) + FD_SET(TNC->TCPDataSock,&errorfs); + + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessXMLData(port); + } + + + if (FD_ISSET(TNC->TCPDataSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // If required, send signon + +// SendPacket(TNC->TCPDataSock,"\x1a", 1, 0); +// SendPacket(TNC->TCPDataSock,"DIGITAL MODE ?", 14, 0); +// SendPacket(TNC->TCPDataSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPDataSock,&errorfs) || FD_ISSET(TNC->TCPSock,&errorfs)) + { + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + + + // See if any frames for this port + + for (Stream = 0; Stream <= 1; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + int SendLen; + char Reply[256]; + int UILen; + char * UIMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + UILen = buffptr->LENGTH; + UILen -= 23; + UIMsg = buffptr->L2DATA; + + UIMsg[UILen] = 0; + + if (UILen < 129 && TNC->Streams[0].Attached == FALSE) // Be sensible! + { + // >00uG8BPQ:72 TestA + SendLen = sprintf(Reply, "u%s:72 %s", TNC->NodeCall, UIMsg); + SendPacket(TNC, Reply, SendLen); + } + ReleaseBuffer(buffptr); + } + + return (0); + + case 2: // send + + + if (!TNC->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + return (0); + } + else + { + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + if (STREAM->Connected) + TidyClose(TNC, buff->PORT); + + STREAM->ReportDISC = TRUE; // Tell Node + + TNC->FLInfo->MCASTMODE = FALSE; + + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MODEM ", 6) == 0) + { + buff->L2DATA[txlen -1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "MODEM:%s MODEM:", &buff->L2DATA[6]); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[6], 'S'); + } + + TNC->InternalCmd = TRUE; + return 1; + } + + if (_memicmp(buff->L2DATA, "FREQ ", 5) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%s WFF:", &buff->L2DATA[5]); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_carrier", (char *)atoi(&buff->L2DATA[5]), 'I'); + } + + TNC->InternalCmd = TRUE; + return 1; + } + + if (_memicmp(buff->L2DATA, "SQUELCH ", 8) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // Only works in KISS + + if (TNC->FLInfo->KISSMODE) + { + if (_memicmp(&buff->L2DATA[8], "ON", 2) == 0) + sprintf(txbuff, "KPSQL:ON KPSQL:"); + + else if (_memicmp(&buff->L2DATA[8], "OFF", 3) == 0) + sprintf(txbuff, "KPSQL:OFF KPSQL:"); + else + txlen = sprintf(txbuff, "KPSQLS:%s KPSQLS:", &buff->L2DATA[8]); + + SendKISSCommand(TNC, txbuff); + TNC->InternalCmd = TRUE; + } + return 1; + } + + if (_memicmp(buff->L2DATA, "KPSATT ", 7) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "KPSATT:%s KPSATT:", &buff->L2DATA[7]); + + SendKISSCommand(TNC, txbuff); + TNC->InternalCmd = TRUE; + } + + return 1; + } + + if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0) + { +// len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + +// if (TNC->MPSKInfo->TX) +// TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting +// else +// SendPacket(TNC->TCPDataSock, Command, len, 0); + +// TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(&buff->L2DATA[0], "MODE", 4) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + buff->L2DATA[txlen - 1] = 0; // Remove CR + + if (strstr(&buff->L2DATA[0], "RAW")) + TNC->FLInfo->RAW = TRUE; + else if (strstr(&buff->L2DATA[0], "KISS")) + TNC->FLInfo->RAW = FALSE; + else + { + buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Error - Invalid Mode\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; + } + + buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Ok - Mode is %s\r", + (TNC->FLInfo->RAW)?"RAW":"KISS"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MCAST", 5) == 0) + { + UINT * buffptr = GetBuff(); + + TNC->FLInfo->MCASTMODE = TRUE; + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + UINT * buffptr = GetBuff(); + int s = 0; + + while(s <= 1) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDig} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + struct ARQINFO * ARQ = TNC->ARQInfo; + int SendLen; + char Reply[80]; + + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = 48; // Not yet defined + TNC->FLInfo->FLARQ = FALSE; + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + strcpy(STREAM->RemoteCall, ptr); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + +//00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + STREAM->Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return 0; + } + + // Send any other command to FLDIGI + + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + char outbuff[1000]; + int newlen; + + buff->L2DATA[-1] = 6; // KISS Control + + newlen = KissEncode(&buff->L2DATA[-1], outbuff, txlen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[0], 'S'); + } + + TNC->InternalCmd = TRUE; + } + + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNC->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + { + // Busy if TX Window reached + + struct ARQINFO * ARQ = TNC->ARQInfo; + int Outstanding; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&TNC->Streams[0].BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); // 3rd Nibble is frames unacked + else + return TNCOK << 8 | STREAM->Disconnecting << 15; + + } + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->CONNECTED = FALSE; + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +int FindFLDIGI(char * Path) +{ + HANDLE hProc; + char ExeName[256] = ""; + char FLDIGIName[256]; + DWORD Pid = 0; + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (EnumProcessesPtr == NULL) + return 0; // Cant get PID + + if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) + return TRUE; + + // Path is to .bat, so need to strip extension of both names + + strcpy(FLDIGIName, Path); + strlop(FLDIGIName, '.'); + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + strlop(ExeName, '.'); + + if (_stricmp(ExeName, FLDIGIName) == 0) + return Processes[i]; + + } + } + } + return 0; +} + + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->WeStartedTNC = 0; // So we don't try again + + return 0; +} + +#endif + +static int RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL) + return 0; + + _strlwr(TNC->ProgramPath); + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart FLDIGI %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart FLDIGI - sento returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } +#ifndef LINBPQ + { + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + + // for some reason the program name must be lower case + + _strlwr(TNC->ProgramPath); + + ret = CreateProcess(TNC->ProgramPath, NULL, NULL, NULL, FALSE,0 ,NULL , NULL, &SInfo, &PInfo); + return ret; + } + } +#endif + return 0; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FLDigi Status" + "

FLDIGI Status

"); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID FLDIGISuspendPort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = FALSE; +} + +VOID FLDIGIReleasePort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = TRUE; +} + +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg) +{ + int txlen, rc; + char txbuff[256]; + char outbuff[256]; + + txlen = sprintf(txbuff, "%c%s", 6, Msg); + txlen = KissEncode(txbuff, outbuff, txlen); + rc = sendto(TNC->TCPDataSock, outbuff, txlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); +} + +VOID * FLDigiExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // The Socket to connect to is in IOBASE + // + + srand((unsigned int)time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + TNC->FLInfo->CONOK = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0 || PortEntry->PORTCONTROL.PORTPACLEN > 128) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + TNC->SuspendPortProc = FLDIGISuspendPort; + TNC->ReleasePortProc = FLDIGIReleasePort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_FLDIGI; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"FLDigi Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + +#ifndef LINBPQ + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) // Not running +#endif + TNC->WeStartedTNC = RestartTNC(TNC); // Always try if Linux + + if (TNC->FLInfo->KISSMODE) + { + // Open Datagram port + + SOCKET sock; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + struct hostent * HostEnt = NULL; + + TNC->FLInfo->CmdControl = 5; //Send params immediately + + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (HostEnt) + { + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + } + + TNC->TCPDataSock = sock = socket(AF_INET,SOCK_DGRAM,0); + + ioctl(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(TNC->TCPPort + 1); + + if (bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = WSAGetLastError(); + Consoleprintf("Bind Failed for UDP port %d - error code = %d", TNC->TCPPort, err); + } + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + } + else + ConnecttoFLDigi(port); + + time(&lasttime[port]); // Get initial time value + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode/CF", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0 Resent 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + return ExtProc; + +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct ARQINFO * ARQ; + struct FLINFO * FL; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + ARQ = TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + FL = TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 5 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + + TNC->FLInfo->KISSMODE = TRUE; // Default to KISS + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort + 40); // Defaults XML 7362 ARQ 7322 + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "TIMEOUT", 7) == 0) + TNC->Timeout = atoi(&buf[8]) * 10; + else + if (_memicmp(buf, "RETRIES", 7) == 0) + TNC->Retries = atoi(&buf[8]); + else + if (_memicmp(buf, "WINDOW", 6) == 0) + TNC->Window = atoi(&buf[7]); + else + if (_memicmp(buf, "ARQMODE", 7) == 0) + TNC->FLInfo->KISSMODE = FALSE; + else + if (_memicmp(buf, "DEFAULTMODEM", 12) == 0) // Send Beacon after each session + { + // Check that freq is also specified + + char * Freq = strchr(&buf[13], '/'); + + if (Freq) + { + *(Freq++) = 0; + strcpy(TNC->FLInfo->DefaultMode, &buf[13]); + TNC->FLInfo->DefaultFreq = atoi(Freq); + } + } + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoFLDigi(int port) +{ + _beginthread(ConnecttoFLDigiThread, 0, (void *)(size_t)port); + + return 0; +} + +static VOID ConnecttoFLDigiThread(void * portptr) +{ + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt = NULL; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("FLDIGI Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi Control socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FLDigi Control socket - error code = %d\n", err); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for FLDigi Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + else + { + sprintf(Msg, "Connect Failed for FLDigi Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + } + + return; +} + +VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d Resent %d Queued %d", + STREAM->BytesRXed, STREAM->BytesTXed, STREAM->BytesAcked, STREAM->BytesResent, STREAM->BytesOutstanding); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); +} + +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen) +{ + if (TNC->FLInfo->KISSMODE) + { + char KissMsg[1000]; + char outbuff[1000]; + int newlen; + + if (TNC->FLInfo->RAW) + { + // KISS RAW + + // Add CRC and Send + + unsigned short CRC; + char crcstring[6]; + + KissMsg[0] = 7; // KISS Raw + KissMsg[1] = 1; // SOH + KissMsg[2] = '0'; // Version + KissMsg[3] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&KissMsg[4], Msg, MsgLen +1 ); // Get terminating NULL + + CRC = CalcCRC(KissMsg + 1, MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(KissMsg, crcstring); + MsgLen += 9; + } + else + { + // Normal KISS + + KissMsg[0] = 0; // KISS Control + KissMsg[1] = TNC->ARQInfo->FarStream; + memcpy(&KissMsg[2], Msg, MsgLen); + MsgLen += 2; + } + + newlen = KissEncode(KissMsg, outbuff, MsgLen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + + SendKISSCommand(TNC, "TXBUF:"); + + } + else + { + // ARQ Scoket + + // Add Header, CRC and Send + + unsigned short CRC; + char crcstring[6]; + char outbuff[1000]; + + outbuff[0] = 1; // SOH + outbuff[1] = '0'; // Version + outbuff[2] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&outbuff[3], Msg, MsgLen + 1); + + CRC = CalcCRC(outbuff , MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(outbuff, crcstring); + MsgLen += 8; + + send(TNC->TCPDataSock, outbuff, MsgLen, 0); + } +} + +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW); + +static int ProcessReceivedData(int port) +{ + int bytes, used, bytesleft; + int i; + char ErrMsg[255]; + unsigned char MessageBuff[1500]; + unsigned char * Message = MessageBuff; + unsigned char * MessageBase = MessageBuff; + + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + // If using KISS/UDP interface use recvfrom + + if (FL->KISSMODE) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + unsigned char * KissEnd; + + bytesleft = recvfrom(TNC->TCPDataSock, Message, 1500, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (bytesleft < 0) + { + int err = WSAGetLastError(); + // if (err != 11) + // printf("KISS Error %d %d\n", nLength, err); + bytes = 0; + } + + while (bytesleft > 0) + { + unsigned char * in; + unsigned char * out; + unsigned char c; + + if (bytesleft < 3) + return 0; + + if (Message[0] != FEND) + return 0; // Duff + + Message = MessageBase; + in = out = &Message[2]; + + // We may have more than one KISS message in a packet + + KissEnd = memchr(&Message[2], FEND, bytesleft ); + + if (KissEnd == 0) + return 0; // Duff + + *(KissEnd) = 0; + + used = (int)(KissEnd - Message + 1); + + bytesleft -= used; + bytes = used; + + MessageBase += used; + + if (Message[1] == 6) // KISS Command + { + UCHAR * ptr = strchr(&Message[2], FEND); + + if (ptr) *ptr = 0; // Null Terminate + + if (bytes > 250) + Message[250] = 0; + + FL->Responding = 5; + + if (TNC->TNCOK == 0) + { + TNC->TNCOK = TRUE; + TNC->CONNECTED = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + // Trap BUSY fiest - there are lots of them, and they are likely to be confused + // with tesponses to Interactive commands + + if (memcmp(&Message[2], "BUSY", 4) == 0) + { + BOOL Changed = FALSE; + + if (Message[7] == 'T' && FL->Busy == FALSE) + { + TNC->Busy = FL->Busy = TRUE; + Changed = TRUE; + } + else + { + if (Message[7] == 'F' && FL->Busy == TRUE) + { + TNC->Busy = FL->Busy = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDIGI} Ok %s\r", &Message[2]); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + // Drop through in case need to extract info from command + } + + // Auto Command + +// Debugprintf("%d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]); + + if (memcmp(&Message[2], "FLSTAT", 4) == 0) + { + if (strstr(&Message[2], "FLSTAT:INIT")) + { + // FLDIGI Reloaded - set parmas + SendKISSCommand(TNC, "RSIDBCAST:ON TRXSBCAST:ON TXBEBCAST:ON KISSRAW:ON"); + } + continue; + } + + if (memcmp(&Message[2], "TRXS", 4) == 0) + { + char * ptr1, * context; + BOOL Changed = FALSE; + + ptr1 = strtok_s(&Message[7], ",", &context); + + if (strstr(ptr1, "TX")) + { + if (TNC->FLInfo->TX == FALSE) + { + TNC->FLInfo->TX = TRUE; + Changed = TRUE; + } + } + else + { + if (TNC->FLInfo->TX) + { + TNC->FLInfo->TX = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (memcmp(&Message[2], "TXBUF:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + STREAM->BytesOutstanding = atoi(ptr1); + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "TXBE:", 5) == 0) + { + STREAM->BytesOutstanding = 0; + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "RSIDN:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + + TNC->FLInfo->CenterFreq = atoi(ptr1); + ptr1 = strtok_s(NULL, ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "MODEM:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "WFF:", 4) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[6], ",", &context); + TNC->FLInfo->CenterFreq = atoi(ptr1); + } + + sprintf(TNC->WEB_MODE, "%s/%d", TNC->FLInfo->CurrentMode, TNC->FLInfo->CenterFreq); + SetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + continue; + } + + if (Message[1] == 7) // Not Normal Data + { + // "RAW" Mode. Just process as if received from TCP Socket Interface + + ProcessFLDigiPacket(TNC, &Message[2] , bytes - 3); // Data may be for another port + continue; + } + + bytes -= 3; // Two FEND and Control + + // Undo KISS + + while (bytes) + { + bytes--; + + c = *(in++); + + if (c == FESC) + { + c = *(in++); + bytes--; + + if (c == TFESC) + c = FESC; + else if (c == TFEND) + c = FEND; + } + *(out++) = c; + } + ProcessFLDigiData(TNC, &Message[3], (int)(out - &Message[3]), Message[2], FALSE); // KISS not RAW + } + return 0; + } + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPDataSock, Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPDataSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessFLDigiPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + + +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + char c; + struct FLINFO * FL = TNC->FLInfo; + + + if (TNC->FLInfo->MCASTMODE) + { + if (TNC->Streams[0].Attached == 0) + return; + + while(Len) + { + c = *(MPTR++); + + if (TNC->InPacket) + { + TNC->DataBuffer[TNC->DataLen++] = c; + + // Sanity Check + + if (TNC->DataLen == 6) + { + char * ptr = &TNC->DataBuffer[1]; + + if (memcmp(ptr, "DATA ", 5) == 0 || + memcmp(ptr, "PROG ", 5) == 0 || + memcmp(ptr, "FILE ", 5) == 0 || + memcmp(ptr, "SIZE ", 5) == 0 || + memcmp(ptr, "DESC ", 5) == 0 || + memcmp(ptr, "CNTL ", 5) == 0 || + memcmp(ptr, "ID ", 3) == 0) + + { + } + else + { + // False Trigger, try again + + TNC->InPacket = FALSE; + } + + } + else + { + if (TNC->InData) + { + if (--TNC->MCASTLen == 0) + { + // Got a packet + + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + buffptr = GetBuff(); + + if (buffptr) + { + TNC->DataBuffer[TNC->DataLen++] = 13; // Keep Tidy + + buffptr[1] = TNC->DataLen; + memcpy(&buffptr[2], &TNC->DataBuffer[0], TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + TNC->InPacket = FALSE; + } + } + else + { + // Looking for > + + if (TNC->DataLen == 16) + { + // Not found it + + TNC->InPacket = FALSE; + } + else + { + if (c == '>') + { + // Got Header - extract Length + + char * ptr; + int len; + + ptr = strchr(TNC->DataBuffer, ' '); + + if (ptr) + { + len = atoi(ptr); + + if (len) + { + TNC->InData = TRUE; + TNC->MCASTLen = len; + } + } + } + } + } + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + } + else + { + // Look for '<' + + if (c == '<') + { + TNC->DataBuffer[0] = c; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + TNC->InData = FALSE; + } + } + Len--; + } + return; + } + // Look for SOH/EOT delimiters. May Have several SOH before EOTTNC->FL + + while(Len) + { + c = *(MPTR++); + + switch (c) + { + case 01: // New Packet + + if (TNC->InPacket) + CheckFLDigiData(TNC); + + TNC->DataBuffer[0] = 1; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + break; + + case 04: + + if (TNC->InPacket) + CheckFLDigiData(TNC); + TNC->DataLen = 0; + TNC->InPacket = FALSE; + + break; + + default: + + if (TNC->InPacket) + { + if (TNC->DataLen == 1) + { + if (c != '0' && c != '1') + { + // Drop if not Protocol '0' or '1' - this should eliminate almost all noise packets + + TNC->InPacket = 0; + break; + } + } + TNC->DataBuffer[TNC->DataLen++] = c; + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + + } + Len--; + } +} +VOID CheckFLDigiData(struct TNCINFO * TNC) +{ + UCHAR * Input = &TNC->DataBuffer[0]; + int Len = TNC->DataLen - 4; // Not including CRC + unsigned short CRC; + char crcstring[6]; + + if (Len < 0) + return; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // RAW format message, either from ARQ Scoket or RAW KISS + + // Check Checksum + + CRC = CalcCRC(Input , Len); + + sprintf(crcstring, "%04X", CRC); + + if (memcmp(&Input[Len], crcstring, 4) !=0) + { + // CRC Error - could just be noise + +// Debugprintf("%s %s", crcstring, Input); + return; + } + ProcessFLDigiData(TNC, &Input[3], Len - 3, Input[2], TRUE); // From RAW +} +/* +VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // ARQ Packet from KISS-Like Hardware + + struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER]; + UCHAR * Input; + int Len; + + if (TNC == NULL) + { + // Set up TNC info + + TNC = TNCInfo[PORT->PORTNUMBER] = zalloc(sizeof(struct TNCINFO)); + TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 10 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + } + + Input = &Buffer->DEST[0]; + Len = Buffer->LENGTH - 7; // Not including CRC + + // Look for attach on any call + + ProcessFLDigiData(TNC, Input, Len); +} +*/ +static int Stuff(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * ptr = inbuff; + + // DLE Escape DLE, SOH, EOT + + for (i = 0; i < len; i++) + { + c = *(ptr++); + +// if (c == 0 || c == DLE || c == SOH || c == EOT) + if (c < 32 && c != 10 && c != 13 && c != 8) + { + outbuff[txptr++] = DLE; + + // if between 0 and 0x1F, Add 40, + // if > x80 and less than 0xa0 subtract 20 + + c += 0x40; + } + outbuff[txptr++]=c; + } + + return txptr; +} + + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * outbuff = inbuff; + UCHAR * ptr = inbuff; + + // This unstuffs into the input buffer + + for (i = 0; i < len; i++) + { + c = *(ptr++); + + if (c == DLE) + { + c = *(ptr++); + i++; + + // if between 0x40 and 0x5F, subtract 0x40, + // else add 0x20 (so we can send chars 80-9f without a double DLE) + + if (c < 0x60) + c -= 0x40; + else + c += 0x20; + } + outbuff[txptr++] = c; + } + + return txptr; +} + +unsigned int crcval = 0xFFFF; + +void update(char c) +{ + int i; + + crcval ^= c & 255; + for (i = 0; i < 8; ++i) + { + if (crcval & 1) + crcval = (crcval >> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} +/* + +00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06 +00kG8BPQ:24 G8BPQ 4 85F9B + +00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5 (128, 5) + +,00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 5 89FCA + +First no sees to be a connection counter. Next may be stream + + +08s___ABFC +08tG8BPQ:73 xxx 33FA +00tG8BPQ:73 yyy 99A3 +08dG8BPQ:90986C +00bG8BPQ:911207 + +call:90 for dis 91 for dis ack 73 for chat) + +08pG8BPQ?__645E +00s_??4235 + +08pG8BPQ?__645E +00s_??4235 + +i Ident +c Connect +k Connect Ack +r Connect NAK +d Disconnect req +s Data Ack/ Retransmit Req )status) +p Poll +f Format Fail +b dis ack +t talk + +a Abort +o Abort ACK + + +00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 6 49A3A +08s___ABFC +08 ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::90 +ARQ::STX +//FLARQ COMPOSER +Date: 09/01/2014 23:24:42 +To: gm8bpq +From: +SubjectA0E0 +08!: Test + +Test Message + +ARQ::ETX +F0F2 +08pG8BPQ!__623E +08pG8BPQ!__623E +08pG8BPQ!__623E + + + + +*/ +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW) +{ + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CTRL = Input[0]; + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + + int SendLen; + char Reply[80]; + + + // Process Message + + // This processes eitrher message from the KISS or RAW interfaces. + // Headers and RAW checksum have been removed, so packet starts with Control Byte + + // Only a connect request is allowed with no session, so check first + + if (CTRL == 'c') + { + // Connect Request + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = TNC->Window; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + if (FL->CONOK == FALSE) + return; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + // See if for us + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (_stricmp(call2, Appl) == 0) + break; + } + + if (App > 31) + if (strcmp(TNC->NodeCall, call2) !=0) + return; // Not Appl or Port/Node Call + + ptr = strtok_s(NULL, " ", &context); + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + BlockSize = atoi(ptr); + + if (ARQ->ARQState) + { + // We have already received a connect request - just ACK it + + goto AckConnectRequest; + } + + // Get a Session + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, call1, 0, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + strcpy(STREAM->MyCall, call2); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, call2, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, call2); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connect Pending"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->FarStream = FarStream; + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->ARQState = ARQ_ACTIVE; + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = FarStream; // Not Yet defined + if (strcmp(port1, "1025") == 0) + { + FL->FLARQ = TRUE; // From FLARQ + ARQ->OurStream = '8'; // FLARQ Ignores what we send + } + else + FL->FLARQ = FALSE; // From other app (eg BPQ) + + FL->RAW = RAW; + + STREAM->NeedDisc = 0; + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char Buffer[32]; + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + STREAM->NeedDisc = 50; // 1 sec + } + } + + ARQ->TXWindow = Window; + + if (BlockSize < 4) BlockSize = 4; + if (BlockSize < 9) BlockSize = 9; + + ARQ->MaxBlock = Blocksizes[BlockSize]; + + + ARQ->ARQTimer = 10; // To force CTEXT to be Queued + + if (App == 32) + { + // Connect to Node - send CTEXT + + if (HFCTEXTLEN > 1) + { + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = HFCTEXTLEN; + memcpy(&buffptr[2], HFCTEXT, HFCTEXTLEN); + SendARQData(TNC, buffptr); + } + } + } + + if (STREAM->NeedDisc) + { + // Send Not Avail + + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = sprintf((char *)&buffptr[2], "Application Not Available\n"); + SendARQData(TNC, buffptr); + } + } + +AckConnectRequest: + + SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_CONNECTACK; + + return; + } + + // All others need a session + +// if (!STREAM->Connected && !STREAM->Connecting) +// return; + + if (CTRL == 'k') + { + // Connect ACK + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = 16; + + char Reply[80]; + int ReplyLen; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ptr = strtok_s(NULL, " ", &context); + if (ptr) + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + if (ptr) + BlockSize = atoi(ptr); + + if (STREAM->Connected) + goto SendKReply; // Repeated ACK + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + STREAM->Connected = TRUE; + + ARQ->ARQTimerState = 0; + ARQ->ARQTimer = 0; + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z'); + + ARQ->ARQTimerState = 0; + ARQ->FarStream = FarStream; + ARQ->TXWindow = TNC->Window; + ARQ->MaxBlock = Blocksizes[BlockSize]; + + ARQ->ARQState = ARQ_ACTIVE; + + STREAM->NeedDisc = 0; + + buffptr = GetBuff(); + + if (buffptr) + { + ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall); + + buffptr[1] = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + +SendKReply: + + // Reply with status + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + // All others need a session + + //if (!STREAM->Connected) + // return; + + + if (CTRL == 's') + { + // Status + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; // Stop retry timer + Input[Len] = 0; + ProcessARQStatus(TNC, ARQ, &Input[1]); + + return; + } + + if (CTRL == 'p') + { + // Poll + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " \x1A", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + else + ARQ->TurnroundTimer = 15; // Allow us to send it all acked + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + return; + } + + + if (CTRL == 'a') + { + // Abort. Send Abort ACK - same as + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " :", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + if (CTRL == 'i') + { + // Ident + + return; + } + + if (CTRL == 't') + { + // Talk - not sure what to do with these + + return; + } + + if (CTRL == 'd') + { + // Disconnect Request + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + + // As the Disc ACK isn't repeated, we have to clear session now + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = 0; + + SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->Retries = 2; + return; + } + + if (CTRL == 'b') + { + // Disconnect ACK + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + ARQ->ARQState = 0; + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + FLReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + } + + if (CTRL == 'u') + { + // Beacon + + //>00uGM8BPQ:72 GM8BPQ TestingAD67 + + char * Call = &Input[1]; + strlop(Call, ':'); + + UpdateMH(TNC, Call, '!', 0); + return; + } + + if (STREAM->Connected) + { + if (Channel != ARQ->OurStream) + return; // Wrong Session + + if (CTRL >= ' ' && CTRL < 96) + { + // ARQ Data + + int Seq = CTRL - 32; + int Work; + +// if (rand() % 5 == 2) +// { +// Debugprintf("Dropping %d", Seq); +// return; +// } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return; // Sould never run out, but cant do much else + + // Remove any DLE transparency + + if (TNC->FLInfo->KISSMODE) + Len -= 1; + else + Len = UnStuff(&Input[1], Len - 1); + + buffptr[1] = Len; + memcpy(&buffptr[2], &Input[1], Len); + STREAM->BytesRXed += Len; + + UpdateStatsLine(TNC, STREAM); + + // Safest always to save, then see what we can process + + if (ARQ->RXHOLDQ[Seq]) + { + // Wot! Shouldn't happen + + ReleaseBuffer(ARQ->RXHOLDQ[Seq]); +// Debugprintf("ARQ Seq %d Duplicate"); + } + + ARQ->RXHOLDQ[Seq] = buffptr; +// Debugprintf("ARQ saving %d", Seq); + + // If this is higher that highest received, save. But beware of wrap' + + // Hi = 2, Seq = 60 dont save s=h = 58 + // Hi = 10 Seq = 12 save s-h = 2 + // Hi = 14 Seq = 10 dont save s-h = -4 + // Hi = 60 Seq = 2 save s-h = -58 + + Work = Seq - ARQ->RXHighest; + + if ((Work > 0 && Work < 32) || Work < -32) + ARQ->RXHighest = Seq; + + // We may now be able to process some + + Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need + + while (ARQ->RXHOLDQ[Work]) + { + // We have it + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]); +// ReleaseBuffer(ARQ->RXHOLDQ[Work]); + + ARQ->RXHOLDQ[Work] = NULL; +// Debugprintf("Processing %d from Q", Work); + + ARQ->RXNoGaps = Work; + Work = (Work + 1) & 63; // The next one we need + } + + ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data + return; + } + } +} + + +VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer) +{ + // Send Data, saving a copy until acked. + + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + UCHAR * ptr; + int Origlen = Buffer[1]; + int Stuffedlen; + + ARQ->TXSeq++; + ARQ->TXSeq &= 63; + + SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32); + + ptr = (UCHAR *)&Buffer[2]; // Start of data; + + ptr[Buffer[1]] = 0; + + if (memcmp(ptr, "ARQ:", 4) == 0) + { + // FLARQ Mail/FIle transfer. Turn off CR > LF translate (used for terminal mode) + + FL->FLARQ = FALSE; + } + + if (FL->FLARQ) + { + // Terminal Mode. Need to convert CR to LF so it displays in FLARQ Window + + ptr = strchr(ptr, 13); + + while (ptr) + { + *(ptr++) = 10; // Replace CR with LF + ptr = strchr(ptr, 13); + } + } + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Origlen); + SendLen += Origlen; + } + else + { + Stuffedlen = Stuff((UCHAR *)&Buffer[2], &TXBuffer[SendLen], Origlen); + SendLen += Stuffedlen; + } + + TXBuffer[SendLen] = 0; + +// if (rand() % 5 == 2) +// Debugprintf("Dropping %d", ARQ->TXSeq); +// else + + ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer; + + STREAM->BytesTXed += Origlen; + + UpdateStatsLine(TNC, STREAM); + + // if waiting for ack, don't send, just queue. Will be sent when ack received + + if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA) + { + SendPacket(TNC, TXBuffer, SendLen); + ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + } + else + STREAM->BytesResent -= Origlen; // So wont be included in resent bytes +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Reply[80]; + int SendLen; + + struct ARQINFO * ARQ = TNC->ARQInfo; + + SendLen = sprintf(Reply, "d%s:90", TNC->Streams[0].MyCall); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_DISC; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + FLReleaseTNC(TNC); +} + +VOID FLReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // if a default Modem is defined, select it + + if (TNC->FLInfo->DefaultMode[0]) + { + char txbuff[80]; + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%d MODEM:%s MODEM: WFF:", TNC->FLInfo->DefaultFreq, TNC->FLInfo->DefaultMode); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", TNC->FLInfo->DefaultMode, 'S'); + SendXMLCommand(TNC, "modem.set_carrier", (char *)TNC->FLInfo->DefaultFreq, 'I'); + } + } + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Queue to be sent after TXDELAY + + memcpy(ARQ->TXMsg, Msg, MsgLen + 1); + ARQ->TXLen = MsgLen; + ARQ->TXDelay = 15; // Try 1500 ms +} + +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Used for Messages that need a reply. Save, send and set timeout + + memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null + ARQ->LastLen = MsgLen; + + // Delay the send for a short while Just use the timeout code + +// SendPacket(sock, Msg, MsgLen, 0); + ARQ->ARQTimer = 1; // Try 500 ms + ARQ->Retries = TNC->Retries + 1; // First timout is rthe real send + + return; +} + + +VOID ARQTimer(struct TNCINFO * TNC) +{ + struct ARQINFO * ARQ = TNC->ARQInfo; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int SendLen; + char Reply[80]; + struct FLINFO * FL = TNC->FLInfo; + + //Send frames, unless held by TurnroundTimer or Window + + int Outstanding; + + // Use new BUSY: poll to detect busy state + + if (FL->TX == FALSE) + if (TNC->FLInfo->KISSMODE) + SendKISSCommand(TNC, "BUSY:"); // Send every poll for now - may need to optimize later + + +/* +// Use Received chars as a rough channel active indicator + + FL->BusyTimer++; + + if (FL->BusyTimer > 4) + { + FL->BusyTimer = 0; + + if (FL->BusyCounter > 2) // 2 chars in last .3 secs + FL->Busy = TRUE; + else + FL->Busy = FALSE; + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + FL->BusyCounter = 0; + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + +*/ // TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't + // need to check for busy (or anything else (I think!) + + if (ARQ->TXDelay) + { + ARQ->TXDelay--; + + if (ARQ->TXDelay) + return; + + SendPacket(TNC, ARQ->TXMsg, ARQ->TXLen); + } + + // if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end) + + if (ARQ->ARQTimerState == ARQ_WAITDATA) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + + ARQ->ARQTimer--; + + if (ARQ->ARQTimer > 0) + return; // Timer Still Running + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + +// Debugprintf("Sending Poll"); + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + } + + // TrunroundTimer is used to allow time for far end to revert to RX + + if (ARQ->TurnroundTimer && !FL->Busy) + ARQ->TurnroundTimer--; + + if (ARQ->TurnroundTimer == 0) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + } + + if (ARQ->ARQTimer) + { + if (FL->TX || FL->Busy) + { + // Only decrement if running send poll timer + + if (ARQ->ARQTimerState != ARQ_WAITDATA) + return; + } + + ARQ->ARQTimer--; + { + if (ARQ->ARQTimer) + return; // Timer Still Running + } + + ARQ->Retries--; + + if (ARQ->Retries) + { + // Retry Current Message + + SendPacket(TNC, ARQ->LastMsg, ARQ->LastLen); + ARQ->ARQTimer = TNC->Timeout + (rand() % 30); + + return; + } + + // Retried out. + + switch (ARQ->ARQTimerState) + { + case ARQ_WAITDATA: + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + case ARQ_CONNECTING: + + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + // Send Disc to TNC in case it got the Connects, but we missed the ACKs + + TidyClose(TNC, 0); + ARQ->Retries = 2; // First timout is the real send, only send once + STREAM->Connecting = FALSE; // Back to Command Mode + ARQ->ARQState = FALSE; + + break; + + case ARQ_WAITACK: + case ARQ_CONNECTACK: + case ARQ_DISC: + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; + ARQ->ARQState = FALSE; + + while (STREAM->PACTORtoBPQ_Q) + ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q)); + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + break; + + } + } +} + +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char * Input) +{ + // Release any acked frames and resend any outstanding + + int LastInSeq = Input[1] - 32; + int LastRXed = Input[2] - 32; + int FirstUnAcked = ARQ->TXLastACK; + int n = (int)strlen(Input) - 3; + char * ptr; + int NexttoResend; + int First, Last, Outstanding; + UINT * Buffer; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int Acked = 0; + + // First status is an ack of Connect ACK + + if (ARQ->ARQTimerState == ARQ_CONNECTACK) + { + ARQ->Retries = 0; + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + } + + // Release all up to LastInSeq + + while (FirstUnAcked != LastInSeq) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + ARQ->TXLastACK = FirstUnAcked; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (FirstUnAcked == ARQ->TXSeq) + { + UpdateStatsLine(TNC, STREAM); + ARQ->NoAckRetries = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; // All Acked + } + // Release any not in retry list up to LastRXed. + + ptr = &Input[3]; + + while (n) + { + NexttoResend = *(ptr++) - 32; + + FirstUnAcked++; + FirstUnAcked &= 63; + + while (FirstUnAcked != NexttoResend) + { + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + + FirstUnAcked++; + FirstUnAcked &= 63; + } + + // We don't ACK this one. Process any more resend values, then release up to LastRXed. + + n--; + } + + // Release rest up to LastRXed + + while (FirstUnAcked != LastRXed) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + // Resend anything in TX Buffer (From LastACK to TXSeq + + Last = ARQ->TXSeq + 1; + Last &= 63; + + First = LastInSeq; + + while (First != Last) + { + First++; + First &= 63; + + if(ARQ->TXHOLDQ[First]) + { + UINT * Buffer = ARQ->TXHOLDQ[First]; + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + +// Debugprintf("Resend %d", First); + + STREAM->BytesResent += Buffer[1]; + + SendLen = sprintf(TXBuffer, "%c", First + 32); + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Buffer[1]); + SendLen += Buffer[1]; + } + else + SendLen += Stuff((UCHAR *)&Buffer[2], &TXBuffer[SendLen], Buffer[1]); + + TXBuffer[SendLen] = 0; + + SendPacket(TNC, TXBuffer, SendLen); + + ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + + if (Acked == 0) + { + // Nothing acked by this statis message + + Acked = 0; // Dont count more thna once + ARQ->NoAckRetries++; + if (ARQ->NoAckRetries > TNC->Retries) + { + // Too many retries - just disconnect + + TidyClose(TNC, 0); + return; + } + } + } + } + + UpdateStatsLine(TNC, STREAM); +} + +VOID FLSlowTimer(struct TNCINFO * TNC) +{ + struct FLINFO * FL = TNC->FLInfo; + + // Entered every 10 secs + + // if in MCAST mode, clear KILL timer (MCAST RX can run for a long time + + if (TNC->FLInfo->MCASTMODE) + { + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + SESS->L4KILLTIMER = 0; + } + + if (FL->KISSMODE) + { + if (FL->Responding) + FL->Responding--; + + if (FL->Responding == 0) + { + TNC->TNCOK = FALSE; + TNC->CONNECTED = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to FLDIGI lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Set basic params till it responds + } + + FL->CmdControl++; + + if (FL->CmdControl > 5) // Every Minute + { + FL->CmdControl = 0; + + SendKISSCommand(TNC, "FLSTAT: MODEM: WFF:"); + } + + SendKISSCommand(TNC, "TRXS: TXBUF:"); // In case TX/RX report is missed + } +} + +static int ProcessXMLData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + char * ptr1, * ptr2, *ptr3; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data. Assume for now we get a whole packet + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr3 = strstr(ptr1, ""); + + if (ptr3) + { + ptr1 = ptr3 + 4; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDIGI} Ok Was %s\r", ptr1); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + } + + return 0; + } + + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr2 = strstr(ptr1, ""); + + if (ptr2) + { + ptr2 += 8; + ptr1 = ptr2; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (strcmp(FL->LastXML, "modem.get_name") == 0) + { + strcpy(TNC->WEB_MODE, ptr1); + SetWindowText(TNC->xIDC_MODE, ptr1); + } + else if (strcmp(FL->LastXML, "main.get_trx_state") == 0) + { + if (strcmp(ptr1, "TX") == 0) + FL->TX = TRUE; + else + FL->TX = FALSE; + + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (strcmp(FL->LastXML, "main.get_squelch") == 0) + { +/* + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); +*/ + return 0; + } +/* + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } +*/ + + } + + return (0); + +} + + + +char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + char ValueString[256] =""; + + if (!TNC->CONNECTED || TNC->FLInfo->KISSMODE) + return; + + if (Value) + if (ParamType == 'S') + sprintf(ValueString, "%s", Value); + else + sprintf(ValueString, "%d", Value); + + strcpy(FL->LastXML, Command); + Len = sprintf(ReqBuf, Req, FL->LastXML, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; +} + +VOID SendXMLPoll(struct TNCINFO * TNC) +{ + int Len; + char ReqBuf[256]; + char SendBuff[256]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + + if (!TNC->CONNECTED) + return; + + if (TNC->FLInfo->KISSMODE) + return; + + if (ARQ->ARQTimer) + { + // if timer is running, poll fot TX State + + strcpy(FL->LastXML, "main.get_trx_state"); + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; + } + + FL->XMLControl++; + + + if (FL->XMLControl > 9) + { + FL->XMLControl = 0; + strcpy(FL->LastXML, "modem.get_name"); + } + else + { + if (FL->XMLControl == 5) + strcpy(FL->LastXML, "main.get_trx_state"); + else + return; + } + + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); +} + +// sudo add-apt-repository ppa:kamalmostafa/fldigi + + diff --git a/FLDigi64.c b/FLDigi64.c new file mode 100644 index 0000000..4ca0a8d --- /dev/null +++ b/FLDigi64.c @@ -0,0 +1,3860 @@ +/* +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 +*/ + +// +// FLARQ Emulator/FLDIGI Interface for BPQ32 +// + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + + +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define DLE 0x10 +#define SOH 1 +#define STX 2 +#define EOT 4 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; +int SemHeldByAPI; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +static void ConnecttoFLDigiThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoFLDigi(); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len); +VOID ProcessFLDigiKISSPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); +VOID FLReleaseTNC(struct TNCINFO * TNC); +unsigned int CalcCRC(UCHAR * ptr, int Len); +VOID ARQTimer(struct TNCINFO * TNC); +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char *Input); +VOID SendXMLPoll(struct TNCINFO * TNC); +static int ProcessXMLData(int port); +VOID CheckFLDigiData(struct TNCINFO * TNC); +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType); +VOID FLSlowTimer(struct TNCINFO * TNC); +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg); + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + +char * strlop(char * buf, char delim); + +extern UCHAR BPQDirectory[]; + +#define MAXBPQPORTS 32 +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port +//static int MPSKtoBPQ_Q[MAXBPQPORTS+1]; // Frames for BPQ, indexed by BPQ Port +//static int BPQtoMPSK_Q[MAXBPQPORTS+1]; // Frames for MPSK. indexed by MPSK port. Only used it TCP session is blocked + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + +static char WindowTitle[] = "FLDIGI"; +static char ClassName[] = "FLDIGISTATUS"; +static int RigControlRow = 165; + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +int Blocksizes[10] = {0,2,4,8,16,32,64,128,256,512}; + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + +// for (Stream = 0; Stream <= 1; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + TNC->FLInfo->RAW = FALSE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + SuspendOtherPorts(TNC); // Dont allow connects on interlocked ports + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Cmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +/* len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + SendPacket(TNC->TCPDataSock, Cmd, len, 0); +*/ + } + } + + switch (fn) + { + case 7: + + // 100 mS Timer. + + // See if waiting for busy to clear before sending a connect + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send connect + + struct ARQINFO * ARQ = TNC->ARQInfo; + int SendLen; + char Reply[80]; + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + STREAM->Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + UINT * buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr[1]=39; + memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + TidyClose(TNC, 0); + } + } + + ARQTimer(TNC); + SendXMLPoll(TNC); + + TNC->SlowTimer--; + + if (TNC->SlowTimer < 0) + { + TNC->SlowTimer = 100; + FLSlowTimer(TNC); // 10 Secs + } + + return 0; + + case 1: // poll + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE && TNC->FLInfo->KISSMODE == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoFLDigi(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&readfs); + + if (TNC->CONNECTED || TNC->FLInfo->KISSMODE) + FD_SET(TNC->TCPDataSock,&readfs); + + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTED) + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED || TNC->FLInfo->KISSMODE) + FD_SET(TNC->TCPDataSock,&errorfs); + + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessXMLData(port); + } + + + if (FD_ISSET(TNC->TCPDataSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // If required, send signon + +// SendPacket(TNC->TCPDataSock,"\x1a", 1, 0); +// SendPacket(TNC->TCPDataSock,"DIGITAL MODE ?", 14, 0); +// SendPacket(TNC->TCPDataSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPDataSock,&errorfs) || FD_ISSET(TNC->TCPSock,&errorfs)) + { + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + + + // See if any frames for this port + + for (Stream = 0; Stream <= 1; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + int SendLen; + char Reply[256]; + int UILen; + char * UIMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + UILen = buffptr->LENGTH; + UILen -= 23; + UIMsg = buffptr->L2DATA; + + UIMsg[UILen] = 0; + + if (UILen < 129 && TNC->Streams[0].Attached == FALSE) // Be sensible! + { + // >00uG8BPQ:72 TestA + SendLen = sprintf(Reply, "u%s:72 %s", TNC->NodeCall, UIMsg); + SendPacket(TNC, Reply, SendLen); + } + ReleaseBuffer(buffptr); + } + + return (0); + + case 2: // send + + + if (!TNC->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + return (0); + } + else + { + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + if (STREAM->Connected) + TidyClose(TNC, buff->PORT); + + STREAM->ReportDISC = TRUE; // Tell Node + + TNC->FLInfo->MCASTMODE = FALSE; + + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MODEM ", 6) == 0) + { + buff->L2DATA[txlen -1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "MODEM:%s MODEM:", &buff->L2DATA[6]); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[6], 'S'); + } + + TNC->InternalCmd = TRUE; + return 1; + } + + if (_memicmp(buff->L2DATA, "FREQ ", 5) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%s WFF:", &buff->L2DATA[5]); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_carrier", atoi(&buff->L2DATA[5]), 'I'); + } + + TNC->InternalCmd = TRUE; + return 1; + } + + if (_memicmp(buff->L2DATA, "SQUELCH ", 8) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // Only works in KISS + + if (TNC->FLInfo->KISSMODE) + { + if (_memicmp(&buff->L2DATA[8], "ON", 2) == 0) + sprintf(txbuff, "KPSQL:ON KPSQL:"); + + else if (_memicmp(&buff->L2DATA[8], "OFF", 3) == 0) + sprintf(txbuff, "KPSQL:OFF KPSQL:"); + else + txlen = sprintf(txbuff, "KPSQLS:%s KPSQLS:", &buff->L2DATA[8]); + + SendKISSCommand(TNC, txbuff); + TNC->InternalCmd = TRUE; + } + return 1; + } + + if (_memicmp(buff->L2DATA, "KPSATT ", 7) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "KPSATT:%s KPSATT:", &buff->L2DATA[7]); + + SendKISSCommand(TNC, txbuff); + TNC->InternalCmd = TRUE; + } + + return 1; + } + + if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0) + { +// len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + +// if (TNC->MPSKInfo->TX) +// TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting +// else +// SendPacket(TNC->TCPDataSock, Command, len, 0); + +// TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(&buff->L2DATA[0], "MODE", 4) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + buff->L2DATA[txlen - 1] = 0; // Remove CR + + if (strstr(&buff->L2DATA[0], "RAW")) + TNC->FLInfo->RAW = TRUE; + else if (strstr(&buff->L2DATA[0], "KISS")) + TNC->FLInfo->RAW = FALSE; + else + { + buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Error - Invalid Mode\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; + } + + buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Ok - Mode is %s\r", + (TNC->FLInfo->RAW)?"RAW":"KISS"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MCAST", 5) == 0) + { + UINT * buffptr = GetBuff(); + + TNC->FLInfo->MCASTMODE = TRUE; + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + UINT * buffptr = GetBuff(); + int s = 0; + + while(s <= 1) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDig} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + struct ARQINFO * ARQ = TNC->ARQInfo; + int SendLen; + char Reply[80]; + + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = 48; // Not yet defined + TNC->FLInfo->FLARQ = FALSE; + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + strcpy(STREAM->RemoteCall, ptr); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + +//00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + STREAM->Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return 0; + } + + // Send any other command to FLDIGI + + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + char outbuff[1000]; + int newlen; + + buff->L2DATA[-1] = 6; // KISS Control + + newlen = KissEncode(&buff->L2DATA[-1], outbuff, txlen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[0], 'S'); + } + + TNC->InternalCmd = TRUE; + } + + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNC->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + { + // Busy if TX Window reached + + struct ARQINFO * ARQ = TNC->ARQInfo; + int Outstanding; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&TNC->Streams[0].BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); // 3rd Nibble is frames unacked + else + return TNCOK << 8 | STREAM->Disconnecting << 15; + + } + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->CONNECTED = FALSE; + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +int FindFLDIGI(char * Path) +{ + HANDLE hProc; + char ExeName[256] = ""; + char FLDIGIName[256]; + DWORD Pid = 0; + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (EnumProcessesPtr == NULL) + return 0; // Cant get PID + + if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) + return TRUE; + + // Path is to .bat, so need to strip extension of both names + + strcpy(FLDIGIName, Path); + strlop(FLDIGIName, '.'); + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + strlop(ExeName, '.'); + + if (_stricmp(ExeName, FLDIGIName) == 0) + return Processes[i]; + + } + } + } + return 0; +} + + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->WeStartedTNC = 0; // So we don't try again + + return 0; +} + +#endif + +static int RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL) + return 0; + + _strlwr(TNC->ProgramPath); + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart FLDIGI %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart FLDIGI - sento returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } +#ifndef LINBPQ + { + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + + // for some reason the program name must be lower case + + _strlwr(TNC->ProgramPath); + + ret = CreateProcess(TNC->ProgramPath, NULL, NULL, NULL, FALSE,0 ,NULL , NULL, &SInfo, &PInfo); + return ret; + } + } +#endif + return 0; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FLDigi Status" + "

FLDIGI Status

"); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID FLDIGISuspendPort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = FALSE; +} + +VOID FLDIGIReleasePort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = TRUE; +} + +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg) +{ + int txlen, rc; + char txbuff[256]; + char outbuff[256]; + + txlen = sprintf(txbuff, "%c%s", 6, Msg); + txlen = KissEncode(txbuff, outbuff, txlen); + rc = sendto(TNC->TCPDataSock, outbuff, txlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); +} + +VOID * FLDigiExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // The Socket to connect to is in IOBASE + // + + srand((unsigned int)time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + TNC->FLInfo->CONOK = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0 || PortEntry->PORTCONTROL.PORTPACLEN > 128) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + TNC->SuspendPortProc = FLDIGISuspendPort; + TNC->ReleasePortProc = FLDIGIReleasePort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_FLDIGI; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"FLDigi Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + +#ifndef LINBPQ + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) // Not running +#endif + TNC->WeStartedTNC = RestartTNC(TNC); // Always try if Linux + + if (TNC->FLInfo->KISSMODE) + { + // Open Datagram port + + SOCKET sock; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + struct hostent * HostEnt = NULL; + + TNC->FLInfo->CmdControl = 5; //Send params immediately + + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (HostEnt) + { + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + } + + TNC->TCPDataSock = sock = socket(AF_INET,SOCK_DGRAM,0); + + ioctl(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(TNC->TCPPort + 1); + + if (bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = WSAGetLastError(); + Consoleprintf("Bind Failed for UDP port %d - error code = %d", TNC->TCPPort, err); + } + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + } + else + ConnecttoFLDigi(port); + + time(&lasttime[port]); // Get initial time value + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode/CF", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0 Resent 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + return ExtProc; + +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct ARQINFO * ARQ; + struct FLINFO * FL; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + ARQ = TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + FL = TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 5 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + + TNC->FLInfo->KISSMODE = TRUE; // Default to KISS + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort + 40); // Defaults XML 7362 ARQ 7322 + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "TIMEOUT", 7) == 0) + TNC->Timeout = atoi(&buf[8]) * 10; + else + if (_memicmp(buf, "RETRIES", 7) == 0) + TNC->Retries = atoi(&buf[8]); + else + if (_memicmp(buf, "WINDOW", 6) == 0) + TNC->Window = atoi(&buf[7]); + else + if (_memicmp(buf, "ARQMODE", 7) == 0) + TNC->FLInfo->KISSMODE = FALSE; + else + if (_memicmp(buf, "DEFAULTMODEM", 12) == 0) // Send Beacon after each session + { + // Check that freq is also specified + + char * Freq = strchr(&buf[13], '/'); + + if (Freq) + { + *(Freq++) = 0; + strcpy(TNC->FLInfo->DefaultMode, &buf[13]); + TNC->FLInfo->DefaultFreq = atoi(Freq); + } + } + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoFLDigi(int port) +{ + _beginthread(ConnecttoFLDigiThread, 0, (void *)(size_t)port); + + return 0; +} + +static VOID ConnecttoFLDigiThread(void * portptr) +{ + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt = NULL; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("FLDIGI Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi Control socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FLDigi Control socket - error code = %d\n", err); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for FLDigi Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + else + { + sprintf(Msg, "Connect Failed for FLDigi Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + } + + return; +} + +VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d Resent %d Queued %d", + STREAM->BytesRXed, STREAM->BytesTXed, STREAM->BytesAcked, STREAM->BytesResent, STREAM->BytesOutstanding); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); +} + +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen) +{ + if (TNC->FLInfo->KISSMODE) + { + char KissMsg[1000]; + char outbuff[1000]; + int newlen; + + if (TNC->FLInfo->RAW) + { + // KISS RAW + + // Add CRC and Send + + unsigned short CRC; + char crcstring[6]; + + KissMsg[0] = 7; // KISS Raw + KissMsg[1] = 1; // SOH + KissMsg[2] = '0'; // Version + KissMsg[3] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&KissMsg[4], Msg, MsgLen +1 ); // Get terminating NULL + + CRC = CalcCRC(KissMsg + 1, MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(KissMsg, crcstring); + MsgLen += 9; + } + else + { + // Normal KISS + + KissMsg[0] = 0; // KISS Control + KissMsg[1] = TNC->ARQInfo->FarStream; + memcpy(&KissMsg[2], Msg, MsgLen); + MsgLen += 2; + } + + newlen = KissEncode(KissMsg, outbuff, MsgLen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + + SendKISSCommand(TNC, "TXBUF:"); + + } + else + { + // ARQ Scoket + + // Add Header, CRC and Send + + unsigned short CRC; + char crcstring[6]; + char outbuff[1000]; + + outbuff[0] = 1; // SOH + outbuff[1] = '0'; // Version + outbuff[2] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&outbuff[3], Msg, MsgLen + 1); + + CRC = CalcCRC(outbuff , MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(outbuff, crcstring); + MsgLen += 8; + + send(TNC->TCPDataSock, outbuff, MsgLen, 0); + } +} + +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW); + +static int ProcessReceivedData(int port) +{ + int bytes, used, bytesleft; + int i; + char ErrMsg[255]; + unsigned char MessageBuff[1500]; + unsigned char * Message = MessageBuff; + unsigned char * MessageBase = MessageBuff; + + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + // If using KISS/UDP interface use recvfrom + + if (FL->KISSMODE) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + unsigned char * KissEnd; + + bytesleft = recvfrom(TNC->TCPDataSock, Message, 1500, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (bytesleft < 0) + { + int err = WSAGetLastError(); + // if (err != 11) + // printf("KISS Error %d %d\n", nLength, err); + bytes = 0; + } + + while (bytesleft > 0) + { + unsigned char * in; + unsigned char * out; + unsigned char c; + + if (bytesleft < 3) + return 0; + + if (Message[0] != FEND) + return 0; // Duff + + Message = MessageBase; + in = out = &Message[2]; + + // We may have more than one KISS message in a packet + + KissEnd = memchr(&Message[2], FEND, bytesleft ); + + if (KissEnd == 0) + return 0; // Duff + + *(KissEnd) = 0; + + used = (int)(KissEnd - Message + 1); + + bytesleft -= used; + bytes = used; + + MessageBase += used; + + if (Message[1] == 6) // KISS Command + { + UCHAR * ptr = strchr(&Message[2], FEND); + + if (ptr) *ptr = 0; // Null Terminate + + if (bytes > 250) + Message[250] = 0; + + FL->Responding = 5; + + if (TNC->TNCOK == 0) + { + TNC->TNCOK = TRUE; + TNC->CONNECTED = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + // Trap BUSY fiest - there are lots of them, and they are likely to be confused + // with tesponses to Interactive commands + + if (memcmp(&Message[2], "BUSY", 4) == 0) + { + BOOL Changed = FALSE; + + if (Message[7] == 'T' && FL->Busy == FALSE) + { + TNC->Busy = FL->Busy = TRUE; + Changed = TRUE; + } + else + { + if (Message[7] == 'F' && FL->Busy == TRUE) + { + TNC->Busy = FL->Busy = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDIGI} Ok %s\r", &Message[2]); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + // Drop through in case need to extract info from command + } + + // Auto Command + +// Debugprintf("%d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]); + + if (memcmp(&Message[2], "FLSTAT", 4) == 0) + { + if (strstr(&Message[2], "FLSTAT:INIT")) + { + // FLDIGI Reloaded - set parmas + SendKISSCommand(TNC, "RSIDBCAST:ON TRXSBCAST:ON TXBEBCAST:ON KISSRAW:ON"); + } + continue; + } + + if (memcmp(&Message[2], "TRXS", 4) == 0) + { + char * ptr1, * context; + BOOL Changed = FALSE; + + ptr1 = strtok_s(&Message[7], ",", &context); + + if (strstr(ptr1, "TX")) + { + if (TNC->FLInfo->TX == FALSE) + { + TNC->FLInfo->TX = TRUE; + Changed = TRUE; + } + } + else + { + if (TNC->FLInfo->TX) + { + TNC->FLInfo->TX = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (memcmp(&Message[2], "TXBUF:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + STREAM->BytesOutstanding = atoi(ptr1); + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "TXBE:", 5) == 0) + { + STREAM->BytesOutstanding = 0; + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "RSIDN:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + + TNC->FLInfo->CenterFreq = atoi(ptr1); + ptr1 = strtok_s(NULL, ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "MODEM:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "WFF:", 4) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[6], ",", &context); + TNC->FLInfo->CenterFreq = atoi(ptr1); + } + + sprintf(TNC->WEB_MODE, "%s/%d", TNC->FLInfo->CurrentMode, TNC->FLInfo->CenterFreq); + SetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + continue; + } + + if (Message[1] == 7) // Not Normal Data + { + // "RAW" Mode. Just process as if received from TCP Socket Interface + + ProcessFLDigiPacket(TNC, &Message[2] , bytes - 3); // Data may be for another port + continue; + } + + bytes -= 3; // Two FEND and Control + + // Undo KISS + + while (bytes) + { + bytes--; + + c = *(in++); + + if (c == FESC) + { + c = *(in++); + bytes--; + + if (c == TFESC) + c = FESC; + else if (c == TFEND) + c = FEND; + } + *(out++) = c; + } + ProcessFLDigiData(TNC, &Message[3], (int)(out - &Message[3]), Message[2], FALSE); // KISS not RAW + } + return 0; + } + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPDataSock, Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPDataSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessFLDigiPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + + +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + char c; + struct FLINFO * FL = TNC->FLInfo; + + + if (TNC->FLInfo->MCASTMODE) + { + if (TNC->Streams[0].Attached == 0) + return; + + while(Len) + { + c = *(MPTR++); + + if (TNC->InPacket) + { + TNC->DataBuffer[TNC->DataLen++] = c; + + // Sanity Check + + if (TNC->DataLen == 6) + { + char * ptr = &TNC->DataBuffer[1]; + + if (memcmp(ptr, "DATA ", 5) == 0 || + memcmp(ptr, "PROG ", 5) == 0 || + memcmp(ptr, "FILE ", 5) == 0 || + memcmp(ptr, "SIZE ", 5) == 0 || + memcmp(ptr, "DESC ", 5) == 0 || + memcmp(ptr, "CNTL ", 5) == 0 || + memcmp(ptr, "ID ", 3) == 0) + + { + } + else + { + // False Trigger, try again + + TNC->InPacket = FALSE; + } + + } + else + { + if (TNC->InData) + { + if (--TNC->MCASTLen == 0) + { + // Got a packet + + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + buffptr = GetBuff(); + + if (buffptr) + { + TNC->DataBuffer[TNC->DataLen++] = 13; // Keep Tidy + + buffptr[1] = TNC->DataLen; + memcpy(&buffptr[2], &TNC->DataBuffer[0], TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + TNC->InPacket = FALSE; + } + } + else + { + // Looking for > + + if (TNC->DataLen == 16) + { + // Not found it + + TNC->InPacket = FALSE; + } + else + { + if (c == '>') + { + // Got Header - extract Length + + char * ptr; + int len; + + ptr = strchr(TNC->DataBuffer, ' '); + + if (ptr) + { + len = atoi(ptr); + + if (len) + { + TNC->InData = TRUE; + TNC->MCASTLen = len; + } + } + } + } + } + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + } + else + { + // Look for '<' + + if (c == '<') + { + TNC->DataBuffer[0] = c; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + TNC->InData = FALSE; + } + } + Len--; + } + return; + } + // Look for SOH/EOT delimiters. May Have several SOH before EOTTNC->FL + + while(Len) + { + c = *(MPTR++); + + switch (c) + { + case 01: // New Packet + + if (TNC->InPacket) + CheckFLDigiData(TNC); + + TNC->DataBuffer[0] = 1; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + break; + + case 04: + + if (TNC->InPacket) + CheckFLDigiData(TNC); + TNC->DataLen = 0; + TNC->InPacket = FALSE; + + break; + + default: + + if (TNC->InPacket) + { + if (TNC->DataLen == 1) + { + if (c != '0' && c != '1') + { + // Drop if not Protocol '0' or '1' - this should eliminate almost all noise packets + + TNC->InPacket = 0; + break; + } + } + TNC->DataBuffer[TNC->DataLen++] = c; + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + + } + Len--; + } +} +VOID CheckFLDigiData(struct TNCINFO * TNC) +{ + UCHAR * Input = &TNC->DataBuffer[0]; + int Len = TNC->DataLen - 4; // Not including CRC + unsigned short CRC; + char crcstring[6]; + + if (Len < 0) + return; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // RAW format message, either from ARQ Scoket or RAW KISS + + // Check Checksum + + CRC = CalcCRC(Input , Len); + + sprintf(crcstring, "%04X", CRC); + + if (memcmp(&Input[Len], crcstring, 4) !=0) + { + // CRC Error - could just be noise + +// Debugprintf("%s %s", crcstring, Input); + return; + } + ProcessFLDigiData(TNC, &Input[3], Len - 3, Input[2], TRUE); // From RAW +} +/* +VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // ARQ Packet from KISS-Like Hardware + + struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER]; + UCHAR * Input; + int Len; + + if (TNC == NULL) + { + // Set up TNC info + + TNC = TNCInfo[PORT->PORTNUMBER] = zalloc(sizeof(struct TNCINFO)); + TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 10 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + } + + Input = &Buffer->DEST[0]; + Len = Buffer->LENGTH - 7; // Not including CRC + + // Look for attach on any call + + ProcessFLDigiData(TNC, Input, Len); +} +*/ +static int Stuff(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * ptr = inbuff; + + // DLE Escape DLE, SOH, EOT + + for (i = 0; i < len; i++) + { + c = *(ptr++); + +// if (c == 0 || c == DLE || c == SOH || c == EOT) + if (c < 32 && c != 10 && c != 13 && c != 8) + { + outbuff[txptr++] = DLE; + + // if between 0 and 0x1F, Add 40, + // if > x80 and less than 0xa0 subtract 20 + + c += 0x40; + } + outbuff[txptr++]=c; + } + + return txptr; +} + + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * outbuff = inbuff; + UCHAR * ptr = inbuff; + + // This unstuffs into the input buffer + + for (i = 0; i < len; i++) + { + c = *(ptr++); + + if (c == DLE) + { + c = *(ptr++); + i++; + + // if between 0x40 and 0x5F, subtract 0x40, + // else add 0x20 (so we can send chars 80-9f without a double DLE) + + if (c < 0x60) + c -= 0x40; + else + c += 0x20; + } + outbuff[txptr++] = c; + } + + return txptr; +} + +unsigned int crcval = 0xFFFF; + +void update(char c) +{ + int i; + + crcval ^= c & 255; + for (i = 0; i < 8; ++i) + { + if (crcval & 1) + crcval = (crcval >> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} +/* + +00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06 +00kG8BPQ:24 G8BPQ 4 85F9B + +00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5 (128, 5) + +,00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 5 89FCA + +First no sees to be a connection counter. Next may be stream + + +08s___ABFC +08tG8BPQ:73 xxx 33FA +00tG8BPQ:73 yyy 99A3 +08dG8BPQ:90986C +00bG8BPQ:911207 + +call:90 for dis 91 for dis ack 73 for chat) + +08pG8BPQ?__645E +00s_??4235 + +08pG8BPQ?__645E +00s_??4235 + +i Ident +c Connect +k Connect Ack +r Connect NAK +d Disconnect req +s Data Ack/ Retransmit Req )status) +p Poll +f Format Fail +b dis ack +t talk + +a Abort +o Abort ACK + + +00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 6 49A3A +08s___ABFC +08 ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::90 +ARQ::STX +//FLARQ COMPOSER +Date: 09/01/2014 23:24:42 +To: gm8bpq +From: +SubjectA0E0 +08!: Test + +Test Message + +ARQ::ETX +F0F2 +08pG8BPQ!__623E +08pG8BPQ!__623E +08pG8BPQ!__623E + + + + +*/ +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW) +{ + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CTRL = Input[0]; + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + + int SendLen; + char Reply[80]; + + + // Process Message + + // This processes eitrher message from the KISS or RAW interfaces. + // Headers and RAW checksum have been removed, so packet starts with Control Byte + + // Only a connect request is allowed with no session, so check first + + if (CTRL == 'c') + { + // Connect Request + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = TNC->Window; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + if (FL->CONOK == FALSE) + return; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + // See if for us + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (_stricmp(call2, Appl) == 0) + break; + } + + if (App > 31) + if (strcmp(TNC->NodeCall, call2) !=0) + return; // Not Appl or Port/Node Call + + ptr = strtok_s(NULL, " ", &context); + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + BlockSize = atoi(ptr); + + if (ARQ->ARQState) + { + // We have already received a connect request - just ACK it + + goto AckConnectRequest; + } + + // Get a Session + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, call1, 0, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + strcpy(STREAM->MyCall, call2); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, call2, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, call2); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connect Pending"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->FarStream = FarStream; + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->ARQState = ARQ_ACTIVE; + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = FarStream; // Not Yet defined + if (strcmp(port1, "1025") == 0) + { + FL->FLARQ = TRUE; // From FLARQ + ARQ->OurStream = '8'; // FLARQ Ignores what we send + } + else + FL->FLARQ = FALSE; // From other app (eg BPQ) + + FL->RAW = RAW; + + STREAM->NeedDisc = 0; + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char Buffer[32]; + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + STREAM->NeedDisc = 50; // 1 sec + } + } + + ARQ->TXWindow = Window; + + if (BlockSize < 4) BlockSize = 4; + if (BlockSize < 9) BlockSize = 9; + + ARQ->MaxBlock = Blocksizes[BlockSize]; + + + ARQ->ARQTimer = 10; // To force CTEXT to be Queued + + if (App == 32) + { + // Connect to Node - send CTEXT + + if (HFCTEXTLEN > 1) + { + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = HFCTEXTLEN; + memcpy(&buffptr[2], HFCTEXT, HFCTEXTLEN); + SendARQData(TNC, buffptr); + } + } + } + + if (STREAM->NeedDisc) + { + // Send Not Avail + + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = sprintf((char *)&buffptr[2], "Application Not Available\n"); + SendARQData(TNC, buffptr); + } + } + +AckConnectRequest: + + SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_CONNECTACK; + + return; + } + + // All others need a session + +// if (!STREAM->Connected && !STREAM->Connecting) +// return; + + if (CTRL == 'k') + { + // Connect ACK + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = 16; + + char Reply[80]; + int ReplyLen; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ptr = strtok_s(NULL, " ", &context); + if (ptr) + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + if (ptr) + BlockSize = atoi(ptr); + + if (STREAM->Connected) + goto SendKReply; // Repeated ACK + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + STREAM->Connected = TRUE; + + ARQ->ARQTimerState = 0; + ARQ->ARQTimer = 0; + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z'); + + ARQ->ARQTimerState = 0; + ARQ->FarStream = FarStream; + ARQ->TXWindow = TNC->Window; + ARQ->MaxBlock = Blocksizes[BlockSize]; + + ARQ->ARQState = ARQ_ACTIVE; + + STREAM->NeedDisc = 0; + + buffptr = GetBuff(); + + if (buffptr) + { + ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall); + + buffptr[1] = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + +SendKReply: + + // Reply with status + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + // All others need a session + + //if (!STREAM->Connected) + // return; + + + if (CTRL == 's') + { + // Status + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; // Stop retry timer + Input[Len] = 0; + ProcessARQStatus(TNC, ARQ, &Input[1]); + + return; + } + + if (CTRL == 'p') + { + // Poll + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " \x1A", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + else + ARQ->TurnroundTimer = 15; // Allow us to send it all acked + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + return; + } + + + if (CTRL == 'a') + { + // Abort. Send Abort ACK - same as + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " :", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + if (CTRL == 'i') + { + // Ident + + return; + } + + if (CTRL == 't') + { + // Talk - not sure what to do with these + + return; + } + + if (CTRL == 'd') + { + // Disconnect Request + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + + // As the Disc ACK isn't repeated, we have to clear session now + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = 0; + + SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->Retries = 2; + return; + } + + if (CTRL == 'b') + { + // Disconnect ACK + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + ARQ->ARQState = 0; + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + FLReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + } + + if (CTRL == 'u') + { + // Beacon + + //>00uGM8BPQ:72 GM8BPQ TestingAD67 + + char * Call = &Input[1]; + strlop(Call, ':'); + + UpdateMH(TNC, Call, '!', 0); + return; + } + + if (STREAM->Connected) + { + if (Channel != ARQ->OurStream) + return; // Wrong Session + + if (CTRL >= ' ' && CTRL < 96) + { + // ARQ Data + + int Seq = CTRL - 32; + int Work; + +// if (rand() % 5 == 2) +// { +// Debugprintf("Dropping %d", Seq); +// return; +// } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return; // Sould never run out, but cant do much else + + // Remove any DLE transparency + + if (TNC->FLInfo->KISSMODE) + Len -= 1; + else + Len = UnStuff(&Input[1], Len - 1); + + buffptr[1] = Len; + memcpy(&buffptr[2], &Input[1], Len); + STREAM->BytesRXed += Len; + + UpdateStatsLine(TNC, STREAM); + + // Safest always to save, then see what we can process + + if (ARQ->RXHOLDQ[Seq]) + { + // Wot! Shouldn't happen + + ReleaseBuffer(ARQ->RXHOLDQ[Seq]); +// Debugprintf("ARQ Seq %d Duplicate"); + } + + ARQ->RXHOLDQ[Seq] = buffptr; +// Debugprintf("ARQ saving %d", Seq); + + // If this is higher that highest received, save. But beware of wrap' + + // Hi = 2, Seq = 60 dont save s=h = 58 + // Hi = 10 Seq = 12 save s-h = 2 + // Hi = 14 Seq = 10 dont save s-h = -4 + // Hi = 60 Seq = 2 save s-h = -58 + + Work = Seq - ARQ->RXHighest; + + if ((Work > 0 && Work < 32) || Work < -32) + ARQ->RXHighest = Seq; + + // We may now be able to process some + + Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need + + while (ARQ->RXHOLDQ[Work]) + { + // We have it + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]); +// ReleaseBuffer(ARQ->RXHOLDQ[Work]); + + ARQ->RXHOLDQ[Work] = NULL; +// Debugprintf("Processing %d from Q", Work); + + ARQ->RXNoGaps = Work; + Work = (Work + 1) & 63; // The next one we need + } + + ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data + return; + } + } +} + + +VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer) +{ + // Send Data, saving a copy until acked. + + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + UCHAR * ptr; + int Origlen = Buffer[1]; + int Stuffedlen; + + ARQ->TXSeq++; + ARQ->TXSeq &= 63; + + SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32); + + ptr = (UCHAR *)&Buffer[2]; // Start of data; + + ptr[Buffer[1]] = 0; + + if (memcmp(ptr, "ARQ:", 4) == 0) + { + // FLARQ Mail/FIle transfer. Turn off CR > LF translate (used for terminal mode) + + FL->FLARQ = FALSE; + } + + if (FL->FLARQ) + { + // Terminal Mode. Need to convert CR to LF so it displays in FLARQ Window + + ptr = strchr(ptr, 13); + + while (ptr) + { + *(ptr++) = 10; // Replace CR with LF + ptr = strchr(ptr, 13); + } + } + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Origlen); + SendLen += Origlen; + } + else + { + Stuffedlen = Stuff((UCHAR *)&Buffer[2], &TXBuffer[SendLen], Origlen); + SendLen += Stuffedlen; + } + + TXBuffer[SendLen] = 0; + +// if (rand() % 5 == 2) +// Debugprintf("Dropping %d", ARQ->TXSeq); +// else + + ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer; + + STREAM->BytesTXed += Origlen; + + UpdateStatsLine(TNC, STREAM); + + // if waiting for ack, don't send, just queue. Will be sent when ack received + + if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA) + { + SendPacket(TNC, TXBuffer, SendLen); + ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + } + else + STREAM->BytesResent -= Origlen; // So wont be included in resent bytes +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Reply[80]; + int SendLen; + + struct ARQINFO * ARQ = TNC->ARQInfo; + + SendLen = sprintf(Reply, "d%s:90", TNC->Streams[0].MyCall); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_DISC; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + FLReleaseTNC(TNC); +} + +VOID FLReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // if a default Modem is defined, select it + + if (TNC->FLInfo->DefaultMode[0]) + { + char txbuff[80]; + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%d MODEM:%s MODEM: WFF:", TNC->FLInfo->DefaultFreq, TNC->FLInfo->DefaultMode); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", TNC->FLInfo->DefaultMode, 'S'); + SendXMLCommand(TNC, "modem.set_carrier", (char *)TNC->FLInfo->DefaultFreq, 'I'); + } + } + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Queue to be sent after TXDELAY + + memcpy(ARQ->TXMsg, Msg, MsgLen + 1); + ARQ->TXLen = MsgLen; + ARQ->TXDelay = 15; // Try 1500 ms +} + +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Used for Messages that need a reply. Save, send and set timeout + + memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null + ARQ->LastLen = MsgLen; + + // Delay the send for a short while Just use the timeout code + +// SendPacket(sock, Msg, MsgLen, 0); + ARQ->ARQTimer = 1; // Try 500 ms + ARQ->Retries = TNC->Retries + 1; // First timout is rthe real send + + return; +} + + +VOID ARQTimer(struct TNCINFO * TNC) +{ + struct ARQINFO * ARQ = TNC->ARQInfo; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int SendLen; + char Reply[80]; + struct FLINFO * FL = TNC->FLInfo; + + //Send frames, unless held by TurnroundTimer or Window + + int Outstanding; + + // Use new BUSY: poll to detect busy state + + if (FL->TX == FALSE) + if (TNC->FLInfo->KISSMODE) + SendKISSCommand(TNC, "BUSY:"); // Send every poll for now - may need to optimize later + + +/* +// Use Received chars as a rough channel active indicator + + FL->BusyTimer++; + + if (FL->BusyTimer > 4) + { + FL->BusyTimer = 0; + + if (FL->BusyCounter > 2) // 2 chars in last .3 secs + FL->Busy = TRUE; + else + FL->Busy = FALSE; + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + FL->BusyCounter = 0; + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + +*/ // TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't + // need to check for busy (or anything else (I think!) + + if (ARQ->TXDelay) + { + ARQ->TXDelay--; + + if (ARQ->TXDelay) + return; + + SendPacket(TNC, ARQ->TXMsg, ARQ->TXLen); + } + + // if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end) + + if (ARQ->ARQTimerState == ARQ_WAITDATA) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + + ARQ->ARQTimer--; + + if (ARQ->ARQTimer > 0) + return; // Timer Still Running + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + +// Debugprintf("Sending Poll"); + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + } + + // TrunroundTimer is used to allow time for far end to revert to RX + + if (ARQ->TurnroundTimer && !FL->Busy) + ARQ->TurnroundTimer--; + + if (ARQ->TurnroundTimer == 0) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + } + + if (ARQ->ARQTimer) + { + if (FL->TX || FL->Busy) + { + // Only decrement if running send poll timer + + if (ARQ->ARQTimerState != ARQ_WAITDATA) + return; + } + + ARQ->ARQTimer--; + { + if (ARQ->ARQTimer) + return; // Timer Still Running + } + + ARQ->Retries--; + + if (ARQ->Retries) + { + // Retry Current Message + + SendPacket(TNC, ARQ->LastMsg, ARQ->LastLen); + ARQ->ARQTimer = TNC->Timeout + (rand() % 30); + + return; + } + + // Retried out. + + switch (ARQ->ARQTimerState) + { + case ARQ_WAITDATA: + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + case ARQ_CONNECTING: + + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDigi} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + // Send Disc to TNC in case it got the Connects, but we missed the ACKs + + TidyClose(TNC, 0); + ARQ->Retries = 2; // First timout is the real send, only send once + STREAM->Connecting = FALSE; // Back to Command Mode + ARQ->ARQState = FALSE; + + break; + + case ARQ_WAITACK: + case ARQ_CONNECTACK: + case ARQ_DISC: + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; + ARQ->ARQState = FALSE; + + while (STREAM->PACTORtoBPQ_Q) + ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q)); + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + break; + + } + } +} + +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char * Input) +{ + // Release any acked frames and resend any outstanding + + int LastInSeq = Input[1] - 32; + int LastRXed = Input[2] - 32; + int FirstUnAcked = ARQ->TXLastACK; + int n = (int)strlen(Input) - 3; + char * ptr; + int NexttoResend; + int First, Last, Outstanding; + UINT * Buffer; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int Acked = 0; + + // First status is an ack of Connect ACK + + if (ARQ->ARQTimerState == ARQ_CONNECTACK) + { + ARQ->Retries = 0; + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + } + + // Release all up to LastInSeq + + while (FirstUnAcked != LastInSeq) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + ARQ->TXLastACK = FirstUnAcked; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (FirstUnAcked == ARQ->TXSeq) + { + UpdateStatsLine(TNC, STREAM); + ARQ->NoAckRetries = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; // All Acked + } + // Release any not in retry list up to LastRXed. + + ptr = &Input[3]; + + while (n) + { + NexttoResend = *(ptr++) - 32; + + FirstUnAcked++; + FirstUnAcked &= 63; + + while (FirstUnAcked != NexttoResend) + { + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + + FirstUnAcked++; + FirstUnAcked &= 63; + } + + // We don't ACK this one. Process any more resend values, then release up to LastRXed. + + n--; + } + + // Release rest up to LastRXed + + while (FirstUnAcked != LastRXed) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + // Resend anything in TX Buffer (From LastACK to TXSeq + + Last = ARQ->TXSeq + 1; + Last &= 63; + + First = LastInSeq; + + while (First != Last) + { + First++; + First &= 63; + + if(ARQ->TXHOLDQ[First]) + { + UINT * Buffer = ARQ->TXHOLDQ[First]; + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + +// Debugprintf("Resend %d", First); + + STREAM->BytesResent += Buffer[1]; + + SendLen = sprintf(TXBuffer, "%c", First + 32); + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Buffer[1]); + SendLen += Buffer[1]; + } + else + SendLen += Stuff((UCHAR *)&Buffer[2], &TXBuffer[SendLen], Buffer[1]); + + TXBuffer[SendLen] = 0; + + SendPacket(TNC, TXBuffer, SendLen); + + ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + + if (Acked == 0) + { + // Nothing acked by this statis message + + Acked = 0; // Dont count more thna once + ARQ->NoAckRetries++; + if (ARQ->NoAckRetries > TNC->Retries) + { + // Too many retries - just disconnect + + TidyClose(TNC, 0); + return; + } + } + } + } + + UpdateStatsLine(TNC, STREAM); +} + +VOID FLSlowTimer(struct TNCINFO * TNC) +{ + struct FLINFO * FL = TNC->FLInfo; + + // Entered every 10 secs + + // if in MCAST mode, clear KILL timer (MCAST RX can run for a long time + + if (TNC->FLInfo->MCASTMODE) + { + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + SESS->L4KILLTIMER = 0; + } + + if (FL->KISSMODE) + { + if (FL->Responding) + FL->Responding--; + + if (FL->Responding == 0) + { + TNC->TNCOK = FALSE; + TNC->CONNECTED = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to FLDIGI lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Set basic params till it responds + } + + FL->CmdControl++; + + if (FL->CmdControl > 5) // Every Minute + { + FL->CmdControl = 0; + + SendKISSCommand(TNC, "FLSTAT: MODEM: WFF:"); + } + + SendKISSCommand(TNC, "TRXS: TXBUF:"); // In case TX/RX report is missed + } +} + +static int ProcessXMLData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + char * ptr1, * ptr2, *ptr3; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data. Assume for now we get a whole packet + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr3 = strstr(ptr1, ""); + + if (ptr3) + { + ptr1 = ptr3 + 4; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "FLDIGI} Ok Was %s\r", ptr1); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + } + + return 0; + } + + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr2 = strstr(ptr1, ""); + + if (ptr2) + { + ptr2 += 8; + ptr1 = ptr2; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (strcmp(FL->LastXML, "modem.get_name") == 0) + { + strcpy(TNC->WEB_MODE, ptr1); + SetWindowText(TNC->xIDC_MODE, ptr1); + } + else if (strcmp(FL->LastXML, "main.get_trx_state") == 0) + { + if (strcmp(ptr1, "TX") == 0) + FL->TX = TRUE; + else + FL->TX = FALSE; + + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (strcmp(FL->LastXML, "main.get_squelch") == 0) + { +/* + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); +*/ + return 0; + } +/* + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } +*/ + + } + + return (0); + +} + + + +char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + char ValueString[256] =""; + + if (!TNC->CONNECTED || TNC->FLInfo->KISSMODE) + return; + + if (Value) + if (ParamType == 'S') + sprintf(ValueString, "%s", Value); + else + sprintf(ValueString, "%d", Value); + + strcpy(FL->LastXML, Command); + Len = sprintf(ReqBuf, Req, FL->LastXML, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; +} + +VOID SendXMLPoll(struct TNCINFO * TNC) +{ + int Len; + char ReqBuf[256]; + char SendBuff[256]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + + if (!TNC->CONNECTED) + return; + + if (TNC->FLInfo->KISSMODE) + return; + + if (ARQ->ARQTimer) + { + // if timer is running, poll fot TX State + + strcpy(FL->LastXML, "main.get_trx_state"); + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; + } + + FL->XMLControl++; + + + if (FL->XMLControl > 9) + { + FL->XMLControl = 0; + strcpy(FL->LastXML, "modem.get_name"); + } + else + { + if (FL->XMLControl == 5) + strcpy(FL->LastXML, "main.get_trx_state"); + else + return; + } + + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); +} + +// sudo add-apt-repository ppa:kamalmostafa/fldigi + + diff --git a/FreeDATA-HPLaptop.c b/FreeDATA-HPLaptop.c new file mode 100644 index 0000000..526f3bc --- /dev/null +++ b/FreeDATA-HPLaptop.c @@ -0,0 +1,3490 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use FreeData TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + +#include "CHeaders.h" +#include "bpq32.h" +#include "tncinfo.h" + +#define SD_BOTH 0x02 + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); +static int Socket_Data(int sock, int error, int eventcode); +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int ProcessEscape(UCHAR * TXMsg); +static void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); +static int ConnecttoFreeData(int port); +void ConnectTNCPort(struct TNCINFO * TNC); +int FreeDataSendCommand(struct TNCINFO * TNC, char * data); +static void SendPing(struct TNCINFO * TNC, char * Call); +static void SendCQ(struct TNCINFO * TNC); +char * stristr (char *ch1, char *ch2); +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len); +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +char * byte_base64_encode(char *str, int len); +void xdecodeblock( unsigned char in[4], unsigned char out[3] ); +void FlushData(struct TNCINFO * TNC); +void CountRestarts(struct TNCINFO * TNC); +void StopTNC(struct TNCINFO * TNC); + +static char ClassName[]="FREEDATASTATUS"; +static char WindowTitle[] = "FreeData Modem"; +static int RigControlRow = 205; + +#ifdef LINBPQ +#include +#else +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->FreeDataInfo = zalloc(sizeof(struct FreeDataINFO)); + +// TNC->FreeDataInfo->useBaseCall = 1; // Default + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + 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) + { + TNC->FreeDataInfo->Capture = _strdup(&buf[8]); + strlop(TNC->FreeDataInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->FreeDataInfo->Playback = _strdup(&buf[9]); + strlop(TNC->FreeDataInfo->Playback, 13); + } + + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + + else if (_memicmp(buf, "HAMLIBHOST", 10) == 0) + { + TNC->FreeDataInfo->hamlibHost = _strdup(&buf[11]); + strlop(TNC->FreeDataInfo->hamlibHost, 13); + } + + else if (_memicmp(buf, "HAMLIBPORT", 10) == 0) + TNC->FreeDataInfo->hamlibPort = atoi(&buf[11]); + + else if (_memicmp(buf, "USEBASECALL", 11) == 0) + TNC->FreeDataInfo->useBaseCall = atoi(&buf[12]); + + else if (_memicmp(buf, "RXDIRECTORY", 11) == 0) + { + TNC->FreeDataInfo->RXDir = _strdup(&buf[12]); + strlop(TNC->FreeDataInfo->RXDir, 13); + } + + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int FreeDataGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +BOOL FreeDataReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} + + + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // FreeData mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID FreeDataChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); +// FreeDataSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SendPoll(TNC); + + // Check for buffered data to send + + if (TNC->FreeDataInfo->toSendTimeout) + { + TNC->FreeDataInfo->toSendTimeout--; + if (TNC->FreeDataInfo->toSendTimeout <= 0) + FlushData(TNC); + } + + return 0; + + case 1: // poll + +// FreeDataCheckRX(TNC); + + if (TNC->DAEMONCONNECTED == FALSE && TNC->DAEMONCONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + TNC->lasttime = ltime; + ConnecttoFreeData(port); + } + } + + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + +// FreeDataSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + FreeDataDisconnect(TNC); + strcpy(TNC->WEB_TNCSTATE, "Disconnecting"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("FreeData New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->TNCCONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + SendDataMsg(TNC, TNC->FreeDataInfo->farCall, buff->L2DATA, txlen); + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + SendPing(TNC, _strupr(Call)); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ping Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char loppedCall[10]; + char toCall[10]; + + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + // FreeDATA doesn't have the concept of a connection, so need to simulate it between the nodes + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + strcpy(TNC->FreeDataInfo->toCall, &buff->L2DATA[2]); + strcpy(loppedCall, TNC->FreeDataInfo->toCall); + if (TNC->FreeDataInfo->useBaseCall) + strlop(loppedCall, '-'); + strcpy(TNC->FreeDataInfo->farCall, loppedCall); + + // MYCALL and Target call are end to end concepts - the TNC cam can only use one call, set at TNC start. and no SSID's + // Messages are sent at TNC level to the tnc call, so we send our tnc call to the other end + + + txlen = sprintf(Connect, "C %s %s %s ", &buff->L2DATA[2], STREAM->MyCall, TNC->FreeDataInfo->ourCall); + + // See if Busy +/* + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } +*/ + TNC->OverrideBusy = FALSE; + + memset(STREAM->RemoteCall, 0, 10); + strcpy(STREAM->RemoteCall, &buff->L2DATA[2]); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// FreeDataSendCommand(TNC, Connect); + FreeDataConnect(TNC, STREAM->RemoteCall); + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + // The TNC doesn't have any commands, so send error message + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Not connected\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + // strlop(buff->L2DATA, 13); + // txlen = SendDataMsg(TNC, TNC->, buff->L2DATA); + // FreeDataSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // FreeData TNC can buffer unlimited data + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->TNCCONNECTED != 0) << 8 | 1; + + return ((TNC->TNCCONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + StopTNC(TNC); + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on FreeDATA"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + TNC->FreeDataInfo->CONOK = 0; + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + TNC->FreeDataInfo->CONOK = 1; + + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID FreeDataReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + FreeDataChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID FreeDataSuspendPort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 0; +} + +VOID FreeDataReleasePort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 1; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

FreeData Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +static HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + +/* +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +*/ + +#endif + + + +VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int line; + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("FreeData Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->Hardware = H_FREEDATA; + + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); + TNC->ARDOPBuffer = malloc(8192); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + memcpy(TNC->FreeDataInfo->ourCall, TNC->NodeCall, 10); + strlop(TNC->FreeDataInfo->ourCall, ' '); + if (TNC->FreeDataInfo->useBaseCall) + strlop(TNC->FreeDataInfo->ourCall, '-'); + + if (TNC->FreeDataInfo->hamlibHost == 0) + TNC->FreeDataInfo->hamlibHost = _strdup("127.0.0.1"); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = FreeDataSuspendPort; + TNC->ReleasePortProc = FreeDataReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + line = 6; + + if (TNC->TXFreq) + { + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 500, 450, ForcedClose); + + InitCommonControls(); // loads common control's DLL + + CreateWindowEx(0, "STATIC", "TX Tune", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNE = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNEVAL = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 320,line,30,20, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->xIDC_TXTUNE, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG(-200, 200)); // min. & max. positions + + line += 22; + } + else + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,450,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,line,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,520,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,line,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,144,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,line,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + time(&TNC->lasttime); // Get initial time value + + ConnecttoFreeData(port); + TNC->FreeDataInfo->CONOK = 1; + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // We don't get data acks, so can't check for bytes outstanding + + FreeDataDisconnect(TNC); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + FreeDataDisconnect(TNC); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + FreeDataReleaseTNC(TNC); + } +} + +VOID FreeDataAbort(struct TNCINFO * TNC) +{ + FreeDataSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID FreeDataDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +static RECT Rect1 = {30, 160, 400, 195}; + +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + // Replace forbidden chars with =xx + + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c >> 4) + 'A'; + *(out++) = (c & 15) + 'A'; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + +VOID FreeDataProcessTNCMessage(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + // First Byte of Message is Type. Messages can be commands or short (<120) data packets + // Data is encoded with =xx replacing restricted chars + + Msg[Len] = 0; + + switch (Msg[0]) + { + case 'C': + + // Connect Request. C G8BPQ-10 GM8BPQ-2 (Target, Origin) + + toCall = strtok_s(&Msg[2], " ", &Context); + fromCall = strtok_s(NULL, " ", &Context); + tncCall = strtok_s(NULL, " ", &Context); + + strcpy(TNC->FreeDataInfo->farCall, tncCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC, "d"); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataSendCommand(TNC, "d"); + + Debugprintf("FreeDara Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, toCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + // Send Connect ACK + Sleep(1000); + FreeDataSendCommand(TNC, "c"); + return; + + case 'c': + + // Connect ACK + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + + case 'D': + + // Disconnect Command + + FreeDataSendCommand(TNC, "d"); + + // Drop through to disconnect this end + + case 'd': + + // Disconnect complete (response to sending "D") + // Or connect refused in response to "C" + + if (STREAM->Connecting) + { + // Connection Refused - If there is a message, pass to appl + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + if (Msg[1]) + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected - %s\r", &Msg[1]); + else + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + return; + } + + // Release Session + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + + case 'B': + + // Was Base64, but has been expanded - just send to User + + // If len > blocksize, fragment + + Len--; + Msg++; // Remove Type + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Msg, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, 256); + Len -= 256; + Msg += 256; + STREAM->BytesRXed += 256; + + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Msg, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, Len); + STREAM->BytesRXed += Len; + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + + case 'I': + + // Encoded Response + + // Undo = transparency + + ptr = Msg + 1; + + while (ptr = strchr(ptr, '=')) + { + // Next two chars are a hex value + + a = ptr[1] - 'A'; + b = ptr[2] - 'A'; + memmove(ptr, ptr + 2, Len); + ptr[0] = (a << 4) + b; + ptr++; + } + + buffptr = GetBuff(); + buffptr->Len = sprintf(buffptr->Data, "%s", &Msg[1]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + case 'f': + + // FreeDATA File Transfer + + // Seems to be f null fn null data + //f.;Makefile.;.;123123123.; + { + char * FN; + time_t CRC; + int FileLen; + char Filename[256]; + FILE * fp1; + unsigned char * ptr; + char Text[64]; + int textLen; + + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + FN = _strdup(&Msg[3]); + ptr = &Msg[4] + strlen(FN); + + ptr = ptr + strlen(ptr) + 2; + CRC = atoi(ptr); + ptr = ptr + strlen(ptr) + 2; + + FileLen = Len - (ptr - Msg); + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FN); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(ptr, 1, FileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FN, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + + free(FN); + return; + + } + + + } + + if (STREAM->Attached == 0) + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toCall) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + strcpy(TNC->FreeDataInfo->farCall, fromCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataDisconnect(TNC); + + Debugprintf("FreeData Call from %s not in ValidCalls - rejected", fromCall); + return; + } + } + } + /* + + // At the moment we dont support applcalls + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + */ + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, toCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + return; + +} + +VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + +} +extern char LOC[7]; +/* +Line 104: if received_json["type"] == 'PING' and received_json["command"] == "PING": +Line 111: if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile": +Line 142: if received_json["type"] == 'ARQ' and received_json["command"] == "sendMessage": +Line 173: if received_json["type"] == 'ARQ' and received_json["command"] == "stopTransmission": +Line 182: if received_json["type"] == 'GET' and received_json["command"] == 'STATION_INFO': +Line 195: if received_json["type"] == 'GET' and received_json["command"] == 'TNC_STATE': +Line 244: if received_json["type"] == 'GET' and received_json["command"] == 'RX_BUFFER': +Line 258: if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': +Line 272: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': +Line 275: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': +*/ + +//{\"type\" : \"ARQ\", \"command\" : \"sendMessage\", \"dxcallsign\" : \"G8BPQ\", \"mode\" : \"10\", \"n_frames\" : \"1\", \"data\" : \"Hello Hello\" , \"checksum\" : \"123\", \"timestamp\" : 1642580748576} + + + +static unsigned char BANNED[] = {'"', '=', ':', '{', '}', '[', ']', '/', 13, 0}; // I think only need to escape = ": CR Null + + +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + // We can't base64 encode chunks. so buffer as original data and encode on send + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, Msg, Len); + WritetoTrace(TNC, Msg, Len); + + return; +} + + + +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len) +{ + char Message[16284]; + char * Base64; + +/* +{ + "type": "arq", + "command": "send_raw", + "uuid": "sjdfksndfkjsnd-123-sdfvlks", +"parameter": + [ + { + "dxcallsign": "DJ2LS", + "mycallsign": "DN2LS-2", + "mode": "255", + "n_frames": "1", + "data": "" + } + ] +} +*/ + + // TNC now only supports send_raw, with base64 encoded data + + char Template[] = "{\"type\": \"arq\", \"command\": \"send_raw\", \"parameter\":" + "[{\"dxcallsign\": \"%s\", \"mycallsign\": \"%s\", \"mode\": \"255\", \"n_frames\": \"1\", \"data\": \"%s\"}]}\n"; + + Base64 = byte_base64_encode(Msg, Len); + + Len = sprintf(Message, Template, Call, myCall, Base64); + + free(Base64); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +void FlushData(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + int Len = Info->toSendCount; + + // We need to flag as data (B) then base64 encode it + + memmove(&Info->toSendData[1], Info->toSendData, Len); + Info->toSendData[0] = 'B'; + Len++; + + SendAsRaw(TNC, Info->farCall, Info->ourCall, Info->toSendData, Len); + + Info->toSendCount = 0; + Info->toSendTimeout = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + +} + +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + + // Add to buffer + + if ((Info->toSendCount + Len) > 8192) // Reasonable Limit + { + // Send the buffered bit + + FlushData(TNC); + } + + memcpy(&Info->toSendData[Info->toSendCount], Msg, Len); + Info->toSendCount += Len; + Info->toSendTimeout = 10; // About a second + + STREAM->BytesTXed += Len; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return Len; +} + +static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) +{ + char Message[256]; + int Len; + + Len = sprintf(Message, "{\"type\" : \"%s\", \"command\" : \"%s\"}\n", Type, Command); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +static void SendPoll(struct TNCINFO * TNC) +{ + char DataMsgPoll[] = "{\"type\" : \"GET\", \"command\" : \"RX_MSG_BUFFER\"}\n"; +// char DaemonPoll[] = "{\"type\" : \"GET\", \"command\" : \"DAEMON_STATE\"}\n"; + char TNCPoll[] = "{\"type\" : \"GET\", \"command\" : \"TNC_STATE\", \"timestamp\" : 0}\n"; + + // Poll daemon rapidly until tnc is connected, then every 5 secs + + if ((TNC->PollDelay++ > 50))// || !TNC->TNCCONNECTED) && TNC->DAEMONCONNECTED) + { +// ret = send(TNC->TCPSock, (char *)&DaemonPoll, strlen(DaemonPoll), 0); + TNC->PollDelay = 0; + + } + + if (TNC->TNCCONNECTED) + { +// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); + +// if (TNC->PollDelay & 1) +// ret = SendTNCCommand(TNC, "GET", "RX_BUFFER"); +// else if (TNC->PollDelay & 2) +// ret = SendTNCCommand(TNC, "GET", "RX_MSG_BUFFER"); +// else +// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); + + + } + return; +} + +static void SendPing(struct TNCINFO * TNC, char * Call) +{ + char Ping[] = "{\"type\" : \"ping\", \"command\" : \"ping\", \"dxcallsign\" : \"%s\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, Ping, Call, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendCQ(struct TNCINFO * TNC) +{ + char CQ[] = "{\"type\" : \"BROADCAST\", \"command\" : \"cqcqcq\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, CQ, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + + +unsigned short int compute_crc(unsigned char *buf,int len); + + +int FreeDataWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +char * getObjectFromArray(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next object in array + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '{') Open ++; else if (c == '}') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} +/* + ["DATACHANNEL;RECEIVEDOPENER","ARQ;RECEIVING","ARQ;RECEIVING;SUCCESS"] [{"DXCALLSIGN":"GM8BPQ +{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642847440,"RXDATA":[{"dt":"f","fn":"config.json", +"ft":"application\/json","d":"data:application\/json;base64, +ewogICAgInRuY19ob3N0IiA6ICIxMjcuMC4wLjEiLAogICAgInRuY19wb3J0IiA6ICIzMDAwIiwKICAgICJkYWVtb25faG9zdCIgOiAiMTI3LjAuMC4xIiwKICAgICJkYWVtb25fcG9ydCIgOiAiMzAwMSIsCiAgICAibXljYWxsIiA6ICJBQTBBQSIsCiAgICAibXlncmlkIiA6ICJBQTExZWEiICAgIAp9" +,"crc":"123123123"}]} +*/ +void ProcessFileObject(struct TNCINFO * TNC, char * This) +{ + char * Call; + char * LOC; + char * FN; + char * Type; + char * ptr, * ptr2; + int Len, NewLen; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + FN = strstr(This, "fn"); + FN += 5; + This = strlop(FN, '"'); + + Type = strstr(This, "base64"); + Type += 7; + This = strlop(Type, '"'); + + // Decode Base64 + + Len = strlen(Type); + + Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + Type --; + + Type[0] = 'B'; ; // Base64 Info + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen + 1); + + +} + +void ProcessMessageObject(struct TNCINFO * TNC, char * This) +{ + // This gets Call and Message from a RX_MSG_BUFFER array element . It doesn't need care that it is json/ + // as the format is such that keys are unique + + // {"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642581230,"RXDATA":[{"dt":"m","d":"Hello","crc":"123"}]} + + char * Call; + char * LOC; + char * Type; + char * Msg; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + Type = strstr(This, "\"dt\""); + Type += 6; + This = strlop(Type, '"'); + + Msg = strchr(This, ':'); + Msg += 2; + This = strlop(Msg, '"'); + + FreeDataProcessTNCMessage(TNC, Call, Msg, strlen(Msg)); +} + +void processJSONINFO(struct TNCINFO * TNC, char * Info, char * Call, double snr) +{ + char * LOC = ""; + char * ptr, * Context; + + // Info is an array. Normally only one element, but should check + + ptr = strtok_s(&Info[1], ",]", &Context); + + while (ptr && ptr[1]) + { + if (strstr(ptr, "BEACON;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + if (strstr(ptr, "PING;RECEIVING")) + { + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "CQ;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "CQ received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "PING;RECEIVEDACK")) + { + char Msg[128]; + int Len; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", Call, snr); + FreeDataProcessTNCMessage(TNC, Call, Msg, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "TRANSMITTING;FAILED")) + { + // Failed to send a message - if it was a connect request tell appl + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + ptr = strtok_s(NULL, ",]", &Context); + } +} + + + + + + +char * getJSONValue(char * Msg, char * Key) +{ + char * ptr, *ptr2, *value = 0; + int vallen, keylen = strlen(Key); + char c; + + // We Null Terminate the value, so we must look for keys in reverse order + + ptr = strstr(Msg, Key); + + if (ptr) + { + ptr += (keylen + 1); + + if (*(ptr) == '[') + { + // Array + + int Open = 0; + int Close = 0; + + ptr2 = ptr; + + while (c = *(ptr++)) + { + if (c == '[') + Open ++; + else if (c == ']') + Close ++; + + if (Open == Close) + { + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + return value; + } + } + } + else if (*(ptr) == '\"') + { + // String + + ptr2 = ptr; + ptr = strchr(ptr + 1, '\"'); + if (ptr) + { + ptr++; + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + } + } + } + return value; +} + + +char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; + + +void ProcessDAEMONJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + char * capture, * playback; + + + if (memcmp(Msg, "{\"command\":\"daemon_state\"", 25) == 0) + { +/* + {"COMMAND":"DAEMON_STATE","DAEMON_STATE":[{"STATUS":"stopped"}], + "PYTHON_VERSION":"3.9", + "HAMLIB_VERSION":"4.0", + "INPUT_DEVICES":[{"ID":5,"NAME":"pulse"},{"ID":9,"NAME":"default"}], + "OUTPUT_DEVICES":[{"ID":0,"NAME":"bcm2835 Headphones: - (hw:0,0)"},{"ID":1,"NAME":"sysdefault"},{"ID":2,"NAME":"lavrate"},{"ID":3,"NAME":"samplerate"},{"ID":4,"NAME":"speexrate"},{"ID":5,"NAME":"pulse"},{"ID":6,"NAME":"upmix"},{"ID":7,"NAME":"vdownmix"},{"ID":8,"NAME":"dmix"},{"ID":9,"NAME":"default"}], + "SERIAL_DEVICES":[{"PORT":"\/dev\/ttyAMA0","DESCRIPTION":"ttyAMA0 [1dff]"}], + "CPU":"39.6", + "RAM":"38.6", + "VERSION":"0.1-prototype"} +*/ + + Msg += 25; + + playback = getJSONValue(Msg, "\"output_devices\""); + capture = getJSONValue(Msg, "\"input_devices\""); + + if (TNC->CaptureDevices) + free(TNC->CaptureDevices); + + TNC->CaptureDevices = _strdup(capture); + + if (TNC->PlaybackDevices) + free(TNC->PlaybackDevices); + + TNC->PlaybackDevices = _strdup(playback); + + ptr = getJSONValue(Msg, "\"daemon_state\""); + + if (strstr(ptr, "stopped")) + { + TNC->FreeDataInfo->TNCRunning = 0; + // if we have Capture and Playback devices, then start the TNC, else look in message for them + + if (TNC->CaptureDevices) + { + if (TNC->FreeDataInfo->startingTNC == 0) + { + // Find Capture and Playback indices + + struct FreeDataINFO * Info = TNC->FreeDataInfo; + char * devptr = stristr(TNC->CaptureDevices, Info->Capture); + int capindex = -1, playindex = -1; + char startTNC[] = "{\"type\":\"set\",\"command\":\"start_tnc\"," + "\"parameter\":[{\"mycall\":\"%s\",\"mygrid\":\"%s\",\"rx_audio\":\"%d\"," + "\"tx_audio\":\"%d\",\"devicename\":\"RIG_MODEL_NETRIGCTL\",\"deviceport\":\"/dev/ttyAMA0\"," + "\"serialspeed\":\"19200\"," + "\"pttprotocol\":\"RTS\",\"pttport\":\"/dev/ttyAMA0\",\"data_bits\":\"8\"," + "\"stop_bits\":\"1\",\"handshake\":\"None\", \"radiocontrol\":\"disabled\"," + "\"rigctld_ip\":\"%s\",\"rigctld_port\":\"%d\"," + "\"enable_scatter\":\"0\",\"enable_fft\":\"0\",\"low_bandwith_mode\":\"0\"}]}\n"; + + + char Command[512]; + int Len; + + if (devptr) + { + while (*(--devptr) != '{'); // find start of subparam + capindex = atoi(&devptr[6]); + } + + devptr = stristr(TNC->PlaybackDevices, Info->Playback); + + if (devptr) + { + while (*(--devptr) != '{'); // find start of subparam + playindex = atoi(&devptr[6]); + } + + if (capindex > -1 && playindex > -1) + { + Len = sprintf(Command, startTNC, TNC->FreeDataInfo->ourCall, LOC, capindex, playindex, + TNC->FreeDataInfo->hamlibHost, TNC->FreeDataInfo->hamlibPort); + send(TNC->TCPSock, Command, Len, 0); +// TNC->FreeDataInfo->startingTNC = 5; + } + } + } + else + { + ptr = getJSONValue(Msg, "\"version\""); + + } + return; + } + TNC->FreeDataInfo->TNCRunning = 1; + + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) + ConnectTNCPort(TNC); + + return; + + } + + Debugprintf(Msg); +} + +void StopTNC(struct TNCINFO * TNC) +{ + char stopTNC[] = "{\"type\" : \"set\", \"command\": \"stop_tnc\" , \"parameter\": \"---\"}"; + + if (TNC->TNCCONNECTED) + { + send(TNC->TCPSock, stopTNC, strlen(stopTNC), 0); + closesocket(TNC->TCPDataSock); + } +} + + +void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + + if (memcmp(Msg, "{\"ptt\":", 7) == 0) + { + if (strstr(Msg, "True")) + { +// TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + } + else + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + } + return; + } + + if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) + { +/* +{"COMMAND":"TNC_STATE","TIMESTAMP":1642429227972,"PTT_STATE":"False","TNC_STATE":"IDLE","ARQ_STATE":"False", +"AUDIO_RMS":"0","SNR":"0","FREQUENCY":"145000000","MODE":"FM","BANDWITH":"15000", +"FFT":"[47.7, 28.9, 19.8, 26.4, 22.9, 19.9, 20.0, 20.1, 21.8, 24.0, 21.4, 14.9, 20.6, 17.9, 19.9, 18.4, 20.8, +22.2, 17.8, 23.7, 17.5, 15.6, 21.7, 17.5, 20.4, 21.7, 23.4, 18.2, 21.6, 17.0, 18.7, 18.2, 19.8, 18.6, 20.2, +.. +12.0, 11.9, 12.9, 13.5, 14.3, 14.6, 16.5, 15.4, 14.9, 13.7, 15.6, 12.3]", +"SCATTER":[],"RX_BUFFER_LENGTH":"0","RX_MSG_BUFFER_LENGTH":"0","ARQ_BYTES_PER_MINUTE":"0","ARQ_BYTES_PER_MINUTE_BURST":"0","ARQ_COMPRESSION_FACTOR":"0","ARQ_TRANSMISSION_PERCENT":"0","TOTAL_BYTES":"0","INFO":[],"BEACON_STATE":"False","STATIONS":[],"EOF":"EOF"}{"type" : "GET", "command" : "TNC_STATE", "timestamp" : 1642429228121} + +"arq_session":"True" +\"arq_session_state\":\"disconnected\" +*/ + char * LOC = 0; + char * Stations; + char * myCall = 0; + char * farCall = 0; + double snr; + int arqstate = 0; + + Msg += 23; + + ptr = strstr(Msg, "snr"); + + if (ptr) + snr = atof(ptr + 6); + + Stations = getJSONValue(Msg, "\"stations\""); + + if (Stations) + { + ptr = Stations + strlen(Stations) + 1; + LOC = getJSONValue(ptr, "\"dxgrid\""); + farCall = getJSONValue(ptr, "\"dxcallsign\""); + myCall = getJSONValue(ptr, "\"mycallsign\""); + + if (myCall && farCall) + { + myCall++; + strlop(myCall, '"'); + farCall++; + strlop(farCall, '"'); + } + } + + // Look for changes in arq_session_state + + ptr = strstr(Msg, "\"arq_session_state\""); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 21; + + if (memcmp(ptr, "disconnected", 10) == 0) + { + arqstate = 1; + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else if (memcmp(ptr, "connecting", 10) == 0) + arqstate = 2; + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + arqstate = 3; + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + arqstate = 4; + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + arqstate = 5; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + + Debugprintf("%d arq_session_state %d", TNC->Port, arqstate); + } + + + ptr = getJSONValue(Msg, "\"info\""); + + if (strcmp(ptr, "[]") != 0) + { + + processJSONINFO(TNC, ptr, farCall, snr); + Debugprintf("%d %s %s", TNC->Port, ptr, farCall); + } + + return; + } + +// {"COMMAND":"RX_BUFFER","DATA-ARRAY":[],"EOF":"EOF"} +/* {"COMMAND":"RX_BUFFER","DATA-ARRAY":[{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642579504, +"RXDATA":[{"dt":"f","fn":"main.js","ft":"text\/javascript" +,"d":"data:text\/javascript;base64,Y29uc3Qge.....9KTsK","crc":"123123123"}]}],"EOF":"EOF"} + + + +{"arq":"received","uuid":"a1346319-6eb0-42aa-b5a0-c9493c8ccdca","timestamp":1645812393,"dxcallsign":"G8BPQ-2","dxgrid":"","data":"QyBHOEJQUS0yIEc4QlBRLTIgRzhCUFEtMiA="} +{"ptt":"True"} + + +*/ + if (memcmp(Msg, "{\"arq\":\"received\"", 17) == 0) + { + int NewLen; + char * ptr, *ptr2, *Type; + char * Call = 0; + char * myCall = 0; + + Msg += 17; + + ptr = getJSONValue(Msg, "\"data\""); + Type = ++ptr; + + // Decode Base64 + + // FreeData replacs / with \/ so need to undo + + ptr2 = strstr(Type, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Type) - 1; + + // Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + myCall = getJSONValue(Msg, "\"mycallsign\""); + Call = getJSONValue(Msg, "\"dxcallsign\""); + + if (Call) + { + Call++; + strlop(Call, '"'); + } + + if (myCall) + { + myCall++; + strlop(myCall, '"'); + } + + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen); + + return; + } + + if (memcmp(Msg, "{\"COMMAND\":\"RX_MSG_BUFFER\"", 26) == 0) + { + char * Next, * This; + + Msg += 26; + ptr = getJSONValue(Msg, "\"EOF\""); + ptr = getJSONValue(Msg, "\"DATA-ARRAY\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + } while (Next && Next[0] == '{'); + + // Delete from TNC + + SendTNCCommand(TNC, "SET", "DEL_RX_MSG_BUFFER"); + } + + + return; + } + Debugprintf(Msg); +} + +int FreeDataConnect(struct TNCINFO * TNC, char * Call) +{ + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, Connect, Call); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataDisconnect(struct TNCINFO * TNC) +{ + char Disconnect[] = "{\"type\" : \"arq\", \"command\": \"disconnect\"}\n"; + char Msg[128]; + int Len; + +// return FreeDataSendCommand(TNC, "D"); + + Len = sprintf(Msg, Disconnect); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) +{ + // Commands are simulated as Messages to the remote BPQ. The TNC itself does not handle any commands + + // First Byte of MSG is a Type - Command or Data. MSG has a limited character set Use =xx for Now. + + // Current Types - C = Connect, D = Disconnect, I = info + + SendAsRaw(TNC, TNC->FreeDataInfo->farCall, TNC->FreeDataInfo->ourCall, Msg, strlen(Msg)); + return 0; +} + +void FreeDataProcessDaemonMsg(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr; + int OpenBraces; + int CloseBraces; + char c; + + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8191 - TNC->InputLen, 0); + + if (InputLen == 8191) + c = 0; + + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->DAEMONCONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + +loop: + TNC->ARDOPBuffer[TNC->InputLen] = 0; // So we cat use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + + + if (TNC->ARDOPBuffer[0] != '{') + { + TNC->InputLen = 0; + return; + } + + ptr = &TNC->ARDOPBuffer[0]; + + OpenBraces = 0; + CloseBraces = 0; + + while (c = *(ptr++)) + { + if (c == '{') + OpenBraces ++; + else if (c == '}') + CloseBraces ++; + + if (OpenBraces == CloseBraces) + { + MsgLen = ptr - (char * )TNC->ARDOPBuffer; + + ProcessDAEMONJSON(TNC, TNC->ARDOPBuffer, MsgLen); + + if (TNC->InputLen == 0 || *ptr == 0) + { + TNC->InputLen = 0; + return; + } + + // More in buffer + + memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen - MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + + } + // Message Incomplete - wait for rest; +} + + + + + + +void FreeDataProcessTNCMsg(struct TNCINFO * TNC) +{ + int DataInputLen, MsgLen; + char * ptr, * endptr; + + // May get message split over packets or multiple messages per packet + + // A complete file transfer arrives as one message, so can bw very long + + + if (TNC->DataInputLen > MAXRXSIZE) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + DataInputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], MAXRXSIZE - TNC->DataInputLen, 0); + + if (DataInputLen == 0 || DataInputLen == SOCKET_ERROR) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TNCCONNECTED = FALSE; + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->DataInputLen += DataInputLen; + + TNC->ARDOPDataBuffer[TNC->DataInputLen] = 0; // So we cat use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + +// Debugprintf(TNC->ARDOPDataBuffer); + + //I think now messages end with LF + +loop: + + endptr = strchr(TNC->ARDOPDataBuffer, 10); + + if (endptr == 0) + return; + + *(endptr) = 0; + + if (TNC->ARDOPDataBuffer[0] != '{') + { + TNC->DataInputLen = 0; + return; + } + + ptr = &TNC->ARDOPDataBuffer[0]; + + MsgLen = endptr - ptr; + + ProcessTNCJSON(TNC, ptr, MsgLen); + + // MsgLen doesnt include lf + + MsgLen++; + + if (TNC->DataInputLen == MsgLen) + { + TNC->DataInputLen = 0; + return; + } + + // More in buffer + + ptr += MsgLen; + TNC->DataInputLen -= MsgLen; + + memmove(TNC->ARDOPDataBuffer, ptr, TNC->DataInputLen + 1); + + goto loop; + + // Message Incomplete - wait for rest; +} + + + +VOID FreeDataThread(void * portptr); + +int ConnecttoFreeData(int port) +{ + _beginthread(FreeDataThread, 0, (void *)(size_t)port); + + return 0; +} + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + + +VOID FreeDataThread(void * portptr) +{ + // FreeData TNC has two sessions, not sure why! + + // Messages are JSON encapulated + // Opens deamon socket. TNC socket is only available once Start TNC command sent + // Looks for data on socket(s) + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->DAEMONCONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +// printf("Starting FreeDATA Thread\n"); + +// if on Windows and Localhost see if TNC is running + +#ifdef WIN32 + + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + + if (TNC->PID == 0) + goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + goto TNCRunning; + } + +#endif + +TNCNotRunning: + + // Not running or can't check, restart if we have a path + +/* if (TNC->ProgramPath) + { + Consoleprintf("Trying to (re)start TNC %s", TNC->ProgramPath); + + if (RestartTNC(TNC)) + CountRestarts(TNC); + + Sleep(TNC->AutoStartDelay); + } +*/ +TNCRunning: + + if (TNC->Alerted == FALSE) + { + sprintf(TNC->WEB_COMMSSTATE, "Connecting to Daemon"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->DAEMONCONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for FreeData Host - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPDataSock); +// closesocket(TNC->TCPSock); + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->DAEMONCONNECTING = FALSE; + return; + } + + TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData Data Daemon socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->DAEMONCONNECTING = FALSE; + closesocket(TNC->TCPDataSock); + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + // Connect Daemon Port + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FreeData Daemon socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->DAEMONCONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->DAEMONCONNECTING = FALSE; + TNC->DAEMONCONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + TNC->Alerted = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData Daemon"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + sprintf(Msg, "Connected to FreeData Daemon Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title +// EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + while (TNC->DAEMONCONNECTED || TNC->TNCCONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + if (TNC->DAEMONCONNECTED) + FD_SET(TNC->TCPSock,&readfs); + + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->TNCCONNECTED) + FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("FreeData Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessTNCMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessDaemonMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { +Lost: + sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->TNCCONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPDataSock); + TNC->TCPDataSock = 0; + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { + sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->DAEMONCONNECTED = 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); + TNC->TCPSock = 0; + } + continue; + } + else + { + } + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +void ConnectTNCPort(struct TNCINFO * TNC) +{ + char Msg[255]; + int err; + int bcopt = TRUE; + + TNC->TNCCONNECTING = TRUE; + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // Connected successful + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->Alerted = FALSE; + return; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + TNC->TNCCONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + return; +} + diff --git a/FreeDATA.c b/FreeDATA.c new file mode 100644 index 0000000..b14ee9f --- /dev/null +++ b/FreeDATA.c @@ -0,0 +1,3492 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use FreeData TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + +#include "CHeaders.h" +#include "bpq32.h" +#include "tncinfo.h" + +#define SD_BOTH 0x02 + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); +static int Socket_Data(int sock, int error, int eventcode); +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int ProcessEscape(UCHAR * TXMsg); +static void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); +static int ConnecttoFreeData(int port); +void ConnectTNCPort(struct TNCINFO * TNC); +int FreeDataSendCommand(struct TNCINFO * TNC, char * data); +static void SendPing(struct TNCINFO * TNC, char * Call); +static void SendCQ(struct TNCINFO * TNC); +char * stristr (char *ch1, char *ch2); +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len); +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +char * byte_base64_encode(char *str, int len); +void xdecodeblock( unsigned char in[4], unsigned char out[3] ); +void FlushData(struct TNCINFO * TNC); +void CountRestarts(struct TNCINFO * TNC); +void StopTNC(struct TNCINFO * TNC); +int FreeDataConnect(struct TNCINFO * TNC, char * Call); +int FreeDataDisconnect(struct TNCINFO * TNC); + +static char ClassName[]="FREEDATASTATUS"; +static char WindowTitle[] = "FreeData Modem"; +static int RigControlRow = 205; + +#ifndef WIN32 +#include +#else +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->FreeDataInfo = zalloc(sizeof(struct FreeDataINFO)); + +// TNC->FreeDataInfo->useBaseCall = 1; // Default + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + 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) + { + TNC->FreeDataInfo->Capture = _strdup(&buf[8]); + strlop(TNC->FreeDataInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->FreeDataInfo->Playback = _strdup(&buf[9]); + strlop(TNC->FreeDataInfo->Playback, 13); + } + + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + + else if (_memicmp(buf, "HAMLIBHOST", 10) == 0) + { + TNC->FreeDataInfo->hamlibHost = _strdup(&buf[11]); + strlop(TNC->FreeDataInfo->hamlibHost, 13); + } + + else if (_memicmp(buf, "HAMLIBPORT", 10) == 0) + TNC->FreeDataInfo->hamlibPort = atoi(&buf[11]); + + else if (_memicmp(buf, "USEBASECALL", 11) == 0) + TNC->FreeDataInfo->useBaseCall = atoi(&buf[12]); + + else if (_memicmp(buf, "RXDIRECTORY", 11) == 0) + { + TNC->FreeDataInfo->RXDir = _strdup(&buf[12]); + strlop(TNC->FreeDataInfo->RXDir, 13); + } + + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int FreeDataGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +BOOL FreeDataReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} + + + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // FreeData mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID FreeDataChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); +// FreeDataSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SendPoll(TNC); + + // Check for buffered data to send + + if (TNC->FreeDataInfo->toSendTimeout) + { + TNC->FreeDataInfo->toSendTimeout--; + if (TNC->FreeDataInfo->toSendTimeout <= 0) + FlushData(TNC); + } + + return 0; + + case 1: // poll + +// FreeDataCheckRX(TNC); + + if (TNC->DAEMONCONNECTED == FALSE && TNC->DAEMONCONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + TNC->lasttime = ltime; + ConnecttoFreeData(port); + } + } + + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + +// FreeDataSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + FreeDataDisconnect(TNC); + strcpy(TNC->WEB_TNCSTATE, "Disconnecting"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("FreeData New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->TNCCONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + SendDataMsg(TNC, TNC->FreeDataInfo->farCall, buff->L2DATA, txlen); + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + SendPing(TNC, _strupr(Call)); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ping Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char loppedCall[10]; + char toCall[10]; + + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + // FreeDATA doesn't have the concept of a connection, so need to simulate it between the nodes + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + strcpy(TNC->FreeDataInfo->toCall, &buff->L2DATA[2]); + strcpy(loppedCall, TNC->FreeDataInfo->toCall); + if (TNC->FreeDataInfo->useBaseCall) + strlop(loppedCall, '-'); + strcpy(TNC->FreeDataInfo->farCall, loppedCall); + + // MYCALL and Target call are end to end concepts - the TNC cam can only use one call, set at TNC start. and no SSID's + // Messages are sent at TNC level to the tnc call, so we send our tnc call to the other end + + + txlen = sprintf(Connect, "C %s %s %s ", &buff->L2DATA[2], STREAM->MyCall, TNC->FreeDataInfo->ourCall); + + // See if Busy +/* + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } +*/ + TNC->OverrideBusy = FALSE; + + memset(STREAM->RemoteCall, 0, 10); + strcpy(STREAM->RemoteCall, &buff->L2DATA[2]); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// FreeDataSendCommand(TNC, Connect); + FreeDataConnect(TNC, STREAM->RemoteCall); + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + // The TNC doesn't have any commands, so send error message + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Not connected\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + // strlop(buff->L2DATA, 13); + // txlen = SendDataMsg(TNC, TNC->, buff->L2DATA); + // FreeDataSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // FreeData TNC can buffer unlimited data + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->TNCCONNECTED != 0) << 8 | 1; + + return ((TNC->TNCCONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + StopTNC(TNC); + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on FreeDATA"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + TNC->FreeDataInfo->CONOK = 0; + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + TNC->FreeDataInfo->CONOK = 1; + + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID FreeDataReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + FreeDataChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID FreeDataSuspendPort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 0; +} + +VOID FreeDataReleasePort(struct TNCINFO * TNC) +{ + TNC->FreeDataInfo->CONOK = 1; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

FreeData Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +static HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + +/* +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +*/ + +#endif + + + +VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int line; + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("FreeData Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->Hardware = H_FREEDATA; + + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); + TNC->ARDOPBuffer = malloc(8192); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + memcpy(TNC->FreeDataInfo->ourCall, TNC->NodeCall, 10); + strlop(TNC->FreeDataInfo->ourCall, ' '); + if (TNC->FreeDataInfo->useBaseCall) + strlop(TNC->FreeDataInfo->ourCall, '-'); + + if (TNC->FreeDataInfo->hamlibHost == 0) + TNC->FreeDataInfo->hamlibHost = _strdup("127.0.0.1"); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = FreeDataSuspendPort; + TNC->ReleasePortProc = FreeDataReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + line = 6; + + if (TNC->TXFreq) + { + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 500, 450, ForcedClose); + + InitCommonControls(); // loads common control's DLL + + CreateWindowEx(0, "STATIC", "TX Tune", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNE = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNEVAL = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 320,line,30,20, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->xIDC_TXTUNE, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG(-200, 200)); // min. & max. positions + + line += 22; + } + else + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,450,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,line,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,520,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,line,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,144,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,line,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + time(&TNC->lasttime); // Get initial time value + + ConnecttoFreeData(port); + TNC->FreeDataInfo->CONOK = 1; + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // We don't get data acks, so can't check for bytes outstanding + + FreeDataDisconnect(TNC); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + FreeDataDisconnect(TNC); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + FreeDataReleaseTNC(TNC); + } +} + +VOID FreeDataAbort(struct TNCINFO * TNC) +{ + FreeDataSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID FreeDataDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +static RECT Rect1 = {30, 160, 400, 195}; + +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + // Replace forbidden chars with =xx + + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c >> 4) + 'A'; + *(out++) = (c & 15) + 'A'; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + +VOID FreeDataProcessTNCMessage(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + // First Byte of Message is Type. Messages can be commands or short (<120) data packets + // Data is encoded with =xx replacing restricted chars + + Msg[Len] = 0; + + switch (Msg[0]) + { + case 'C': + + // Connect Request. C G8BPQ-10 GM8BPQ-2 (Target, Origin) + + toCall = strtok_s(&Msg[2], " ", &Context); + fromCall = strtok_s(NULL, " ", &Context); + tncCall = strtok_s(NULL, " ", &Context); + + strcpy(TNC->FreeDataInfo->farCall, tncCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataSendCommand(TNC, "d"); + + Debugprintf("FreeDara Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, toCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + // Send Connect ACK + Sleep(1000); + FreeDataSendCommand(TNC, "c"); + return; + + case 'c': + + // Connect ACK + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + + case 'D': + + // Disconnect Command + + FreeDataSendCommand(TNC, "d"); + + // Drop through to disconnect this end + + case 'd': + + // Disconnect complete (response to sending "D") + // Or connect refused in response to "C" + + if (STREAM->Connecting) + { + // Connection Refused - If there is a message, pass to appl + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + if (Msg[1]) + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected - %s\r", &Msg[1]); + else + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + return; + } + + // Release Session + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + + case 'B': + + // Was Base64, but has been expanded - just send to User + + // If len > blocksize, fragment + + Len--; + Msg++; // Remove Type + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Msg, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, 256); + Len -= 256; + Msg += 256; + STREAM->BytesRXed += 256; + + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Msg, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, Len); + STREAM->BytesRXed += Len; + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + + case 'I': + + // Encoded Response + + // Undo = transparency + + ptr = Msg + 1; + + while (ptr = strchr(ptr, '=')) + { + // Next two chars are a hex value + + a = ptr[1] - 'A'; + b = ptr[2] - 'A'; + memmove(ptr, ptr + 2, Len); + ptr[0] = (a << 4) + b; + ptr++; + } + + buffptr = GetBuff(); + buffptr->Len = sprintf(buffptr->Data, "%s", &Msg[1]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + case 'f': + + // FreeDATA File Transfer + + // Seems to be f null fn null data + //f.;Makefile.;.;123123123.; + { + char * FN; + time_t CRC; + int FileLen; + char Filename[256]; + FILE * fp1; + unsigned char * ptr; + char Text[64]; + int textLen; + + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + FN = _strdup(&Msg[3]); + ptr = &Msg[4] + strlen(FN); + + ptr = ptr + strlen(ptr) + 2; + CRC = atoi(ptr); + ptr = ptr + strlen(ptr) + 2; + + FileLen = Len - (ptr - Msg); + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FN); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(ptr, 1, FileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FN, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + + free(FN); + return; + + } + + + } + + if (STREAM->Attached == 0) + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toCall) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + strcpy(TNC->FreeDataInfo->farCall, fromCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataDisconnect(TNC); + + Debugprintf("FreeData Call from %s not in ValidCalls - rejected", fromCall); + return; + } + } + } + /* + + // At the moment we dont support applcalls + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + */ + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, toCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + return; + +} + +VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + +} +extern char LOC[7]; +/* +Line 104: if received_json["type"] == 'PING' and received_json["command"] == "PING": +Line 111: if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile": +Line 142: if received_json["type"] == 'ARQ' and received_json["command"] == "sendMessage": +Line 173: if received_json["type"] == 'ARQ' and received_json["command"] == "stopTransmission": +Line 182: if received_json["type"] == 'GET' and received_json["command"] == 'STATION_INFO': +Line 195: if received_json["type"] == 'GET' and received_json["command"] == 'TNC_STATE': +Line 244: if received_json["type"] == 'GET' and received_json["command"] == 'RX_BUFFER': +Line 258: if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': +Line 272: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': +Line 275: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': +*/ + +//{\"type\" : \"ARQ\", \"command\" : \"sendMessage\", \"dxcallsign\" : \"G8BPQ\", \"mode\" : \"10\", \"n_frames\" : \"1\", \"data\" : \"Hello Hello\" , \"checksum\" : \"123\", \"timestamp\" : 1642580748576} + + + +static unsigned char BANNED[] = {'"', '=', ':', '{', '}', '[', ']', '/', 13, 0}; // I think only need to escape = ": CR Null + + +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + // We can't base64 encode chunks. so buffer as original data and encode on send + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, Msg, Len); + WritetoTrace(TNC, Msg, Len); + + return; +} + + + +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len) +{ + char Message[16284]; + char * Base64; + +/* +{ + "type": "arq", + "command": "send_raw", + "uuid": "sjdfksndfkjsnd-123-sdfvlks", +"parameter": + [ + { + "dxcallsign": "DJ2LS", + "mycallsign": "DN2LS-2", + "mode": "255", + "n_frames": "1", + "data": "" + } + ] +} +*/ + + // TNC now only supports send_raw, with base64 encoded data + + char Template[] = "{\"type\": \"arq\", \"command\": \"send_raw\", \"parameter\":" + "[{\"dxcallsign\": \"%s\", \"mycallsign\": \"%s\", \"mode\": \"255\", \"n_frames\": \"1\", \"data\": \"%s\"}]}\n"; + + Base64 = byte_base64_encode(Msg, Len); + + Len = sprintf(Message, Template, Call, myCall, Base64); + + free(Base64); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +void FlushData(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + int Len = Info->toSendCount; + + // We need to flag as data (B) then base64 encode it + + memmove(&Info->toSendData[1], Info->toSendData, Len); + Info->toSendData[0] = 'B'; + Len++; + + SendAsRaw(TNC, Info->farCall, Info->ourCall, Info->toSendData, Len); + + Info->toSendCount = 0; + Info->toSendTimeout = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + +} + +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + + // Add to buffer + + if ((Info->toSendCount + Len) > 8192) // Reasonable Limit + { + // Send the buffered bit + + FlushData(TNC); + } + + memcpy(&Info->toSendData[Info->toSendCount], Msg, Len); + Info->toSendCount += Len; + Info->toSendTimeout = 10; // About a second + + STREAM->BytesTXed += Len; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->BytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return Len; +} + +static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) +{ + char Message[256]; + int Len; + + Len = sprintf(Message, "{\"type\" : \"%s\", \"command\" : \"%s\"}\n", Type, Command); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +static void SendPoll(struct TNCINFO * TNC) +{ + char DataMsgPoll[] = "{\"type\" : \"GET\", \"command\" : \"RX_MSG_BUFFER\"}\n"; +// char DaemonPoll[] = "{\"type\" : \"GET\", \"command\" : \"DAEMON_STATE\"}\n"; + char TNCPoll[] = "{\"type\" : \"GET\", \"command\" : \"TNC_STATE\", \"timestamp\" : 0}\n"; + + // Poll daemon rapidly until tnc is connected, then every 5 secs + + if ((TNC->PollDelay++ > 50))// || !TNC->TNCCONNECTED) && TNC->DAEMONCONNECTED) + { +// ret = send(TNC->TCPSock, (char *)&DaemonPoll, strlen(DaemonPoll), 0); + TNC->PollDelay = 0; + + } + + if (TNC->TNCCONNECTED) + { +// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); + +// if (TNC->PollDelay & 1) +// ret = SendTNCCommand(TNC, "GET", "RX_BUFFER"); +// else if (TNC->PollDelay & 2) +// ret = SendTNCCommand(TNC, "GET", "RX_MSG_BUFFER"); +// else +// ret = send(TNC->TCPDataSock, (char *)&TNCPoll, strlen(TNCPoll), 0); + + + } + return; +} + +static void SendPing(struct TNCINFO * TNC, char * Call) +{ + char Ping[] = "{\"type\" : \"ping\", \"command\" : \"ping\", \"dxcallsign\" : \"%s\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, Ping, Call, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendCQ(struct TNCINFO * TNC) +{ + char CQ[] = "{\"type\" : \"BROADCAST\", \"command\" : \"cqcqcq\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, CQ, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + + +unsigned short int compute_crc(unsigned char *buf,int len); + + +int FreeDataWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +char * getObjectFromArray(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next object in array + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '{') Open ++; else if (c == '}') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} +/* + ["DATACHANNEL;RECEIVEDOPENER","ARQ;RECEIVING","ARQ;RECEIVING;SUCCESS"] [{"DXCALLSIGN":"GM8BPQ +{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642847440,"RXDATA":[{"dt":"f","fn":"config.json", +"ft":"application\/json","d":"data:application\/json;base64, +ewogICAgInRuY19ob3N0IiA6ICIxMjcuMC4wLjEiLAogICAgInRuY19wb3J0IiA6ICIzMDAwIiwKICAgICJkYWVtb25faG9zdCIgOiAiMTI3LjAuMC4xIiwKICAgICJkYWVtb25fcG9ydCIgOiAiMzAwMSIsCiAgICAibXljYWxsIiA6ICJBQTBBQSIsCiAgICAibXlncmlkIiA6ICJBQTExZWEiICAgIAp9" +,"crc":"123123123"}]} +*/ +void ProcessFileObject(struct TNCINFO * TNC, char * This) +{ + char * Call; + char * LOC; + char * FN; + char * Type; + char * ptr, * ptr2; + int Len, NewLen; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + FN = strstr(This, "fn"); + FN += 5; + This = strlop(FN, '"'); + + Type = strstr(This, "base64"); + Type += 7; + This = strlop(Type, '"'); + + // Decode Base64 + + Len = strlen(Type); + + Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + Type --; + + Type[0] = 'B'; ; // Base64 Info + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen + 1); + + +} + +void ProcessMessageObject(struct TNCINFO * TNC, char * This) +{ + // This gets Call and Message from a RX_MSG_BUFFER array element . It doesn't need care that it is json/ + // as the format is such that keys are unique + + // {"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642581230,"RXDATA":[{"dt":"m","d":"Hello","crc":"123"}]} + + char * Call; + char * LOC; + char * Type; + char * Msg; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + Type = strstr(This, "\"dt\""); + Type += 6; + This = strlop(Type, '"'); + + Msg = strchr(This, ':'); + Msg += 2; + This = strlop(Msg, '"'); + + FreeDataProcessTNCMessage(TNC, Call, Msg, strlen(Msg)); +} + +void processJSONINFO(struct TNCINFO * TNC, char * Info, char * Call, double snr) +{ + char * LOC = ""; + char * ptr, * Context; + + // Info is an array. Normally only one element, but should check + + ptr = strtok_s(&Info[1], ",]", &Context); + + while (ptr && ptr[1]) + { + if (strstr(ptr, "BEACON;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + if (strstr(ptr, "PING;RECEIVING")) + { + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "CQ;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "CQ received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "PING;RECEIVEDACK")) + { + char Msg[128]; + int Len; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", Call, snr); + FreeDataProcessTNCMessage(TNC, Call, Msg, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "TRANSMITTING;FAILED")) + { + // Failed to send a message - if it was a connect request tell appl + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + ptr = strtok_s(NULL, ",]", &Context); + } +} + + + + + + +char * getJSONValue(char * Msg, char * Key) +{ + char * ptr, *ptr2, *value = 0; + int vallen, keylen = strlen(Key); + char c; + + // We Null Terminate the value, so we must look for keys in reverse order + + ptr = strstr(Msg, Key); + + if (ptr) + { + ptr += (keylen + 1); + + if (*(ptr) == '[') + { + // Array + + int Open = 0; + int Close = 0; + + ptr2 = ptr; + + while (c = *(ptr++)) + { + if (c == '[') + Open ++; + else if (c == ']') + Close ++; + + if (Open == Close) + { + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + return value; + } + } + } + else if (*(ptr) == '\"') + { + // String + + ptr2 = ptr; + ptr = strchr(ptr + 1, '\"'); + if (ptr) + { + ptr++; + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + } + } + } + return value; +} + + +char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; + + +void ProcessDAEMONJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + char * capture, * playback; + + + if (memcmp(Msg, "{\"command\":\"daemon_state\"", 25) == 0) + { +/* + {"COMMAND":"DAEMON_STATE","DAEMON_STATE":[{"STATUS":"stopped"}], + "PYTHON_VERSION":"3.9", + "HAMLIB_VERSION":"4.0", + "INPUT_DEVICES":[{"ID":5,"NAME":"pulse"},{"ID":9,"NAME":"default"}], + "OUTPUT_DEVICES":[{"ID":0,"NAME":"bcm2835 Headphones: - (hw:0,0)"},{"ID":1,"NAME":"sysdefault"},{"ID":2,"NAME":"lavrate"},{"ID":3,"NAME":"samplerate"},{"ID":4,"NAME":"speexrate"},{"ID":5,"NAME":"pulse"},{"ID":6,"NAME":"upmix"},{"ID":7,"NAME":"vdownmix"},{"ID":8,"NAME":"dmix"},{"ID":9,"NAME":"default"}], + "SERIAL_DEVICES":[{"PORT":"\/dev\/ttyAMA0","DESCRIPTION":"ttyAMA0 [1dff]"}], + "CPU":"39.6", + "RAM":"38.6", + "VERSION":"0.1-prototype"} +*/ + + Msg += 25; + + playback = getJSONValue(Msg, "\"output_devices\""); + capture = getJSONValue(Msg, "\"input_devices\""); + + if (TNC->CaptureDevices) + free(TNC->CaptureDevices); + + TNC->CaptureDevices = _strdup(capture); + + if (TNC->PlaybackDevices) + free(TNC->PlaybackDevices); + + TNC->PlaybackDevices = _strdup(playback); + + ptr = getJSONValue(Msg, "\"daemon_state\""); + + if (strstr(ptr, "stopped")) + { + TNC->FreeDataInfo->TNCRunning = 0; + // if we have Capture and Playback devices, then start the TNC, else look in message for them + + if (TNC->CaptureDevices) + { + if (TNC->FreeDataInfo->startingTNC == 0) + { + // Find Capture and Playback indices + + struct FreeDataINFO * Info = TNC->FreeDataInfo; + char * devptr = stristr(TNC->CaptureDevices, Info->Capture); + int capindex = -1, playindex = -1; + char startTNC[] = "{\"type\":\"set\",\"command\":\"start_tnc\"," + "\"parameter\":[{\"mycall\":\"%s\",\"mygrid\":\"%s\",\"rx_audio\":\"%d\"," + "\"tx_audio\":\"%d\",\"devicename\":\"RIG_MODEL_NETRIGCTL\",\"deviceport\":\"/dev/ttyAMA0\"," + "\"serialspeed\":\"19200\"," + "\"pttprotocol\":\"RTS\",\"pttport\":\"/dev/ttyAMA0\",\"data_bits\":\"8\"," + "\"stop_bits\":\"1\",\"handshake\":\"None\", \"radiocontrol\":\"disabled\"," + "\"rigctld_ip\":\"%s\",\"rigctld_port\":\"%d\"," + "\"enable_scatter\":\"0\",\"enable_fft\":\"0\",\"low_bandwith_mode\":\"0\"}]}\n"; + + + char Command[512]; + int Len; + + if (devptr) + { + while (*(--devptr) != '{'); // find start of subparam + capindex = atoi(&devptr[6]); + } + + devptr = stristr(TNC->PlaybackDevices, Info->Playback); + + if (devptr) + { + while (*(--devptr) != '{'); // find start of subparam + playindex = atoi(&devptr[6]); + } + + if (capindex > -1 && playindex > -1) + { + Len = sprintf(Command, startTNC, TNC->FreeDataInfo->ourCall, LOC, capindex, playindex, + TNC->FreeDataInfo->hamlibHost, TNC->FreeDataInfo->hamlibPort); + send(TNC->TCPSock, Command, Len, 0); +// TNC->FreeDataInfo->startingTNC = 5; + } + } + } + else + { + ptr = getJSONValue(Msg, "\"version\""); + + } + return; + } + TNC->FreeDataInfo->TNCRunning = 1; + + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) + ConnectTNCPort(TNC); + + return; + + } + + Debugprintf(Msg); +} + +void StopTNC(struct TNCINFO * TNC) +{ + char stopTNC[] = "{\"type\" : \"set\", \"command\": \"stop_tnc\" , \"parameter\": \"---\"}"; + + if (TNC->TNCCONNECTED) + { + send(TNC->TCPSock, stopTNC, strlen(stopTNC), 0); + closesocket(TNC->TCPDataSock); + } +} + + +void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + + if (memcmp(Msg, "{\"ptt\":", 7) == 0) + { + if (strstr(Msg, "True")) + { +// TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + } + else + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + } + return; + } + + if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) + { +/* +{"COMMAND":"TNC_STATE","TIMESTAMP":1642429227972,"PTT_STATE":"False","TNC_STATE":"IDLE","ARQ_STATE":"False", +"AUDIO_RMS":"0","SNR":"0","FREQUENCY":"145000000","MODE":"FM","BANDWITH":"15000", +"FFT":"[47.7, 28.9, 19.8, 26.4, 22.9, 19.9, 20.0, 20.1, 21.8, 24.0, 21.4, 14.9, 20.6, 17.9, 19.9, 18.4, 20.8, +22.2, 17.8, 23.7, 17.5, 15.6, 21.7, 17.5, 20.4, 21.7, 23.4, 18.2, 21.6, 17.0, 18.7, 18.2, 19.8, 18.6, 20.2, +.. +12.0, 11.9, 12.9, 13.5, 14.3, 14.6, 16.5, 15.4, 14.9, 13.7, 15.6, 12.3]", +"SCATTER":[],"RX_BUFFER_LENGTH":"0","RX_MSG_BUFFER_LENGTH":"0","ARQ_BYTES_PER_MINUTE":"0","ARQ_BYTES_PER_MINUTE_BURST":"0","ARQ_COMPRESSION_FACTOR":"0","ARQ_TRANSMISSION_PERCENT":"0","TOTAL_BYTES":"0","INFO":[],"BEACON_STATE":"False","STATIONS":[],"EOF":"EOF"}{"type" : "GET", "command" : "TNC_STATE", "timestamp" : 1642429228121} + +"arq_session":"True" +\"arq_session_state\":\"disconnected\" +*/ + char * LOC = 0; + char * Stations; + char * myCall = 0; + char * farCall = 0; + double snr; + int arqstate = 0; + + Msg += 23; + + ptr = strstr(Msg, "snr"); + + if (ptr) + snr = atof(ptr + 6); + + Stations = getJSONValue(Msg, "\"stations\""); + + if (Stations) + { + ptr = Stations + strlen(Stations) + 1; + LOC = getJSONValue(ptr, "\"dxgrid\""); + farCall = getJSONValue(ptr, "\"dxcallsign\""); + myCall = getJSONValue(ptr, "\"mycallsign\""); + + if (myCall && farCall) + { + myCall++; + strlop(myCall, '"'); + farCall++; + strlop(farCall, '"'); + } + } + + // Look for changes in arq_session_state + + ptr = strstr(Msg, "\"arq_session_state\""); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 21; + + if (memcmp(ptr, "disconnected", 10) == 0) + { + arqstate = 1; + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else if (memcmp(ptr, "connecting", 10) == 0) + arqstate = 2; + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + arqstate = 3; + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + arqstate = 4; + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + arqstate = 5; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + + Debugprintf("%d arq_session_state %d", TNC->Port, arqstate); + } + + + ptr = getJSONValue(Msg, "\"info\""); + + if (strcmp(ptr, "[]") != 0) + { + + processJSONINFO(TNC, ptr, farCall, snr); + Debugprintf("%d %s %s", TNC->Port, ptr, farCall); + } + + return; + } + +// {"COMMAND":"RX_BUFFER","DATA-ARRAY":[],"EOF":"EOF"} +/* {"COMMAND":"RX_BUFFER","DATA-ARRAY":[{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642579504, +"RXDATA":[{"dt":"f","fn":"main.js","ft":"text\/javascript" +,"d":"data:text\/javascript;base64,Y29uc3Qge.....9KTsK","crc":"123123123"}]}],"EOF":"EOF"} + + + +{"arq":"received","uuid":"a1346319-6eb0-42aa-b5a0-c9493c8ccdca","timestamp":1645812393,"dxcallsign":"G8BPQ-2","dxgrid":"","data":"QyBHOEJQUS0yIEc4QlBRLTIgRzhCUFEtMiA="} +{"ptt":"True"} + + +*/ + if (memcmp(Msg, "{\"arq\":\"received\"", 17) == 0) + { + int NewLen; + char * ptr, *ptr2, *Type; + char * Call = 0; + char * myCall = 0; + + Msg += 17; + + ptr = getJSONValue(Msg, "\"data\""); + Type = ++ptr; + + // Decode Base64 + + // FreeData replacs / with \/ so need to undo + + ptr2 = strstr(Type, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Type) - 1; + + // Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + myCall = getJSONValue(Msg, "\"mycallsign\""); + Call = getJSONValue(Msg, "\"dxcallsign\""); + + if (Call) + { + Call++; + strlop(Call, '"'); + } + + if (myCall) + { + myCall++; + strlop(myCall, '"'); + } + + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen); + + return; + } + + if (memcmp(Msg, "{\"COMMAND\":\"RX_MSG_BUFFER\"", 26) == 0) + { + char * Next, * This; + + Msg += 26; + ptr = getJSONValue(Msg, "\"EOF\""); + ptr = getJSONValue(Msg, "\"DATA-ARRAY\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + } while (Next && Next[0] == '{'); + + // Delete from TNC + + SendTNCCommand(TNC, "SET", "DEL_RX_MSG_BUFFER"); + } + + + return; + } + Debugprintf(Msg); +} + +int FreeDataConnect(struct TNCINFO * TNC, char * Call) +{ + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, Connect, Call); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataDisconnect(struct TNCINFO * TNC) +{ + char Disconnect[] = "{\"type\" : \"arq\", \"command\": \"disconnect\"}\n"; + char Msg[128]; + int Len; + +// return FreeDataSendCommand(TNC, "D"); + + Len = sprintf(Msg, Disconnect); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) +{ + // Commands are simulated as Messages to the remote BPQ. The TNC itself does not handle any commands + + // First Byte of MSG is a Type - Command or Data. MSG has a limited character set Use =xx for Now. + + // Current Types - C = Connect, D = Disconnect, I = info + + SendAsRaw(TNC, TNC->FreeDataInfo->farCall, TNC->FreeDataInfo->ourCall, Msg, strlen(Msg)); + return 0; +} + +void FreeDataProcessDaemonMsg(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr; + int OpenBraces; + int CloseBraces; + char c; + + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8191 - TNC->InputLen, 0); + + if (InputLen == 8191) + c = 0; + + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->DAEMONCONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + +loop: + TNC->ARDOPBuffer[TNC->InputLen] = 0; // So we cat use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + + + if (TNC->ARDOPBuffer[0] != '{') + { + TNC->InputLen = 0; + return; + } + + ptr = &TNC->ARDOPBuffer[0]; + + OpenBraces = 0; + CloseBraces = 0; + + while (c = *(ptr++)) + { + if (c == '{') + OpenBraces ++; + else if (c == '}') + CloseBraces ++; + + if (OpenBraces == CloseBraces) + { + MsgLen = ptr - (char * )TNC->ARDOPBuffer; + + ProcessDAEMONJSON(TNC, TNC->ARDOPBuffer, MsgLen); + + if (TNC->InputLen == 0 || *ptr == 0) + { + TNC->InputLen = 0; + return; + } + + // More in buffer + + memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen - MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + + } + // Message Incomplete - wait for rest; +} + + + + + + +void FreeDataProcessTNCMsg(struct TNCINFO * TNC) +{ + int DataInputLen, MsgLen; + char * ptr, * endptr; + + // May get message split over packets or multiple messages per packet + + // A complete file transfer arrives as one message, so can bw very long + + + if (TNC->DataInputLen > MAXRXSIZE) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + DataInputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], MAXRXSIZE - TNC->DataInputLen, 0); + + if (DataInputLen == 0 || DataInputLen == SOCKET_ERROR) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TNCCONNECTED = FALSE; + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->DataInputLen += DataInputLen; + + TNC->ARDOPDataBuffer[TNC->DataInputLen] = 0; // So we cat use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + +// Debugprintf(TNC->ARDOPDataBuffer); + + //I think now messages end with LF + +loop: + + endptr = strchr(TNC->ARDOPDataBuffer, 10); + + if (endptr == 0) + return; + + *(endptr) = 0; + + if (TNC->ARDOPDataBuffer[0] != '{') + { + TNC->DataInputLen = 0; + return; + } + + ptr = &TNC->ARDOPDataBuffer[0]; + + MsgLen = endptr - ptr; + + ProcessTNCJSON(TNC, ptr, MsgLen); + + // MsgLen doesnt include lf + + MsgLen++; + + if (TNC->DataInputLen == MsgLen) + { + TNC->DataInputLen = 0; + return; + } + + // More in buffer + + ptr += MsgLen; + TNC->DataInputLen -= MsgLen; + + memmove(TNC->ARDOPDataBuffer, ptr, TNC->DataInputLen + 1); + + goto loop; + + // Message Incomplete - wait for rest; +} + + + +VOID FreeDataThread(void * portptr); + +int ConnecttoFreeData(int port) +{ + _beginthread(FreeDataThread, 0, (void *)(size_t)port); + + return 0; +} + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + + +VOID FreeDataThread(void * portptr) +{ + // FreeData TNC has two sessions, not sure why! + + // Messages are JSON encapulated + // Opens deamon socket. TNC socket is only available once Start TNC command sent + // Looks for data on socket(s) + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->DAEMONCONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +// printf("Starting FreeDATA Thread\n"); + +// if on Windows and Localhost see if TNC is running + +#ifdef WIN32 + + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + + if (TNC->PID == 0) + goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + goto TNCRunning; + } + +#endif + +TNCNotRunning: + + // Not running or can't check, restart if we have a path + +/* if (TNC->ProgramPath) + { + Consoleprintf("Trying to (re)start TNC %s", TNC->ProgramPath); + + if (RestartTNC(TNC)) + CountRestarts(TNC); + + Sleep(TNC->AutoStartDelay); + } +*/ +TNCRunning: + + if (TNC->Alerted == FALSE) + { + sprintf(TNC->WEB_COMMSSTATE, "Connecting to Daemon"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->DAEMONCONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for FreeData Host - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPDataSock); +// closesocket(TNC->TCPSock); + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->DAEMONCONNECTING = FALSE; + return; + } + + TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData Data Daemon socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->DAEMONCONNECTING = FALSE; + closesocket(TNC->TCPDataSock); + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + // Connect Daemon Port + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FreeData Daemon socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->DAEMONCONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->DAEMONCONNECTING = FALSE; + TNC->DAEMONCONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + TNC->Alerted = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData Daemon"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + sprintf(Msg, "Connected to FreeData Daemon Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title +// EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + while (TNC->DAEMONCONNECTED || TNC->TNCCONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + if (TNC->DAEMONCONNECTED) + FD_SET(TNC->TCPSock,&readfs); + + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->TNCCONNECTED) + FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("FreeData Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessTNCMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessDaemonMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { +Lost: + sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->TNCCONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPDataSock); + TNC->TCPDataSock = 0; + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { + sprintf(Msg, "FreeData Daemon Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to Daemon lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->DAEMONCONNECTED = 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); + TNC->TCPSock = 0; + } + continue; + } + else + { + } + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +void ConnectTNCPort(struct TNCINFO * TNC) +{ + char Msg[255]; + int err; + int bcopt = TRUE; + + TNC->TNCCONNECTING = TRUE; + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // Connected successful + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->Alerted = FALSE; + return; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + TNC->TNCCONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + return; +} + diff --git a/GetVersion.h b/GetVersion.h new file mode 100644 index 0000000..30c0161 --- /dev/null +++ b/GetVersion.h @@ -0,0 +1,28 @@ + +char VersionString[50]=""; +char VersionStringWithBuild[50]=""; +int Ver[4] = {Vers}; +char TextVerstring[50] = ""; + +VOID GetVersionInfo(TCHAR * File) +{ +#ifndef LINBPQ + + char isDebug[40]=""; + +#ifdef SPECIALVERSION + strcat(isDebug, SPECIALVERSION); +#endif +#ifdef _DEBUG + strcat(isDebug, "Debug Build "); +#endif + + sprintf(VersionString,"%d.%d.%d.%d %s", Ver[0], Ver[1], Ver[2], Ver[3], isDebug); + + sprintf(TextVerstring,"V%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]); + + sprintf(VersionStringWithBuild,"%d.%d.%d Build %d %s", Ver[0], Ver[1], Ver[2], Ver[3], isDebug); + + return; +#endif +} diff --git a/HALDriver.c b/HALDriver.c new file mode 100644 index 0000000..a59669a --- /dev/null +++ b/HALDriver.c @@ -0,0 +1,1906 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define HAL 1 + +#define SetMYCALL 0x13 +#define ConnectEnable 0x52 +#define ConnectDisable 0x42 +#define SetEAS 0x59 // Echo as Sent +#define SetTones 0xec +#define ClearOnDisc 0x57 + +static char ClassName[]="HALSTATUS"; + +static char WindowTitle[] = "HAL"; +static int RigControlRow = 185; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +//int MaxStreams = 0; + +#ifndef LINBPQ +extern HFONT hFont; +#endif + +static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", + "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", + "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", + "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", + "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +static void CheckRX(struct TNCINFO * TNC); +VOID HALPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); +VOID ProcessHALCmd(struct TNCINFO * TNC); +VOID ProcessHALData(struct TNCINFO * TNC); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +BOOL HALConnected(struct TNCINFO * TNC, char * Call); +VOID HALDisconnected(struct TNCINFO * TNC); + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID COMClearDTR(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + + + +//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +//char BaseDir[]="c:"; + +static VOID CloseLogfile(int Flags) +{ +// CloseHandle(LogHandle[Flags]); +// LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + +static VOID OpenLogfile(int Flags) +{ +/* +UCHAR FN[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +*/ +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +// int cnt; +// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + TNC->WL2K = DecodeWL2KReportLine(buf); + continue; + } + if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) + { + TNC->XONXOFF = TRUE; + continue; + } + + if (_memicmp(buf, "TONES", 5) == 0) + { + int tone1 = 0, tone2 = 0; + + ptr = strtok(&buf[6], " ,/\t\n\r"); + if (ptr) + { + tone1 = atoi(ptr); + ptr = strtok(NULL, " ,/\t\n\r"); + if (ptr) + { + tone2 = atoi(ptr); + ptr = &TNC->InitScript[TNC->InitScriptLen]; + + // Try putting into FSK mode first + + *(ptr++) = 0x84; + *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) + *(ptr++) = tone1 >> 8; + *(ptr++) = tone1 & 0xff; + *(ptr++) = tone2 >> 8; + *(ptr++) = tone2 & 0xff; + + TNC->InitScriptLen += 6; + + continue; + } + } + goto BadLine; + } + if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) + { + + ptr = strtok(&buf[12], " ,\t\n\r"); + if (ptr) + { + if (_stricmp(ptr, "CLOVER") == 0) + TNC->DefaultMode = Clover; + else if (_stricmp(ptr, "PACTOR") == 0) + TNC->DefaultMode = Pactor; + else if (_stricmp(ptr, "AMTOR") == 0) + TNC->DefaultMode = AMTOR; + else goto BadLine; + + continue; + } + goto BadLine; + } + } + BadLine: + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + } + + return (TRUE); +} + +static size_t ExtProc(int fn, int port,unsigned char * buff) +{ + int txlen = 0; + UINT * buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL) + return 0; + + if (fn < 4 || fn > 5) + if (TNC->hDevice == 0) + return 0; // Port not open + + STREAM = &TNC->Streams[0]; + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff[4] = 0; + + return -1; + } + } + + CheckRX(TNC); + HALPoll(port); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen=buffptr[1]; + + buff[4] = 0; + buff[7] = 0xf0; + memcpy(&buff[8],buffptr+2,datalen); // Data goes to +7, but we have an extra byte + datalen+=8; + + PutLengthinBuffer((PDATAMESSAGE)buff, datalen); + + // buff[5]=(datalen & 0xff); + // buff[6]=(datalen >> 8); + + 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[4]; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr[1] = 36; + memcpy(buffptr+2, "No Connection to PACTOR TNC\r", 36); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + buffptr[1] = txlen; + memcpy(buffptr+2, &buff[8], txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)buff; + + if (STREAM->FramesQueued > 4) + return (1 | TNC->HostMode << 8); + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "HAL Status

HAL Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +UINT HALExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int len; + char Msg[80]; + HWND x; + + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); + WritetoConsole(msg); + + return (int)ExtProc; + } + + TNC->Port = port; + + TNC->Hardware = H_HAL; + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->MAXHOSTMODESESSIONS = 1; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + if (TNC->DefaultMode) + TNC->CurrentMode = TNC->DefaultMode; + else + TNC->CurrentMode = Clover; + + TNC->PollDelay = 999999999; + + // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL + + len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); + len++; // We include the NULL + + memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); + TNC->InitScriptLen += len; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_LEDS = zalloc(100); + strcpy(TNC->WEB_LEDS, " X X X X X X"); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); + + x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + TNC->ClientHeight = 233; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + SendCmd(TNC, "\x09" , 1); // Reset + + WritetoConsole("\n"); + + return ((int)ExtProc); +} + + + +static VOID KISSCLOSE(int Port) +{ + struct TNCINFO * conn = TNCInfo[Port]; + + // drop DTR and RTS + + COMClearDTR(conn->hDevice); + COMClearRTS(conn->hDevice); + + // purge any outstanding reads/writes and close device handle + + CloseCOMPort(conn->hDevice); + + return; +} + + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * Xptr; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // We need to konw whether data is received or echoed, so we can't split commands and data here. + // Pass everything to the Command Handler. It will check that there are enough bytes for the command, + // and wait for more if not. + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // If USB version, we might get unescaped xon and xoff, which we must ignore + + if (TNC->XONXOFF) + { + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + + while(Xptr) + { + Debugprintf("XON Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + + while(Xptr) + { + Debugprintf("XOFF Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape + + if (Xptr) + + // Make sure we have the escaped char as well + + if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char + return; + } + + ProcessHALBuffer(TNC, Length); + + TNC->RXLen = 0; + + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + return TRUE; +} + +VOID HALPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + UCHAR TXMsg[1000]; + int datalen; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + } + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (TNC->TNCOK) + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + SendCmd(TNC, "\x42", 1); // Connect Enable off + + return; + + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + + if (STREAM->Attached) + CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + + UCHAR TXMsg[80]; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + // Set Listen Mode + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x58", 1); // Listen + + break; + + case Clover: + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + SendCmd(TNC, "\x60\x09", 2); // Robust Retries + SendCmd(TNC, "\x61\x09", 2); // Normal Retries + + break; + } + + SendCmd(TNC, "\x52", 1); // ConnectEnable + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, Status); + + return; + } + } + +#define MAXHALTX 256 + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) + { + int datalen; + UINT * buffptr; + UCHAR * MsgPtr; + unsigned char TXMsg[500]; + + buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; + datalen=buffptr[1]; + MsgPtr = (UCHAR *)&buffptr[2]; + + if (STREAM->Connected) + { + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + STREAM->FramesQueued--; + return; + } + } + + // Must send data in small chunks - the Hal has limited buffer space + + // If in IRS force a turnround + + if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) + { + if (TNC->TimeInRX++ > 15) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + else + goto Poll; + } + + TNC->TimeInRX = 0; + + EncodeAndSend(TNC, MsgPtr, datalen); + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + WriteLogLine(2, MsgPtr, datalen); + + STREAM->BytesTXed += datalen; + STREAM->FramesQueued--; + + ShowTraffic(TNC); + + return; + } + else + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if (memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) + { + TNC->CurrentMode = Clover; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + MySetWindowText(TNC->xIDC_MODE, "Clover"); + strcpy(TNC->WEB_MODE, "Clover"); + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + + return; + } + + if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) + { + TNC->CurrentMode = Pactor; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x48", 1); // Listen Off + + return; + } + if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) + { + TNC->CurrentMode = AMTOR; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + + datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); + + // DOnt set connecting till we get the 19 response so we can trap listen as a fail + break; + + case Clover: + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); + + break; + } + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "CLOVER ", 7) == 0) + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", + STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + SendCmd(TNC, "\x07", 1); // Normal Disconnect + TNC->NeedPACTOR = 50; + + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? Treat as HEX string + + datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", + (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], + (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], + (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], + (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); + +// SendCmd(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + } + } + } +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 20) + return; + + TNC->PollDelay = 0; + + if (TNC->TNCOK) + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll + else + SendCmd(TNC, "\x09" , 1); // Reset + + TNC->Timeout = 100; + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + // TNC Has Restarted, send init commands (can probably send all at once) + +// TNC->TXBuffer[0] = 0x1b; +// TNC->TXLen = 1; + + WriteCommBlock(TNC); + + SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 20; // Need to set Calls and start scan + + TNC->DataMode = RXDATA; // Start with RX Data + + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll +// SendCmd(TNC, "\xc9" , 1); // Huffman Off + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries + +// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem + + TNC->Timeout = 50; + + return; + +} + +VOID ProcessHALData(struct TNCINFO * TNC) +{ + // Received Data just pass to Appl + + UINT * buffptr; + int Len = TNC->DataLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + TNC->DataLen = 0; + + if (TNC->DataMode == TXDATA) + { + STREAM->BytesAcked += Len; +// Debugprintf("Acked %d", Len); + + if (STREAM->BytesAcked > STREAM->BytesTXed) + Debugprintf("Too Much Acked"); + + if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) + { + // All sent + + if (STREAM->Disconnecting) + TidyClose(TNC, 0); + else + if (TNC->CurrentMode != Clover) + + // turn round link + + SendCmd(TNC, "\x0c" , 1); // Turnround + + } + } + else + { + if (TNC->DataMode == RXDATA) + { +// Debugprintf("RXed %d", Len); + buffptr = GetBuff(); + if (buffptr == NULL) + return; // No buffers, so ignore + + buffptr[1] = Len; // Length + + WriteLogLine(1, TNC->DataBuffer, Len); + + STREAM->BytesRXed += Len; + + memcpy(&buffptr[2], TNC->DataBuffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + + ShowTraffic(TNC); + + return; +} + + + +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) +{ + UCHAR Char; + UCHAR * inptr; + UCHAR * cmdptr; + UCHAR * dataptr; + BOOL CmdEsc, DataEsc; + + inptr = TNC->RXBuffer; + + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + CmdEsc = TNC->CmdEsc; + DataEsc = TNC->DataEsc; + + // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // Command Responses can be variable length + + // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives + + while(Length--) + { + Char = *(inptr++); + + if (CmdEsc) + { + CmdEsc = FALSE; + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape. We ensured above that data follows so we can process it inline + + Length--; + Char = *(inptr++) - 0x20; + } + *(cmdptr++) = Char; + } + else if (DataEsc) + { + DataEsc = FALSE; + goto DataChar; + } + else +NotData: + if (Char == 0x80) // Next Char is Command + CmdEsc = TRUE; + else if (Char == 0x81) // Next Char is escaped data (80 or 81) + DataEsc = TRUE; + else + { + // This is a Data Char. We must process any Commands received so far, so we know the type of data + + DataChar: + + TNC->CmdLen = cmdptr - TNC->CmdBuffer; + ProcessHALCmd(TNC); + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + + *(dataptr++) = Char; // Normal Data + + // Now process any other data chars + + while(Length--) + { + Char = *(inptr++); + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape within data. We ensured above that data follows so we + // can process it here + + Length--; + Char = *(inptr++) - 0x20; + } + + if (Char == 0x80 || Char == 0x81) + { + // Process any data we have, then loop back + + TNC->DataLen = dataptr - TNC->DataBuffer; + ProcessHALData(TNC); + + goto NotData; + } + *(dataptr++) = Char; // Normal Data + } + + // Used all data + + TNC->DataLen = dataptr - TNC->DataBuffer; + + ProcessHALData(TNC); + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + return; + } + } + + // Save State + + TNC->CmdLen = cmdptr - TNC->CmdBuffer; + + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + if (TNC->DataLen) + ProcessHALData(TNC); + + if (TNC->CmdLen) + ProcessHALCmd(TNC); +} + +VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) +{ + MySetWindowText(TNC->xIDC_STATE, Msg); + strcpy(TNC->WEB_STATE, Msg); +} + +VOID ProcessHALCmd(struct TNCINFO * TNC) +{ + char * Call; + int Stream = 0; + int Opcode; + int StatusByte; + int Leds; + int Len; + int Used; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + +CmdLoop: + + Opcode = TNC->CmdBuffer[0]; + Len = TNC->CmdLen; + + if (Len == 0) + return; + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs + + switch(Opcode) + { + case 0x09: //Hardware Reset - equivalent to power on reset + + // Hardware has reset - need to reinitialise + + TNC->HostMode = 0; // Force Reinit + + Used = 1; + break; + + case 0x7a: // FSK Modes Status + + // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x06: // FSK TX (RTTY) + case 0x07: // FSK RX (RTTY) + case 0x10: // AMTOR STANDBY (LISTEN ON) + case 0x11: // AMTOR STANDBY (LISTEN OFF) + case 0x12: // AMTOR FEC TX (AMTOR) + case 0x13: // AMTOR FEC RX (AMTOR) + case 0x14: // P-MODE FEC TX (P-MODE) + case 0x15: // FREE SIGNAL TX (AMTOR) + case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) + + // Diaplay Linke Status + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + break; + + case 0x0C: // P-MODE STANDBY (LISTEN ON) + case 0x0D: // P-MODE STANDBY (LISTEN OFF) + + // if we were connecting, this means connect failed. + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + if (STREAM->Connecting) + HALDisconnected(TNC); + + break; + + case 0x0E: // ISS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"ISS"); + strcpy(TNC->WEB_TXRX, "ISS"); + TNC->TXRXState = 'S'; + break; + + case 0x0F: // IRS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"IRS"); + strcpy(TNC->WEB_TXRX, "IRS"); + TNC->TXRXState = 'R'; + break; + + case 0x00: // IDLE (AMTOR/P-MODE) + case 0x01: // TFC (AMTOR/P-MODE) + case 0x02: // RQ (AMTOR/P-MODE) + case 0x03: // ERR (AMTOR/P-MODE) + case 0x04: // PHS (AMTOR/P-MODE) + case 0x05: // OVER (AMTOR/P-MODE) (not implemented) + + MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + + +//$807A $8008 P-MODE100 (P-MODE) +//$807A $8009 P-MODE200 (P-MODE) +//$807A $800A HUFFMAN ON (P-MODE) +//$807A $800B HUFFMAN OFF (P-MODE) + ; + } + Used = 2; + break; + + + case 0x7d: // Get LED Status + + // We use Get LED Status as a Poll + + if (Len < 2) return; // Wait for more + + Leds = TNC->CmdBuffer[1]; + sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", + (Leds & 0x20)? 'X' : ' ', + (Leds & 0x10)? 'X' : ' ', + (Leds & 0x08)? 'X' : ' ', + (Leds & 0x04)? 'X' : ' ', + (Leds & 0x02)? 'X' : ' ', + (Leds & 0x01)? 'X' : ' '); + +// STBY CALL LINK ERROR TX RX + MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); + + Used = 2; + break; + + case 0x21: // Monitored FEC CCB + case 0x22: // Monitored ARQ CCB + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + UpdateMH(TNC, Call, '!', 0); + + break; + + case 0x27: // Clover ARQ LINK REQUEST status message + + //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). + + if (Len < 2) return; // Wait for more + + // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL + Used = 2; + break; + + case 0x2D: // FSK ARQ Link Request status message + + // $802D $8001 $8000 CLOVER Link Request (not implemented) + // $802D $8002 $8000 AMTOR CCIR-476 Link Request + // $802D $8003 $8000 AMTOR CCIR-625 Link Request + // $802D $8004 $8000 P-MODE Link Request + + if (Len < 3) return; // Wait for more + + // Don't need to do anything (but may save Session type later + + Used = 3; + break; + + + case 0x28: // Monitored Call + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls + + break; + + + case 0x20: // Clover Linked with - Call Connected + case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. + case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . + case 0x2B: // P-MODE link to + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + HALConnected(TNC, Call); + + break; + + case 0x23: // Normal Disconnected - followed by $8000 + case 0x24: // Link failed (any of the link errors) + case 0x25: // Signal Lost (LOS) + + if (Len < 2) return; // Wait for more + + HALDisconnected(TNC); + + Used = 2; + break; + + + // Stream Switch Reports - we will need to do something with these if Echo as Sent is set + // or we do something with the secondary port + + case 0x30: // Switch to Receive Data characters + case 0x31: // Switch to Transmit Data characters + case 0x32: // Switch to RX data from secondary port + + TNC->DataMode = Opcode; + Used = 1; + break; + + case 0x33: // Send TX data to modem + case 0x34: // Send TX data to secondary port + + TNC->TXMode = Opcode; + Used = 1; + break; + + case 0x70: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 9) return; // Wait for more + + Used = 9; + break; + + case 0x71: // SelCall On/Off + + if (Len < 2) return; // Wait for more + + Used = 2; + break; + + case 0x72: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 15) return; // Wait for more + + Used = 15; + break; + + case 0x73: // Clover Link state + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x00: mySetWindowText(TNC, "Channel idle"); break; + case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; + case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; + case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; + case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; + case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; + case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; + case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; + case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; + case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; + case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; + case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; + case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; + case 0x8E: mySetWindowText(TNC, "TX idle"); break; + case 0x8F: mySetWindowText(TNC, "RX idle"); break; + case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; + case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; + case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; + case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; + } + + Used = 2; + break; + + case 0x75: // Clover waveform format + + if (Len < 5) return; // Wait for more + + Used = 5; + break; + + case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy + // $807F $80xx $8030 Invalid or unimplemented command code + // $807F $80xx $8031 Invalid parameter value + // $807F $80xx $8032 Not allowed when connected + // $807F $80xx $8033 Not allowed when disconnected + // $807F $80xx $8034 Not valid in this mode + // $807F $80xx $8035 Not valid in this code + // $807F $8096 $8036 EEPROM write error + + if (Len < 3) return; // Wait for more + + if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) + { + // Reject of XON/XOFF enable + +// TNC->XONXOFF = FALSE; +// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); + } + else + Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); + + Used = 3; + break; + + // Following are all immediate commands - response is echo of command + + case 0x6f: // XON/XOFF on + +// TNC->XONXOFF = TRUE; // And drop through +// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); + + case 0x19: // Call P-MODE to + case 0x10: // Robust Link to using MYCALL + case 0x11: // Normal Link to using MYCALL + + STREAM->Connecting = TRUE; + + case 0x00: // P Load LOD file + case 0x01: // P Load S28 file + case 0x02: //Check Unit Error Status + case 0x03: //F Check System Clock + case 0x04: //C Close PTT and transmit Clover waveform + case 0x05: //Open PTT and stop transmit test + case 0x06: //Immediate Abort (Panic Kill) + case 0x07: //Normal disconnect (wait for ACK) + case 0x08: //Software reset - restore all program defaults + case 0x0A: //Send CW ID + case 0x0B: //Close PTT and transmit Single Tone + case 0x0C: //F Normal OVER (AMTOR,P-MODE) + case 0x0D: //F Force RTTY TX (Baudot/ASCII) + case 0x0E: //F Go to RTTY RX (Baudot/ASCII) + case 0x0F: //Go to LOD/S28 file loader + case SetMYCALL: // Set MYCALL Response + + case 0x1E: // Set MYALTCALL Response + + case 0x41: + case 0x42: + case 0x46: + case 0x47: + case 0x48: + case 0x4d: + case 0x52: // Enable adaptive Clover format + case 0x54: // Enable adaptive Clover format + + case 0x56: // Expanded Link State Reports OFF/ON + case 0x57: // Clear buffers on disc + case 0x58: + case 0x59: + case 0x60: // Robust Mode Retries + case 0x61: // Normal Mode Retries + case 0x80: //Switch to CLOVER mode + case 0x81: //Select AMTOR Standby + case 0x82: //Select AMTOR FEC + case 0x83: //Select P-MODE Standby + case 0x84: //Switch to FSK modes + case 0x85: //Select Baudot + case 0x86: //Select ASCII + case 0x87: //Forced OVER (AMTOR, P-MODE) + case 0x88: //Forced END (AMTOR, P-MODE) + case 0x89: //Force LTRS shift + case 0x8A: //Force FIGS shift + case 0x8B: //Send MARK tone + case 0x8C: //Send SPACE tone + case 0x8D: //Send MARK/SPACE tones + case 0x8E: //Received first character on line + case 0x8F: //Close PTT only (no tones) + + case 0xC9: //Huffman Off/On + case 0xCC: + case 0xD9: //Close PTT only (no tones) + + case SetTones: + + Used = 1; + break; + + case 0x91: // ???? + +// if (Len < 2) return; // Wait for more + + Used = 1; + break; + + default: + + // We didn't recognise command, so don't know how long it is - disaster! + + Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); + TNC->CmdLen = 0; + + return; + } + + if (Used == Len) + { + // All used - most likely case + + TNC->CmdLen = 0; + return; + } + + // Move Command Down buffer, and reenter + + TNC->CmdLen -= Used; + + memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); + + goto CmdLoop; + + +} + + +VOID HALDisconnected(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + CloseLogfile(0); + CloseLogfile(1); + CloseLogfile(2); + + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + UINT * buffptr; + + // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesQueued = 0; + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + // Connected, or Disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; +} + +BOOL HALConnected(struct TNCINFO * TNC, char * Call) +{ + char Msg[80]; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CallCopy[80]; + + strcpy(CallCopy, Call); + strcat(CallCopy, " "); // Some routines expect 10 char calls + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + // Stop Scanner + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + ShowTraffic(TNC); + + TNC->DataMode = RXDATA; + + OpenLogfile(0); + OpenLogfile(1); + OpenLogfile(2); + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->CurrentMode != Clover) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + + return TRUE; + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); + WriteLogLine(2, CTEXTMSG, CTEXTLEN); + + STREAM->BytesTXed += CTEXTLEN; + } + return TRUE; + } + + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, CallCopy, '+', 'O'); + + + return TRUE; +} + + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With DLE Encoding Encoding + + TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With Command Encoding (preceed each with 0x80 + + int i,txptr=0; + UCHAR * outbuff = TNC->TXBuffer; + + for (i=0; iTXLen = txptr; + WriteCommBlock(TNC); +} + +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + // Escape x80 and x81 with x81 + +// outbuff[0] = 0x80; +// outbuff[1] = 0x33; // Send data to modem + + for (i=0;iNeedPACTOR = 30; +} + + + + diff --git a/HALDriver64.c b/HALDriver64.c new file mode 100644 index 0000000..d093dba --- /dev/null +++ b/HALDriver64.c @@ -0,0 +1,1907 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define HAL 1 + +#define SetMYCALL 0x13 +#define ConnectEnable 0x52 +#define ConnectDisable 0x42 +#define SetEAS 0x59 // Echo as Sent +#define SetTones 0xec +#define ClearOnDisc 0x57 + +static char ClassName[]="HALSTATUS"; + +static char WindowTitle[] = "HAL"; +static int RigControlRow = 185; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +//int MaxStreams = 0; + +#ifndef LINBPQ +extern HFONT hFont; +#endif + +static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", + "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", + "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", + "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", + "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +static void CheckRX(struct TNCINFO * TNC); +VOID HALPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); +VOID ProcessHALCmd(struct TNCINFO * TNC); +VOID ProcessHALData(struct TNCINFO * TNC); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +BOOL HALConnected(struct TNCINFO * TNC, char * Call); +VOID HALDisconnected(struct TNCINFO * TNC); + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID COMClearDTR(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + + + +//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +//char BaseDir[]="c:"; + +static VOID CloseLogfile(int Flags) +{ +// CloseHandle(LogHandle[Flags]); +// LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + +static VOID OpenLogfile(int Flags) +{ +/* +UCHAR FN[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +*/ +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +// int cnt; +// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + TNC->WL2K = DecodeWL2KReportLine(buf); + continue; + } + if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) + { + TNC->XONXOFF = TRUE; + continue; + } + + if (_memicmp(buf, "TONES", 5) == 0) + { + int tone1 = 0, tone2 = 0; + + ptr = strtok(&buf[6], " ,/\t\n\r"); + if (ptr) + { + tone1 = atoi(ptr); + ptr = strtok(NULL, " ,/\t\n\r"); + if (ptr) + { + tone2 = atoi(ptr); + ptr = &TNC->InitScript[TNC->InitScriptLen]; + + // Try putting into FSK mode first + + *(ptr++) = 0x84; + *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) + *(ptr++) = tone1 >> 8; + *(ptr++) = tone1 & 0xff; + *(ptr++) = tone2 >> 8; + *(ptr++) = tone2 & 0xff; + + TNC->InitScriptLen += 6; + + continue; + } + } + goto BadLine; + } + if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) + { + + ptr = strtok(&buf[12], " ,\t\n\r"); + if (ptr) + { + if (_stricmp(ptr, "CLOVER") == 0) + TNC->DefaultMode = Clover; + else if (_stricmp(ptr, "PACTOR") == 0) + TNC->DefaultMode = Pactor; + else if (_stricmp(ptr, "AMTOR") == 0) + TNC->DefaultMode = AMTOR; + else goto BadLine; + + continue; + } + goto BadLine; + } + } + BadLine: + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + } + + return (TRUE); +} + +static size_t ExtProc(int fn, int port , PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL) + return 0; + + if (fn < 4 || fn > 5) + if (TNC->hDevice == 0) + return 0; // Port not open + + STREAM = &TNC->Streams[0]; + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = 0; + + return -1; + } + } + + CheckRX(TNC); + HALPoll(port); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 27; + memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + if (STREAM->FramesQueued > 4) + return (1 | TNC->HostMode << 8); + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "HAL Status

HAL Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * HALExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int len; + char Msg[80]; +#ifndef LINBPQ + HWND x; +#endif + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->Hardware = H_HAL; + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->MAXHOSTMODESESSIONS = 1; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + if (TNC->DefaultMode) + TNC->CurrentMode = TNC->DefaultMode; + else + TNC->CurrentMode = Clover; + + TNC->PollDelay = 999999999; + + // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL + + len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); + len++; // We include the NULL + + memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); + TNC->InitScriptLen += len; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_LEDS = zalloc(100); + strcpy(TNC->WEB_LEDS, " X X X X X X"); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); + + x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + TNC->ClientHeight = 233; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + SendCmd(TNC, "\x09" , 1); // Reset + + WritetoConsole("\n"); + + return ExtProc; +} + + + +static VOID KISSCLOSE(int Port) +{ + struct TNCINFO * conn = TNCInfo[Port]; + + // drop DTR and RTS + + COMClearDTR(conn->hDevice); + COMClearRTS(conn->hDevice); + + // purge any outstanding reads/writes and close device handle + + CloseCOMPort(conn->hDevice); + + return; +} + + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * Xptr; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // We need to konw whether data is received or echoed, so we can't split commands and data here. + // Pass everything to the Command Handler. It will check that there are enough bytes for the command, + // and wait for more if not. + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // If USB version, we might get unescaped xon and xoff, which we must ignore + + if (TNC->XONXOFF) + { + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + + while(Xptr) + { + Debugprintf("XON Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + + while(Xptr) + { + Debugprintf("XOFF Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape + + if (Xptr) + + // Make sure we have the escaped char as well + + if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char + return; + } + + ProcessHALBuffer(TNC, Length); + + TNC->RXLen = 0; + + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + return TRUE; +} + +VOID HALPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + UCHAR TXMsg[1000]; + int datalen; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + } + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (TNC->TNCOK) + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + SendCmd(TNC, "\x42", 1); // Connect Enable off + + return; + + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + + if (STREAM->Attached) + CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + + UCHAR TXMsg[80]; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + // Set Listen Mode + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x58", 1); // Listen + + break; + + case Clover: + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + SendCmd(TNC, "\x60\x09", 2); // Robust Retries + SendCmd(TNC, "\x61\x09", 2); // Normal Retries + + break; + } + + SendCmd(TNC, "\x52", 1); // ConnectEnable + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, Status); + + return; + } + } + +#define MAXHALTX 256 + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) + { + int datalen; + UINT * buffptr; + UCHAR * MsgPtr; + unsigned char TXMsg[500]; + + buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; + datalen=buffptr[1]; + MsgPtr = (UCHAR *)&buffptr[2]; + + if (STREAM->Connected) + { + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + STREAM->FramesQueued--; + return; + } + } + + // Must send data in small chunks - the Hal has limited buffer space + + // If in IRS force a turnround + + if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) + { + if (TNC->TimeInRX++ > 15) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + else + goto Poll; + } + + TNC->TimeInRX = 0; + + EncodeAndSend(TNC, MsgPtr, datalen); + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + WriteLogLine(2, MsgPtr, datalen); + + STREAM->BytesTXed += datalen; + STREAM->FramesQueued--; + + ShowTraffic(TNC); + + return; + } + else + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if (memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) + { + TNC->CurrentMode = Clover; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + MySetWindowText(TNC->xIDC_MODE, "Clover"); + strcpy(TNC->WEB_MODE, "Clover"); + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + + return; + } + + if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) + { + TNC->CurrentMode = Pactor; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x48", 1); // Listen Off + + return; + } + if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) + { + TNC->CurrentMode = AMTOR; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + + datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); + + // DOnt set connecting till we get the 19 response so we can trap listen as a fail + break; + + case Clover: + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); + + break; + } + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "CLOVER ", 7) == 0) + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", + STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + SendCmd(TNC, "\x07", 1); // Normal Disconnect + TNC->NeedPACTOR = 50; + + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? Treat as HEX string + + datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", + (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], + (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], + (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], + (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); + +// SendCmd(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + } + } + } +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 20) + return; + + TNC->PollDelay = 0; + + if (TNC->TNCOK) + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll + else + SendCmd(TNC, "\x09" , 1); // Reset + + TNC->Timeout = 100; + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + // TNC Has Restarted, send init commands (can probably send all at once) + +// TNC->TXBuffer[0] = 0x1b; +// TNC->TXLen = 1; + + WriteCommBlock(TNC); + + SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 20; // Need to set Calls and start scan + + TNC->DataMode = RXDATA; // Start with RX Data + + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll +// SendCmd(TNC, "\xc9" , 1); // Huffman Off + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries + +// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem + + TNC->Timeout = 50; + + return; + +} + +VOID ProcessHALData(struct TNCINFO * TNC) +{ + // Received Data just pass to Appl + + UINT * buffptr; + int Len = TNC->DataLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + TNC->DataLen = 0; + + if (TNC->DataMode == TXDATA) + { + STREAM->BytesAcked += Len; +// Debugprintf("Acked %d", Len); + + if (STREAM->BytesAcked > STREAM->BytesTXed) + Debugprintf("Too Much Acked"); + + if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) + { + // All sent + + if (STREAM->Disconnecting) + TidyClose(TNC, 0); + else + if (TNC->CurrentMode != Clover) + + // turn round link + + SendCmd(TNC, "\x0c" , 1); // Turnround + + } + } + else + { + if (TNC->DataMode == RXDATA) + { +// Debugprintf("RXed %d", Len); + buffptr = GetBuff(); + if (buffptr == NULL) + return; // No buffers, so ignore + + buffptr[1] = Len; // Length + + WriteLogLine(1, TNC->DataBuffer, Len); + + STREAM->BytesRXed += Len; + + memcpy(&buffptr[2], TNC->DataBuffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + + ShowTraffic(TNC); + + return; +} + + + +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) +{ + UCHAR Char; + UCHAR * inptr; + UCHAR * cmdptr; + UCHAR * dataptr; + BOOL CmdEsc, DataEsc; + + inptr = TNC->RXBuffer; + + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + CmdEsc = TNC->CmdEsc; + DataEsc = TNC->DataEsc; + + // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // Command Responses can be variable length + + // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives + + while(Length--) + { + Char = *(inptr++); + + if (CmdEsc) + { + CmdEsc = FALSE; + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape. We ensured above that data follows so we can process it inline + + Length--; + Char = *(inptr++) - 0x20; + } + *(cmdptr++) = Char; + } + else if (DataEsc) + { + DataEsc = FALSE; + goto DataChar; + } + else +NotData: + if (Char == 0x80) // Next Char is Command + CmdEsc = TRUE; + else if (Char == 0x81) // Next Char is escaped data (80 or 81) + DataEsc = TRUE; + else + { + // This is a Data Char. We must process any Commands received so far, so we know the type of data + + DataChar: + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + ProcessHALCmd(TNC); + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + + *(dataptr++) = Char; // Normal Data + + // Now process any other data chars + + while(Length--) + { + Char = *(inptr++); + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape within data. We ensured above that data follows so we + // can process it here + + Length--; + Char = *(inptr++) - 0x20; + } + + if (Char == 0x80 || Char == 0x81) + { + // Process any data we have, then loop back + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + ProcessHALData(TNC); + + goto NotData; + } + *(dataptr++) = Char; // Normal Data + } + + // Used all data + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + + ProcessHALData(TNC); + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + return; + } + } + + // Save State + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + if (TNC->DataLen) + ProcessHALData(TNC); + + if (TNC->CmdLen) + ProcessHALCmd(TNC); +} + +VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) +{ + MySetWindowText(TNC->xIDC_STATE, Msg); + strcpy(TNC->WEB_STATE, Msg); +} + +VOID ProcessHALCmd(struct TNCINFO * TNC) +{ + char * Call; + int Stream = 0; + int Opcode; + int StatusByte; + int Leds; + int Len; + int Used; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + +CmdLoop: + + Opcode = TNC->CmdBuffer[0]; + Len = TNC->CmdLen; + + if (Len == 0) + return; + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs + + switch(Opcode) + { + case 0x09: //Hardware Reset - equivalent to power on reset + + // Hardware has reset - need to reinitialise + + TNC->HostMode = 0; // Force Reinit + + Used = 1; + break; + + case 0x7a: // FSK Modes Status + + // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x06: // FSK TX (RTTY) + case 0x07: // FSK RX (RTTY) + case 0x10: // AMTOR STANDBY (LISTEN ON) + case 0x11: // AMTOR STANDBY (LISTEN OFF) + case 0x12: // AMTOR FEC TX (AMTOR) + case 0x13: // AMTOR FEC RX (AMTOR) + case 0x14: // P-MODE FEC TX (P-MODE) + case 0x15: // FREE SIGNAL TX (AMTOR) + case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) + + // Diaplay Linke Status + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + break; + + case 0x0C: // P-MODE STANDBY (LISTEN ON) + case 0x0D: // P-MODE STANDBY (LISTEN OFF) + + // if we were connecting, this means connect failed. + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + if (STREAM->Connecting) + HALDisconnected(TNC); + + break; + + case 0x0E: // ISS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"ISS"); + strcpy(TNC->WEB_TXRX, "ISS"); + TNC->TXRXState = 'S'; + break; + + case 0x0F: // IRS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"IRS"); + strcpy(TNC->WEB_TXRX, "IRS"); + TNC->TXRXState = 'R'; + break; + + case 0x00: // IDLE (AMTOR/P-MODE) + case 0x01: // TFC (AMTOR/P-MODE) + case 0x02: // RQ (AMTOR/P-MODE) + case 0x03: // ERR (AMTOR/P-MODE) + case 0x04: // PHS (AMTOR/P-MODE) + case 0x05: // OVER (AMTOR/P-MODE) (not implemented) + + MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + + +//$807A $8008 P-MODE100 (P-MODE) +//$807A $8009 P-MODE200 (P-MODE) +//$807A $800A HUFFMAN ON (P-MODE) +//$807A $800B HUFFMAN OFF (P-MODE) + ; + } + Used = 2; + break; + + + case 0x7d: // Get LED Status + + // We use Get LED Status as a Poll + + if (Len < 2) return; // Wait for more + + Leds = TNC->CmdBuffer[1]; + sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", + (Leds & 0x20)? 'X' : ' ', + (Leds & 0x10)? 'X' : ' ', + (Leds & 0x08)? 'X' : ' ', + (Leds & 0x04)? 'X' : ' ', + (Leds & 0x02)? 'X' : ' ', + (Leds & 0x01)? 'X' : ' '); + +// STBY CALL LINK ERROR TX RX + MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); + + Used = 2; + break; + + case 0x21: // Monitored FEC CCB + case 0x22: // Monitored ARQ CCB + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + UpdateMH(TNC, Call, '!', 0); + + break; + + case 0x27: // Clover ARQ LINK REQUEST status message + + //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). + + if (Len < 2) return; // Wait for more + + // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL + Used = 2; + break; + + case 0x2D: // FSK ARQ Link Request status message + + // $802D $8001 $8000 CLOVER Link Request (not implemented) + // $802D $8002 $8000 AMTOR CCIR-476 Link Request + // $802D $8003 $8000 AMTOR CCIR-625 Link Request + // $802D $8004 $8000 P-MODE Link Request + + if (Len < 3) return; // Wait for more + + // Don't need to do anything (but may save Session type later + + Used = 3; + break; + + + case 0x28: // Monitored Call + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls + + break; + + + case 0x20: // Clover Linked with - Call Connected + case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. + case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . + case 0x2B: // P-MODE link to + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + HALConnected(TNC, Call); + + break; + + case 0x23: // Normal Disconnected - followed by $8000 + case 0x24: // Link failed (any of the link errors) + case 0x25: // Signal Lost (LOS) + + if (Len < 2) return; // Wait for more + + HALDisconnected(TNC); + + Used = 2; + break; + + + // Stream Switch Reports - we will need to do something with these if Echo as Sent is set + // or we do something with the secondary port + + case 0x30: // Switch to Receive Data characters + case 0x31: // Switch to Transmit Data characters + case 0x32: // Switch to RX data from secondary port + + TNC->DataMode = Opcode; + Used = 1; + break; + + case 0x33: // Send TX data to modem + case 0x34: // Send TX data to secondary port + + TNC->TXMode = Opcode; + Used = 1; + break; + + case 0x70: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 9) return; // Wait for more + + Used = 9; + break; + + case 0x71: // SelCall On/Off + + if (Len < 2) return; // Wait for more + + Used = 2; + break; + + case 0x72: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 15) return; // Wait for more + + Used = 15; + break; + + case 0x73: // Clover Link state + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x00: mySetWindowText(TNC, "Channel idle"); break; + case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; + case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; + case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; + case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; + case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; + case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; + case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; + case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; + case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; + case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; + case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; + case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; + case 0x8E: mySetWindowText(TNC, "TX idle"); break; + case 0x8F: mySetWindowText(TNC, "RX idle"); break; + case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; + case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; + case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; + case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; + } + + Used = 2; + break; + + case 0x75: // Clover waveform format + + if (Len < 5) return; // Wait for more + + Used = 5; + break; + + case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy + // $807F $80xx $8030 Invalid or unimplemented command code + // $807F $80xx $8031 Invalid parameter value + // $807F $80xx $8032 Not allowed when connected + // $807F $80xx $8033 Not allowed when disconnected + // $807F $80xx $8034 Not valid in this mode + // $807F $80xx $8035 Not valid in this code + // $807F $8096 $8036 EEPROM write error + + if (Len < 3) return; // Wait for more + + if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) + { + // Reject of XON/XOFF enable + +// TNC->XONXOFF = FALSE; +// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); + } + else + Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); + + Used = 3; + break; + + // Following are all immediate commands - response is echo of command + + case 0x6f: // XON/XOFF on + +// TNC->XONXOFF = TRUE; // And drop through +// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); + + case 0x19: // Call P-MODE to + case 0x10: // Robust Link to using MYCALL + case 0x11: // Normal Link to using MYCALL + + STREAM->Connecting = TRUE; + + case 0x00: // P Load LOD file + case 0x01: // P Load S28 file + case 0x02: //Check Unit Error Status + case 0x03: //F Check System Clock + case 0x04: //C Close PTT and transmit Clover waveform + case 0x05: //Open PTT and stop transmit test + case 0x06: //Immediate Abort (Panic Kill) + case 0x07: //Normal disconnect (wait for ACK) + case 0x08: //Software reset - restore all program defaults + case 0x0A: //Send CW ID + case 0x0B: //Close PTT and transmit Single Tone + case 0x0C: //F Normal OVER (AMTOR,P-MODE) + case 0x0D: //F Force RTTY TX (Baudot/ASCII) + case 0x0E: //F Go to RTTY RX (Baudot/ASCII) + case 0x0F: //Go to LOD/S28 file loader + case SetMYCALL: // Set MYCALL Response + + case 0x1E: // Set MYALTCALL Response + + case 0x41: + case 0x42: + case 0x46: + case 0x47: + case 0x48: + case 0x4d: + case 0x52: // Enable adaptive Clover format + case 0x54: // Enable adaptive Clover format + + case 0x56: // Expanded Link State Reports OFF/ON + case 0x57: // Clear buffers on disc + case 0x58: + case 0x59: + case 0x60: // Robust Mode Retries + case 0x61: // Normal Mode Retries + case 0x80: //Switch to CLOVER mode + case 0x81: //Select AMTOR Standby + case 0x82: //Select AMTOR FEC + case 0x83: //Select P-MODE Standby + case 0x84: //Switch to FSK modes + case 0x85: //Select Baudot + case 0x86: //Select ASCII + case 0x87: //Forced OVER (AMTOR, P-MODE) + case 0x88: //Forced END (AMTOR, P-MODE) + case 0x89: //Force LTRS shift + case 0x8A: //Force FIGS shift + case 0x8B: //Send MARK tone + case 0x8C: //Send SPACE tone + case 0x8D: //Send MARK/SPACE tones + case 0x8E: //Received first character on line + case 0x8F: //Close PTT only (no tones) + + case 0xC9: //Huffman Off/On + case 0xCC: + case 0xD9: //Close PTT only (no tones) + + case SetTones: + + Used = 1; + break; + + case 0x91: // ???? + +// if (Len < 2) return; // Wait for more + + Used = 1; + break; + + default: + + // We didn't recognise command, so don't know how long it is - disaster! + + Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); + TNC->CmdLen = 0; + + return; + } + + if (Used == Len) + { + // All used - most likely case + + TNC->CmdLen = 0; + return; + } + + // Move Command Down buffer, and reenter + + TNC->CmdLen -= Used; + + memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); + + goto CmdLoop; + + +} + + +VOID HALDisconnected(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + CloseLogfile(0); + CloseLogfile(1); + CloseLogfile(2); + + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + UINT * buffptr; + + // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesQueued = 0; + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + // Connected, or Disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; +} + +BOOL HALConnected(struct TNCINFO * TNC, char * Call) +{ + char Msg[80]; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CallCopy[80]; + + strcpy(CallCopy, Call); + strcat(CallCopy, " "); // Some routines expect 10 char calls + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + // Stop Scanner + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + ShowTraffic(TNC); + + TNC->DataMode = RXDATA; + + OpenLogfile(0); + OpenLogfile(1); + OpenLogfile(2); + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->CurrentMode != Clover) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + + return TRUE; + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); + WriteLogLine(2, CTEXTMSG, CTEXTLEN); + + STREAM->BytesTXed += CTEXTLEN; + } + return TRUE; + } + + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, CallCopy, '+', 'O'); + + + return TRUE; +} + + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With DLE Encoding Encoding + + TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With Command Encoding (preceed each with 0x80 + + int i,txptr=0; + UCHAR * outbuff = TNC->TXBuffer; + + for (i=0; iTXLen = txptr; + WriteCommBlock(TNC); +} + +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + // Escape x80 and x81 with x81 + +// outbuff[0] = 0x80; +// outbuff[1] = 0x33; // Send data to modem + + for (i=0;iNeedPACTOR = 30; +} + + + + diff --git a/HFCommon.c b/HFCommon.c new file mode 100644 index 0000000..6971d83 --- /dev/null +++ b/HFCommon.c @@ -0,0 +1,2145 @@ +/* +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 +*/ + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "kernelresource.h" +#include "CHeaders.h" +#include "tncinfo.h" +#ifndef LINBPQ +#include +#endif +//#include +#include "bpq32.h" +#include "adif.h" + +extern char * PortConfig[33]; + +HANDLE hInstance; +extern HBRUSH bgBrush; +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + +extern int Ver[]; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +char * GetChallengeResponse(char * Call, char * ChallengeString); + +VOID __cdecl Debugprintf(const char * format, ...); +VOID FromLOC(char * Locator, double * pLat, double * pLon); +BOOL ToLOC(double Lat, double Lon , char * Locator); + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon); + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +int Winmor_Socket_Data(int sock, int error, int eventcode); + +struct WL2KInfo * WL2KReports; + +int WL2KTimer = 0; + +int ModetoBaud[31] = {0,0,0,0,0,0,0,0,0,0,0, // 0 = 10 + 200,600,3200,600,3200,3200, // 11 - 16 + 0,0,0,0,0,0,0,0,0,0,0,0,0,600}; // 17 - 30 + +extern char HFCTEXT[]; +extern int HFCTEXTLEN; + + +extern char WL2KCall[10]; +extern char WL2KLoc[7]; + + +VOID MoveWindows(struct TNCINFO * TNC) +{ +#ifndef LINBPQ + RECT rcClient; + int ClientHeight, ClientWidth; + + GetClientRect(TNC->hDlg, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (TNC->hMonitor) + MoveWindow(TNC->hMonitor,2 , TNC->RigControlRow + 3, ClientWidth-4, ClientHeight - (TNC->RigControlRow + 3), TRUE); +#endif +} + +char * Config; +static char * ptr1, * ptr2; + +#ifndef LINBPQ + +LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + +// hdc = BeginPaint (hWnd, &ps); + +// SelectObject( hdc, hFont) ; + +// EndPaint (hWnd, &ps); +// +// wParam = hdc; + + break; + + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + TNC->DontRestart = TRUE; + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_HSCROLL: + { + DWORD dwPos; // current position of slider + char value[16]; + + switch (LOWORD(wParam)) + { + case TB_ENDTRACK: + case TB_THUMBTRACK: + + TNC->TXOffset = SendMessage(TNC->xIDC_TXTUNE, TBM_GETPOS, 0, 0); + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + + break; + } + + default: + break; + } + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + +BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, int Width, int Height, VOID ForcedCloseProc()) +{ +#ifdef LINBPQ + return FALSE; +#else + WNDCLASS wc; + char Title[80]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Key[80]; + char Size[80]; + int Top, Left; + HANDLE hDlg = 0; + static int LP = 1235; + + if (TNC->hDlg) + { + ShowWindow(TNC->hDlg, SW_SHOWNORMAL); + SetForegroundWindow(TNC->hDlg); + return FALSE; // Already open + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + +// if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_TELNET ||TNC->Hardware == H_ARDOP || +// TNC->Hardware == H_V4 || TNC->Hardware == H_FLDIGI || TNC->Hardware == H_UIARQ || TNC->Hardware == H_VARA) + if (TNC->PortRecord) + sprintf(Title, "%s Status - Port %d %s", WindowTitle, TNC->Port, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + else + sprintf(Title, "Rigcontrol"); + + if (TNC->Hardware == H_MPSK) + sprintf(Title, "Rigcontrol for MultiPSK Port %d", TNC->Port); + + TNC->hDlg = hDlg = CreateMDIWindow(ClassName, Title, 0, + 0, 0, Width, Height, ClientWnd, hInstance, ++LP); + + // CreateDialog(hInstance,ClassName,0,NULL); + + Rect.top = 100; + Rect.left = 20; + Rect.right = Width + 20; + Rect.bottom = Height + 100; + + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + { + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &TNC->Minimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + } + + if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_ARDOP|| TNC->Hardware == H_VARA) + retCode = RegQueryValueEx(hKey,"TNC->RestartAfterFailure",0, + (ULONG *)&Type,(UCHAR *)&TNC->RestartAfterFailure,(ULONG *)&Vallen); + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + +// GetWindowRect(hDlg, &Rect); // Get the real size + + MoveWindow(hDlg, Left - (OffsetW /2), Top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE); + + if (TNC->Minimized) + ShowWindow(hDlg, SW_SHOWMINIMIZED); + else + ShowWindow(hDlg, SW_RESTORE); + + TNC->RigControlRow = RigControlRow; + + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + + TNC->ForcedCloseProc = ForcedCloseProc; + + return TRUE; +#endif +} + + +// WL2K Reporting Code. + +static SOCKADDR_IN sinx; + + +VOID SendReporttoWL2KThread(void * unused); +VOID SendHTTPReporttoWL2KThread(void * unused); + +VOID CheckWL2KReportTimer() +{ + if (WL2KReports == NULL) + return; // Shouldn't happen! + + WL2KTimer--; + + if (WL2KTimer != 0) + return; + +#ifdef WIN32 + WL2KTimer = 2 * 32910; // Every 2 Hours - PC Tick is a bit slow +#else + WL2KTimer = 2 * 36000; // Every 2 Hours +#endif + + if (CheckAppl(NULL, "RMS ") == NULL) + if (CheckAppl(NULL, "RELAY ") == NULL) + return; + + _beginthread(SendHTTPReporttoWL2KThread, 0, 0); + + return; +} + +static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" + "Accept: application/json\r\n" +// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n" + "Content-Type: application/json\r\n" + "Host: %s:%d\r\n" + "Content-Length: %d\r\n" + //r\nUser-Agent: BPQ32(G8BPQ)\r\n" +// "Expect: 100-continue\r\n" + "\r\n{%s}"; + +char Missing[] = "** Missing **"; + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value) +{ + char * ptr1, * ptr2; + + strcpy(Value, Missing); + + ptr1 = strstr(_REPLYBUFFER, Name); + + if (ptr1 == 0) + return; + + ptr1 += (strlen(Name) + 1); + + ptr2 = strchr(ptr1, '"'); + + if (ptr2) + { + size_t ValLen = ptr2 - ptr1; + memcpy(Value, ptr1, ValLen); + Value[ValLen] = 0; + } + + return; +} + + +// Send Winlink Session Record + +extern char LOC[7]; +extern char TextVerstring[50]; + +double Distance(double laa, double loa, double lah, double loh, BOOL KM); +double Bearing(double lat2, double lon2, double lat1, double lon1); +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); +SOCKET OpenWL2KHTTPSock(); + + + +struct WL2KMode +{ + int Mode; + char * WL2KString; + char * ADIFString; + char * BPQString; +}; + +struct WL2KMode WL2KModeList[] = +{ + {0,"Packet 1200"}, + {1,"Packet 2400"}, + {2, "Packet 4800"}, + {3, "Packet 9600"}, + {4, "Packet 19200"}, + {5, "Packet 38400"}, + {11, "Pactor 1"}, + {12, "Pactor 1,2"}, + {13, "Pactor 1,2,3"}, + {14, "Pactor 2"}, + {15, "Pactor 2,3"}, + {16, "Pactor 3"}, + {17, "Pactor 1,2,3,4"}, + {18, "Pactor 2,3,4"}, + {19, "Pactor 3,4"}, + {20, "Pactor 4"}, + {21, "WINMOR 500"}, + {22, "WINMOR 1600"}, + {30, "Robust Packet"}, + {40, "ARDOP 200"}, + {41, "ARDOP 500"}, + {42, "ARDOP 1000"}, + {43, "ARDOP 2000"}, + {44, "ARDOP 2000 FM"}, + {50, "VARA"}, + {51, "VARA FM"}, + {52, "VARA FM WIDE"}, + {53, "VARA 500"} +}; + +char WL2KModes [55][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "Pactor 1", "Pactor", "Pactor", "Pactor 2", "Pactor", "Pactor 3", "Pactor", "Pactor", "Pactor", "Pactor 4", // 11 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500", "VARA 2750"}; + + +VOID SendWL2KSessionRecordThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/session/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybridThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/radioNetwork/params/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC) +{ + char Message[512]; + char Date[80] ; + int Len; + struct TCPINFO * TCP = TNC->TCPInfo; + time_t T; + struct tm * tm; + char Call[10]; + + if (TCP == NULL || TCP->GatewayLoc[0] == 0) + return; + + strcpy(Call, TCP->GatewayCall); + strlop(Call, '-'); + + T = time(NULL); + tm = gmtime(&T); + + //2021-10-31-14=35=29 + + sprintf(Date, "%04d-%02d-%02d-%02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + +// "Callsign":"String","Password":"String","Param":"String","Value":"String","Key":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"RMSRelayVersion\",\"Value\":\"%s|%s|*HARMNNNN|%s|%s|\"", + Call, TCP->SecureCMSPassword, Date, "3.1.11.2", + TCP->HybridServiceCode, TCP->GatewayLoc); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"CoLocatedRMS\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridCoLocatedRMS); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"AllowFreq\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridFrequencies); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + return; +} + +BOOL NoSessionAccount = FALSE; +BOOL SessionAccountChecked = FALSE; + +BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived) +{ +/* +The API is /session/add https://api.winlink.org/json/metadata?op=SessionAdd + +The important parameters are (others can be omitted): + +Application (gateway program name) +Server (gateway callsign) +ServerGrid +Client (client callsign) +ClientGrid +Mode (Pactor, winmor, vara, etc) +Frequency +MessagesSent +MessagesReceived +BytesSent +BytesReceived +HoldingSeconds (duration of connection) +IdTag (random alphanumeric, 12 chars) + +"Application":"RMS Trimode", +"Version":"1.3.25.0", +"Cms":"CMS-A", +"Server":"AB4NX", +"ServerGrid":"EM73WT", +"Client":"VE2SCA","ClientGrid":"", +"Sid":"","Mode":"WINMOR16", +"Frequency":10145000, +"Kilometers":0, +"Degrees":0, +"LastCommand":">", +"MessagesSent":0, +"MessagesReceived":0, +"BytesSent":179, +"BytesReceived":0, +"HoldingSeconds":126, +"IdTag":"ATK9S3QGL2E1"} +*/ + time_t T; + + char Message[4096] = ""; + char * MessagePtr; + int MessageLen; + int Dist = 0; + int intBearing = 0; + + double Lat, Lon; + double myLat, myLon; + + char Tag[32]; + + SOCKET sock; + char Response[1024]; + int Len; + + // Only report if the CMSCall has a WL2KAccount + + if (NoSessionAccount) + return TRUE; + + if (!SessionAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SessionAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", ADIF->CMSCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "false")) + { + WritetoConsole("WL2K Traffic Reporting disabled - Gateway "); + WritetoConsole(ADIF->CMSCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + Debugprintf("WL2K Traffic Reporting disabled - Gateway %s does not have a Winlink Account", ADIF->CMSCall); + NoSessionAccount = TRUE; + return TRUE; + } + } + } + + if (ADIF == NULL || ADIF->LOC[0] == 0) + return TRUE; + + if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0) + return TRUE; + + T = time(NULL); + + // Extract Info we need + + // Distance and Bearing + + if (LOC[0] && ADIF->LOC[0]) + { + FromLOC(LOC, &myLat, &myLon); + FromLOC(ADIF->LOC, &Lat, &Lon); + + Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE); + intBearing = (int)Bearing(Lat, Lon, myLat, myLon); + } + + MessageLen = sprintf(Message, "\"Application\":\"%s\",", "BPQ32"); + MessageLen += sprintf(&Message[MessageLen], "\"Version\":\"%s\",", TextVerstring); + MessageLen += sprintf(&Message[MessageLen], "\"Cms\":\"%s\",", "CMS"); + MessageLen += sprintf(&Message[MessageLen], "\"Server\":\"%s\",", ADIF->CMSCall); + MessageLen += sprintf(&Message[MessageLen], "\"ServerGrid\":\"%s\",", LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Client\":\"%s\",", ADIF->Call); + MessageLen += sprintf(&Message[MessageLen], "\"ClientGrid\":\"%s\",", ADIF->LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Sid\":\"%s\",", ADIF->UserSID); + MessageLen += sprintf(&Message[MessageLen], "\"Mode\":\"%s\",", WL2KModes[ADIF->Mode]); + MessageLen += sprintf(&Message[MessageLen], "\"Frequency\":%lld,", ADIF->Freq); + MessageLen += sprintf(&Message[MessageLen], "\"Kilometers\":%d,", Dist); + MessageLen += sprintf(&Message[MessageLen], "\"Degrees\":%d,", intBearing); + MessageLen += sprintf(&Message[MessageLen], "\"LastCommand\":\"%s\",", ADIF->Termination); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesSent\":%d,", ADIF->Sent); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesReceived\":%d,", ADIF->Received); + MessageLen += sprintf(&Message[MessageLen], "\"BytesSent\":%d,", BytesSent); + MessageLen += sprintf(&Message[MessageLen], "\"BytesReceived\":%d,", BytesReceived); + MessageLen += sprintf(&Message[MessageLen], "\"HoldingSeconds\":%d,", (int)(T - ADIF->StartTime)); + sprintf(Tag, "%012X", (int)T * (rand() + 1)); + MessageLen += sprintf(&Message[MessageLen], "\"IdTag\":\"%s\"", Tag); + + MessagePtr = _strdup(Message); + _beginthread(SendWL2KSessionRecordThread, 0, (void *)MessagePtr); + + return TRUE; +} + +char APIKey[] = ",\"Key\":\"0D0C7AD6B38C45A7A9534E67111C38A7\""; + + +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return) +{ + int InputLen = 0; + int inptr = 0; + char Buffer[2048]; + char Header[2048]; + char * ptr, * ptr1; + int Sent; + + strcat(Params, APIKey); + Len += (int)strlen(APIKey); + + sprintf(Header, HeaderTemplate, Request, "api.winlink.org", 80, Len + 2, Params); + Sent = send(sock, Header, (int)strlen(Header), 0); + + if (Sent == -1) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update send()", Err); + return; + } + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 2048 - inptr, 0); + + if (InputLen == -1 || InputLen == 0) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update recv()", Err); + return; + } + + // As we are using a persistant connection, can't look for close. Check + // for complete message + + inptr += InputLen; + + Buffer[inptr] = 0; + + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + // got header + + int Hddrlen = (int)(ptr - Buffer); + + ptr1 = strstr(Buffer, "Content-Length:"); + + if (ptr1) + { + // Have content length + + int ContentLen = atoi(ptr1 + 16); + + if (ContentLen + Hddrlen + 4 == inptr) + { + // got whole response + + if (strstr(Buffer, " 200 OK")) + { + if (Return) + { + memcpy(Return, ptr + 4, ContentLen); + Return[ContentLen] = 0; + } + else + Debugprintf("WL2K Database update ok"); + + } + else + { + strlop(Buffer, 13); + Debugprintf("WL2K Update Params - %s", Params); + Debugprintf("WL2K Update failed - %s", Buffer); + } + return; + } + } + else + { + ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); + + if (ptr1) + { + // Just accept anything until I've sorted things with Lee + Debugprintf("%s", ptr1); + Debugprintf("WL2K Database update ok"); + return; + } + } + } + } +} + +BOOL WL2KAccountChecked = FALSE; +BOOL NoWL2KAccount = FALSE; + +VOID SendHTTPReporttoWL2KThread(void * unused) +{ + // Uses HTTP/JSON Interface + + struct WL2KInfo * WL2KReport = WL2KReports; + char * LastHost = NULL; + char * LastRMSCall = NULL; + char Message[512]; + int LastSocket = 0; + SOCKET sock = 0; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + int Len; + + // Send all reports in list + + char Response[1024]; + + // Only report if the CMSCall has a WL2KAccount + + if (NoWL2KAccount) + return; + + if (!WL2KAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + WL2KAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", + WL2KReport->BaseCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "false")) + { + WritetoConsole("WL2K Reporting disabled - Gateway "); + WritetoConsole(WL2KReport->BaseCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + NoWL2KAccount = TRUE; + return; + } + } + } + + while (WL2KReport) + { + // Resolve Name if needed + + if (LastHost && strcmp(LastHost, WL2KReport->Host) == 0) // Same host? + goto SameHost; + + // New Host - Connect to it + + LastHost = WL2KReport->Host; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(WL2KReport->Host); + destaddr.sin_port = htons(WL2KReport->WL2KPort); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + Debugprintf("Resolving %s", WL2KReport->Host); + HostEnt = gethostbyname (WL2KReport->Host); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", WL2KReport->Host, err, err); + return; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + // Allocate a Socket entry + + if (sock) + closesocket(sock); + + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + return; + +// ioctlsocket(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt, 4); + + destaddr.sin_family = AF_INET; + + if (sock == INVALID_SOCKET) + { + sock = 0; + return; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + // Connect to Host + + if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + + // + // Connect failed + // + + Debugprintf("Connect Failed"); + closesocket(sock); + sock = 0; + break; + } + + SameHost: + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"BaseCallsign\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"Frequency\":%lld," + "\"Mode\":%d," + "\"Baud\":%d," + "\"Power\":%d," + "\"Height\":%d," + "\"Gain\":%d," + "\"Direction\":%d," + "\"Hours\":\"%s\"," + "\"ServiceCode\":\"%s\"", + + WL2KReport->RMSCall, WL2KReport->BaseCall, WL2KReport->GridSquare, + WL2KReport->Freq, WL2KReport->mode, WL2KReport->baud, WL2KReport->power, + WL2KReport->height, WL2KReport->gain, WL2KReport->direction, + WL2KReport->Times, WL2KReport->ServiceCode); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/channel/add", Message, Len, NULL); + + + // Send Version Message + + + if (LastRMSCall == NULL || strcmp(WL2KReport->RMSCall, LastRMSCall) != 0) + { + int Len; + + LastRMSCall = WL2KReport->RMSCall; + + // "Callsign":"String","Program":"String","Version":"String","Comments":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Program\":\"BPQ32\"," + "\"Version\":\"%d.%d.%d.%d\",\"Comments\":\"Test Comment\"", + WL2KReport->RMSCall, Ver[0], Ver[1], Ver[2], Ver[3]); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/version/add", Message, Len, NULL); + } + + WL2KReport = WL2KReport->Next; + } + + Sleep(100); + closesocket(sock); + sock = 0; + +} + +struct WL2KInfo * DecodeWL2KReportLine(char * buf) +{ + //06'', '', '', , , , , + // , , , '', , '' + + // WL2KREPORT service, api.winlink.org, 80, GM8BPQ, IO68VL, 00-23, 144800000, PKT1200, 10, 20, 5, 0, BPQTEST + + char * Context; + char * p_cmd; + char * param; + char errbuf[256]; + struct WL2KInfo * WL2KReport = zalloc(sizeof(struct WL2KInfo)); + char * ptr; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + + memset(Param, 0, 2048); + + strcpy(errbuf, buf); + + p_cmd = strtok_s(&buf[10], ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + strcpy(WL2KReport->ServiceCode, p_cmd); + + // Can default Host and Port, so cant use strtok for them + + ptr1 = Context; + + while (ptr1 && *ptr1 && n < 2) + { + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + + } + + if (n < 2) + goto BadLine; + + if (Param[1][0] == 0) + WL2KReport->WL2KPort = 80; // HTTP Interface + else + WL2KReport->WL2KPort = atoi(&Param[1][0]); + + if (Param[0][0] == 0) + WL2KReport->Host = _strdup("api.winlink.org"); + else + { + _strlwr(&Param[0][0]); + + if (strstr(&Param[0][0], "winlink.org")) + { + WL2KReport->WL2KPort = 80; // HTTP Interface + WL2KReport->Host = _strdup("api.winlink.org"); + } + else + WL2KReport->Host = _strdup(&Param[0][0]); + } + + Context = ptr1; + + p_cmd = strtok_s(NULL, ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + if (WL2KReport->WL2KPort == 0) goto BadLine; + + strcpy(WL2KReport->RMSCall, p_cmd); + strcpy(WL2KReport->BaseCall, p_cmd); + strlop(WL2KReport->BaseCall, '-'); // Remove any SSID + + strcpy(WL2KCall, WL2KReport->BaseCall); // For SYSOP Update + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) != 6) goto BadLine; + + strcpy(WL2KReport->GridSquare, p_cmd); + strcpy(WL2KLoc, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) > 79) goto BadLine; + + // Convert any : in times to comma + + ptr = strchr(p_cmd, ':'); + + while (ptr) + { + *ptr = ','; + ptr = strchr(p_cmd, ':'); + } + + strcpy(WL2KReport->Times, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + WL2KReport->Freq = strtoll(p_cmd, NULL, 10); + + if (WL2KReport->Freq == 0) // Invalid + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Mode Designator - one of + + // PKTnnnnnn + // WINMOR500 + // WINMOR1600 + // ROBUST + // P1 P12 P123 P1234 etc + + if (memcmp(param, "PKT", 3) == 0) + { + int Speed, Mode; + + Speed = atoi(¶m[3]); + + WL2KReport->baud = Speed; + + if (Speed <= 1200) + Mode = 0; // 1200 + else if (Speed <= 2400) + Mode = 1; // 2400 + else if (Speed <= 4800) + Mode = 2; // 4800 + else if (Speed <= 9600) + Mode = 3; // 9600 + else if (Speed <= 19200) + Mode = 4; // 19200 + else if (Speed <= 38400) + Mode = 5; // 38400 + else + Mode = 6; // >38400 + + WL2KReport->mode = Mode; + } + else if (_stricmp(param, "WINMOR500") == 0) + WL2KReport->mode = 21; + else if (_stricmp(param, "WINMOR1600") == 0) + WL2KReport->mode = 22; + else if (_stricmp(param, "ROBUST") == 0) + { + WL2KReport->mode = 30; + WL2KReport->baud = 600; + } + else if (_stricmp(param, "ARDOP200") == 0) + WL2KReport->mode = 40; + else if (_stricmp(param, "ARDOP500") == 0) + WL2KReport->mode = 41; + else if (_stricmp(param, "ARDOP1000") == 0) + WL2KReport->mode = 42; + else if (_stricmp(param, "ARDOP2000") == 0) + WL2KReport->mode = 43; + else if (_stricmp(param, "ARDOP2000FM") == 0) + WL2KReport->mode = 44; + else if (_stricmp(param, "P1") == 0) + WL2KReport->mode = 11; + else if (_stricmp(param, "P12") == 0) + WL2KReport->mode = 12; + else if (_stricmp(param, "P123") == 0) + WL2KReport->mode = 13; + else if (_stricmp(param, "P2") == 0) + WL2KReport->mode = 14; + else if (_stricmp(param, "P23") == 0) + WL2KReport->mode = 15; + else if (_stricmp(param, "P3") == 0) + WL2KReport->mode = 16; + else if (_stricmp(param, "P1234") == 0) + WL2KReport->mode = 17; + else if (_stricmp(param, "P234") == 0) + WL2KReport->mode = 18; + else if (_stricmp(param, "P34") == 0) + WL2KReport->mode = 19; + else if (_stricmp(param, "P4") == 0) + WL2KReport->mode = 20; + else if (_stricmp(param, "VARA") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARA2300") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARAFM") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM12") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM96") == 0) + WL2KReport->mode = 52; + else if (_stricmp(param, "VARA500") == 0) + WL2KReport->mode = 53; + else if (_stricmp(param, "VARA2750") == 0) + WL2KReport->mode = 54; + else + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Optional Params + + WL2KReport->power = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->height = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->gain = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->direction = (param)? atoi(param) : 0; + + WL2KTimer = 60; + + WL2KReport->Next = WL2KReports; + WL2KReports = WL2KReport; + + return WL2KReport; + +BadLine: + + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + + return 0; +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis); + +VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, TRUE); +} +VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, LOC, Report, FALSE); +} + +VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, FALSE); +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis) +{ + PMHSTRUC MH = TNC->PortRecord->PORTCONTROL.PORTMHEARD; + PMHSTRUC MHBASE = MH; + UCHAR AXCall[72] = ""; + int i; + char * LOC, * LOCEND; + char ReportMode[20]; + char NoLOC[7] = ""; + double Freq; + char ReportFreq[350] = ""; + int OldCount = 0; + char ReportCall[16]; + + + if (MH == 0) return; + + if (Digis) + { + // Call is an ax.25 digi string not a text call + + memcpy(AXCall, Call, 7 * 9); + ReportCall[ConvFromAX25(Call, ReportCall)] = 0; + + // if this is a UI frame with a locator or APRS position + // we could derive a position from it + + } + else + { + strcpy(ReportCall, Call); + ConvToAX25(Call, AXCall); + AXCall[6] |= 1; // Set End of address + } + + // Adjust freq to centre + +// if (Mode != ' ' && TNC->RIG->Valchar[0]) + if (TNC->RIG->Valchar[0]) + { + if (TNC->Hardware == H_UZ7HO) + { + // See if we have Center Freq Info + if (TNC->AGWInfo->CenterFreq) + { + Freq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); + } +#ifdef WIN32 + else if (TNC->AGWInfo->hFreq) + { + char Centre[16]; + double ModemFreq; + + SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); + + ModemFreq = atof(Centre); + + Freq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); + } +#endif + else + Freq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 + } + else + + // Not UZ7HO or Linux + + Freq = atof(TNC->RIG->Valchar) + 0.0015; + + _gcvt(Freq, 9, ReportFreq); + } + + if (TNC->Hardware == H_ARDOP) + { + LOC = memchr(Call, '[', 20); + + if (LOC) + { + LOCEND = memchr(Call, ']', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + goto NOLOC; + } + } + } + + else if (TNC->Hardware != H_WINMOR) // Only WINMOR has a locator + { + LOC = NoLOC; + goto NOLOC; + } + + + LOC = memchr(Call, '(', 20); + + if (LOC) + { + LOCEND = memchr(Call, ')', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + } + } + else + LOC = NoLOC; + +NOLOC: + + if (Loc) + LOC = Loc; // Supplied Locator overrides + + for (i = 0; i < MHENTRIES; i++) + { + if (Mode == ' ' || Mode == '*') // Packet + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && MH->MHDIGI == Mode)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + else + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && + MH->MHDIGI == Mode && strcmp(MH->MHFreq, ReportFreq) == 0)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + +// memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); + memcpy (MHBASE->MHCALL, AXCall, 7 * 9); // Save Digis + MHBASE->MHDIGI = Mode; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + + memcpy(MHBASE->MHLocator, LOC, 6); + strcpy(MHBASE->MHFreq, ReportFreq); + + // Report to NodeMap + + if (Report == FALSE) + return; + + if (Mode == '*') + return; // Digi'ed Packet + + if (Mode == ' ') // Packet Data + { + if (TNC->PktUpdateMap == 1) + Mode = '!'; + else + return; + } + + ReportMode[0] = TNC->Hardware + '@'; + ReportMode[1] = Mode; + if (TNC->Hardware == H_HAL) + ReportMode[2] = TNC->CurrentMode; + else + ReportMode[2] = (TNC->RIG->CurrentBandWidth) ? TNC->RIG->CurrentBandWidth : '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + if (LOC[0] == 0) + { + double Lat, Lon; + + if (GetPosnFromAPRS(ReportCall, &Lat, &Lon) && Lat != 0.0) + { + ToLOC(Lat, Lon, LOC); + } + } + + SendMH(TNC, ReportCall, ReportFreq, LOC, ReportMode); + + return; +} + +VOID CloseDriverWindow(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + + TNC = TNCInfo[port]; + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + PostMessage(TNC->hDlg, WM_CLOSE,0,0); +// DestroyWindow(TNC->hDlg); + + TNC->hDlg = NULL; +#endif + return; +} + +VOID SaveWindowPos(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + char Key[80]; + + TNC = TNCInfo[port]; + + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + sprintf(Key, "PACTOR\\PORT%d", port); + + SaveMDIWindowPos(TNC->hDlg, Key, "Size", TNC->Minimized); + +#endif + return; +} + +VOID ShowTraffic(struct TNCINFO * TNC) +{ + char Status[80]; + + sprintf(Status, "RX %d TX %d ACKED %d ", + TNC->Streams[0].BytesRXed, TNC->Streams[0].BytesTXed, TNC->Streams[0].BytesAcked); +#ifndef LINBPQ + SetDlgItemText(TNC->hDlg, IDC_TRAFFIC, Status); +#endif +} + +BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC) +{ + // See if this port, or any interlocked ports are reporting channel busy + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (ThisTNC->Busy) + return TRUE; // Our port is busy + + if (rxInterlock == 0 && txInterlock == 0) + return ThisTNC->Busy; // No Interlock + + for (i=1; i<33; i++) + { + TNC = TNCInfo[i]; + + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->Busy) + return TRUE; // Interlocked port is busy + + } + return FALSE; // None Busy +} + +char ChallengeResponse[13]; + +char * GetChallengeResponse(char * Call, char * ChallengeString) +{ + // Generates a response to the CMS challenge string... + + long long Challenge = _atoi64(ChallengeString); + long long CallSum = 0; + long long Mask; + long long Response; + long long XX = 1065484730; + + char CallCopy[10]; + UINT i; + + + if (Challenge == 0) + return "000000000000"; + +// Calculate Mask from Callsign + + memcpy(CallCopy, Call, 10); + strlop(CallCopy, '-'); + strlop(CallCopy, ' '); + + for (i = 0; i < strlen(CallCopy); i++) + { + CallSum += CallCopy[i]; + } + + Mask = CallSum + CallSum * 4963 + CallSum * 782386; + + Response = (Challenge % 930249781); + Response ^= Mask; + + sprintf(ChallengeResponse, "%012lld", Response); + + return ChallengeResponse; +} + +SOCKET OpenWL2KHTTPSock() +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(80); + + // Resolve name to address + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + return sock; +} + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER) +{ + SOCKET sock = 0; + int Len; + char Message[1000]; + + sock = OpenWL2KHTTPSock(); + + if (sock == 0) + return 0; + + // {"Callsign":"String"} + + Len = sprintf(Message, "\"Callsign\":\"%s\"", Call); + + SendHTTPRequest(sock, "/sysop/get", Message, Len, _REPLYBUFFER); + + closesocket(sock); + + return _REPLYBUFFER[0]; +} + +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL) +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int len = 100; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + char SendBuffer[1000]; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr("api.winlink.org"); + destaddr.sin_port = htons(80); + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + len = recv(sock, &Buffer[0], len, 0); + + len = sprintf(SendBuffer, "02%07d%-12s%s%s", (int)strlen(SQL), Call, GetChallengeResponse(Call, Buffer), SQL); + + send(sock, SendBuffer, len, 0); + + len = 1000; + + len = recv(sock, &Buffer[0], len, 0); + + Buffer[len] = 0; + Debugprintf(Buffer); + + closesocket(sock); + + return TRUE; + +} +// http://server.winlink.org:8085/csv/reply/ChannelList?Modes=40,41,42,43,44&ServiceCodes=BPQTEST,PUBLIC + +// Process config lines that are common to a number of HF modes + +extern int nextDummyInterlock; + +int standardParams(struct TNCINFO * TNC, char * buf) +{ + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0) + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[16]) * 60; + else if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time before failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + else if (_memicmp(buf, "AUTOSTARTDELAY", 14) == 0) // Time to wait for TNC to start + TNC->AutoStartDelay = atoi(&buf[15]); + else if (_memicmp(buf, "DEFAULTRADIOCOMMAND", 19) == 0) + TNC->DefaultRadioCmd = _strdup(&buf[20]); + else if (_memicmp(buf, "MYCALLS", 7) == 0) + { + TNC->LISTENCALLS = _strdup(&buf[8]); + strlop(TNC->LISTENCALLS, '\r'); + } + else if (_memicmp(buf, "FREQUENCY", 9) == 0) + TNC->Frequency = _strdup(&buf[10]); + else if (_memicmp(buf, "SendTandRtoRelay", 16) == 0) + TNC->SendTandRtoRelay = atoi(&buf[17]); + else if (_memicmp(buf, "Radio", 5) == 0) // Rig Control RADIO for TX amd RX (Equiv to INTERLOCK) + TNC->RXRadio = TNC->TXRadio = atoi(&buf[6]); + else if (_memicmp(buf, "TXRadio", 7) == 0) // Rig Control RADIO for TX + TNC->TXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "RXRadio", 7) == 0) // Rig Control RADIO for RXFRETRIES + TNC->RXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "TXFreq", 6) == 0) // For PTT Sets Freq mode + TNC->TXFreq = strtoll(&buf[7], NULL, 10); + else if (_memicmp(buf, "DefaultFreq", 11) == 0) // For PTT Sets Freq mode + TNC->DefaultFreq = strtoll(&buf[12], NULL, 10); + else if (_memicmp(buf, "PTTONHEX", 8) == 0) + { + // Hex String to use for PTT on for this port + + char * ptr1 = &buf[9]; + char * ptr2 = TNC->PTTOn; + int i, j, len; + + TNC->PTTOnLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else if (_memicmp(buf, "PTTOFFHEX", 9) == 0) + { + // Hex String to use for PTT off + + char * ptr = &buf[10]; + char * ptr2 = TNC->PTTOff; + int i, j, len; + + TNC->PTTOffLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else + return FALSE; + + return TRUE; +} + +void DecodePTTString(struct TNCINFO * TNC, char * ptr) +{ + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + else if (_stricmp(ptr, "CM108") == 0) + TNC->PTTMode = PTTCM108; + else if (_stricmp(ptr, "HAMLIB") == 0) + TNC->PTTMode = PTTHAMLIB; + else if (_stricmp(ptr, "FLRIG") == 0) + TNC->PTTMode = PTTFLRIG; +} + +extern SOCKET ReportSocket; +extern char LOCATOR[80]; +extern char ReportDest[7]; +extern int NumberofPorts; +extern struct RIGPORTINFO * PORTInfo[34]; // Records are Malloc'd + +time_t LastModeReportTime; +time_t LastFreqReportTime; + +VOID SendReportMsg(char * buff, int txlen); + +void sendModeReport() +{ + // if TNC is connected send mode and frequencies to Node Map as a MODE record + // Are we better sending scan info as a separate record ?? + + // MODE Port, HWType, Interlock + + struct PORTCONTROL * PORT = PORTTABLE; + + struct TNCINFO * TNC; + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "MODE "; + int i, Len = 5; + + if ((CurrentSecs - LastModeReportTime) < 900) // Every 15 Mins + return; + + LastModeReportTime = CurrentSecs; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->CONNECTED == 0 && TNC->TNCOK == 0) + continue; + + if (ReportSocket == 0 || LOCATOR[0] == 0) + continue; + + if (TNC->Frequency) + Len += sprintf(&Msg[Len], "%d,%d,%d,%.6f/", TNC->Port, TNC->Hardware, TNC->RXRadio, atof(TNC->Frequency)); + else + Len += sprintf(&Msg[Len], "%d,%d,%d/", TNC->Port, TNC->Hardware, TNC->RXRadio); + + if (Len > 240) + break; + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + +void sendFreqReport(char * From) +{ + // Send info from rig control or Port Frequency info to Node Map for Mode page. + + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "FREQ "; + int i, Len = 5, p; + + struct RIGPORTINFO * RIGPORT; + struct RIGINFO * RIG; + struct TimeScan * Band; + struct PORTCONTROL * PORT = PORTTABLE; + struct TNCINFO * TNC; + + if ((CurrentSecs - LastFreqReportTime) < 7200) // Every 2 Hours + return; + + LastFreqReportTime = CurrentSecs; + + for (p = 0; p < NumberofPorts; p++) + { + RIGPORT = PORTInfo[p]; + + for (i = 0; i < RIGPORT->ConfiguredRigs; i++) + { + int j = 1, k = 0; + + RIG = &RIGPORT->Rigs[i]; + + if (RIG->reportFreqs) + { + Len += sprintf(&Msg[Len], "%d/00:00/%s,\\|",RIG->Interlock,RIG->reportFreqs); + } + else + { + if (RIG->TimeBands) + { + Len += sprintf(&Msg[Len], "%d/",RIG->Interlock); + while (RIG->TimeBands[j]) + { + Band = RIG->TimeBands[j]; + k = 0; + + if (Band->Scanlist[0]) + { + Len += sprintf(&Msg[Len], "%02d:%02d/", Band->Start / 3600, Band->Start % 3600); + + while (Band->Scanlist[k]) + { + Len += sprintf(&Msg[Len],"%.0f,", Band->Scanlist[k]->Freq + RIG->rxOffset); + k++; + } + Len += sprintf(&Msg[Len], "\\"); + } + j++; + } + Len += sprintf(&Msg[Len], "|"); + } + } + } + } + + // Look for Port freq info + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->Frequency == NULL) + continue; + + if (TNC->RIG->TimeBands && TNC->RIG->TimeBands[1]->Scanlist) + continue; // Have freq info from Rigcontrol + + if (TNC->RXRadio == 0) // Replace with dummy + TNC->RXRadio = nextDummyInterlock++; + + // Use negative port no instead of interlock group + + Len += sprintf(&Msg[Len], "%d/00:00/%.0f|", TNC->RXRadio, atof(TNC->Frequency) * 1000000.0); + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + + diff --git a/HSMODEM.c b/HSMODEM.c new file mode 100644 index 0000000..2c0d336 --- /dev/null +++ b/HSMODEM.c @@ -0,0 +1,1880 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use HSMODEM TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + + +#include "CHeaders.h" + +#pragma pack(1) + +struct BroadcastMsg +{ + unsigned char Type; + unsigned char initialVolTX; + unsigned char initialVolRX; + unsigned char AudioTimespan; + unsigned char intialVolSpeaker; + unsigned char initalVolMic; + unsigned char Retransmits; + unsigned char SendAudio; + unsigned char RTTYAutoSync; + unsigned char Speed; + char playbackDevice[100]; + char captureDevice[100]; + char Callsign[20]; + char Locator[10]; + char Name[20]; +}; + +struct FileHeader +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[164]; +}; + +struct FileData +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + unsigned char Data[219]; +}; + +#pragma pack() + +struct HSFILEINFO +{ + struct HSFILEINFO * Next; // May want to chain entries for partial files + + char fileName[50]; + unsigned short CRC; // Used as a transfer ID + int fileSize; + int Sequence; + int State; + int Type; + time_t LastRX; + unsigned char goodBlocks[1024]; + unsigned char * Data; + int dataPointer; + int lastBlock; + int lostBlocks; + unsigned char * txData; + int txSize; + int txLeft; +}; + + +struct HSMODEMINFO +{ + struct HSFILEINFO * File; + + int Mode; + char * Capture; // Capture Device Name + char * Playback; // Playback Device Name + int Seq; // To make CRC more Unique + int txFifo; + int rxFifo; + int Sync; + int DCD; +}; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL HSMODEMWriteCommBlock(struct TNCINFO * TNC); +void HSMODEMCheckRX(struct TNCINFO * TNC); +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen); +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int HSMODEMGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); +void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); + +static char ClassName[]="HSMODEMSTATUS"; +static char WindowTitle[] = "HSMODEM"; +static int RigControlRow = 205; + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->HSModemInfo = zalloc(sizeof(struct HSMODEMINFO)); + TNC->HSModemInfo->File = zalloc(sizeof(struct HSFILEINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport + 2); // We only receive on Port + 2 + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->HSModemInfo->Capture = _strdup(&buf[8]); + strlop(TNC->HSModemInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->HSModemInfo->Playback = _strdup(&buf[9]); + strlop(TNC->HSModemInfo->Playback, 13); + } + else if (_memicmp(buf, "MODE ", 5) == 0) + TNC->HSModemInfo->Mode = atoi(&buf[5]); + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int HSMODEMGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +BOOL HSMODEMReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} + + + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // HSMODEM mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID HSMODEMChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); + HSMODEMSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + if (TNC->CONNECTED) + { + TNC->CONNECTED--; + + if (TNC->CONNECTED == 0) + { + sprintf(TNC->WEB_COMMSSTATE, "Connection to HSMODEM lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + } + + TNC->PollDelay++; + + if (TNC->PollDelay > 20) + { + TNC->PollDelay = 0; + + SendPoll(TNC); + } + + return 0; + + case 1: // poll + + HSMODEMCheckRX(TNC); + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + HSMODEMSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + HSMODEMSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("HSMODEM New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->BytesTXed += txlen; + + bytes=HSMODEMSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + // for now just send, but allow sending control + // characters with \\ or ^ escape + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + bytes=HSMODEMSendData(TNC, TXMsg, txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->BytesTXed += bytes; +// WritetoTrace(TNC, &buff->L2DATA[0], txlen); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MODE ", 5) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->HSModemInfo->Mode = atoi(&buff->L2DATA[5]); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + SendMode(TNC); + + return 0; + + } + + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + HSMODEMSendCommand(TNC, Connect); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + + HSMODEMSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + Outstanding = Queued = 0; + + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->CONNECTED != 0) << 8 | 1; + + return ((TNC->CONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + HSMODEMSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + HSMODEMSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID HSMODEMReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + HSMODEMChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID HSMODEMSuspendPort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "CONOK OFF\r"); +} + +VOID HSMODEMReleasePort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "CONOK ON\r"); +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

HSMODEM Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + + +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + + TextOut(hdc, 10, 162, "RX", 4); + TextOut(hdc, 10, 182, "TX", 4); + + if (TNC->HSModemInfo->Sync) + TextOut(hdc, 305, 162, "Sync", 4); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + Rectangle(ps.hdc, 40, 165, TNC->HSModemInfo->rxFifo + 42, 175); + SelectObject(ps.hdc, RedBrush); + Rectangle(ps.hdc, 40, 185, (TNC->HSModemInfo->txFifo * 10) + 42, 195); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + + + +VOID * HSMODEMExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int ret; + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("HSMODEM Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->Hardware = H_HSMODEM; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = HSMODEMSuspendPort; + TNC->ReleasePortProc = HSMODEMReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + + // Open and bind UDP socket + + TNC->TCPSock = socket(AF_INET,SOCK_DGRAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + WritetoConsole("Failed to create UDP socket for HSMODEM"); + ret = WSAGetLastError(); + } + else + ioctl (TNC->TCPSock, FIONBIO, ¶m); + + ret = bind(TNC->TCPSock, (struct sockaddr *) &TNC->destaddr, sizeof(struct sockaddr_in)); + + if (ret != 0) + { + // Bind Failed + + ret = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP port %d - error code = %d", TNC->TCPPort + 2, ret); + WritetoConsole(Msg); + } + + +// SendInitScript(TNC); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + HSMODEMReleaseTNC(TNC); + } +} + +VOID HSMODEMAbort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID HSMODEMDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +RECT Rect1 = {30, 160, 400, 195}; + + +VOID HSMODEMProcessTNCMessage(struct TNCINFO * TNC, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FileHeader * FH; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + int fileLen, Seq, Offset; + + // Any message indicates Ok + + Msg[Len] = 0; + + if (TNC->CONNECTED == 0) + { + // Just come up + + sprintf(TNC->WEB_COMMSSTATE, "Connected to HSMODEM"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + SendMode(TNC); + } + + TNC->CONNECTED = 100; // time out after 10 secs + + /* + 3: responses to broadcast messages (see: GUI Interface: UDP/IP/Initialization) + 1: received payload data + 4: FFT data for a spectrum monitor + 5: IQ data for a constellation display + 6: received RTTY characters + */ + switch (Msg[0]) + { + case 1: + /* + Byte 0 ... 0x01 + Byte 1 ... frame type (which was inserted by the sender) + Byte 2 ... frame counter MSB + Byte 3 ... frame counter LSB (10 bits used) + Byte 4 ... frame information (which was inserted by the sender) + Byte 5 ... unused + Byte 6 ... measured line speed MSB + Byte 7 ... measured line speed LSB + Bytes 8-10 ... unused + Bytes 11-229 ... 219 bytes payload return; + + 1 … BER Test Pattern + 2 … Image + 3 … Ascii File + 4 … HTML File + 5 … Binary File + 6 … Voice Audio (for Codec 2 or Opus) + 7 … UserInfo + */ + + Seq = Msg[2] << 8 | Msg[3]; + + switch (Msg[1]) + { + case 1: + case 6: + case 7: + + Debugprintf("%d %d %02x %s %s %s", Msg[1], Seq, Msg[4], &Msg[11], &Msg[31], &Msg[41]); + return; + + case 2: + case 3: + case 4: + case 5: + + // File transfer types + + switch (Msg[4]) + { + case 0: + case 3: + + // File Header + + FH = (struct FileHeader *) &Msg[9]; + + if (FH->CRC == Info->CRC) + { + Debugprintf("Dup Header %X", Info->CRC); + return; + } + + Info->CRC = FH->CRC; + + fileLen = FH->Size[0] * 65536 + FH->Size[1] * 256 + FH->Size[2]; + + Info->Data = zalloc(fileLen + 512); + + if (Info->Data == NULL) + return; + + Info->fileSize = fileLen; + strcpy(Info->fileName, FH->filename); + + memset(Info->goodBlocks, 0, 1024); + Info->goodBlocks[0] = 1; + + Info->lastBlock = 0; + Info->lostBlocks = 0; + Info->LastRX = time(NULL); + + Debugprintf("%d %d %04X %02x %s %d %s", Msg[1], Seq, FH->CRC, Msg[4], + FH->filename, fileLen, FH->Data); + + memcpy(Info->Data, FH->Data, 164); + Info->dataPointer = 164; + break; + + case 1: + case 2: + + // Data Frame + + if (Seq == Info->lastBlock) + { + Debugprintf("Duplicate data frame %d", Seq); + return; + } + + Info->lastBlock++; + + if (Info->lastBlock != Seq) + Info->lostBlocks += Seq - Info->lastBlock; + + Info->goodBlocks[Seq] = 1; + + Offset = (Seq - 1) * 221 + 164; + + memcpy(&Info->Data[Offset], &Msg[11], 221); + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + break; + + default: + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + return; + + } + + // End of Data Frame Case + + if (Msg[4] == 2 || Msg[4] == 3) + { + // Last Frame - check file + + if (Info->lostBlocks == 0) + { + // filename is encoding of calls and frame type + + struct _MESSAGE * buffptr; + +// FILE * fp1 = fopen(Info->fileName, "wb"); +// int WriteLen; + +// if (fp1) +// { +// WriteLen = (int)fwrite(Info->Data, 1, Info->fileSize, fp1); +// fclose(fp1); +// } + + if (strchr(Info->fileName, '!')) + { + // Callsigns encoded in filename + + + + char * Origin = &Info->fileName[0]; + char * Type = strlop(Origin, '_'); + char * Dest = strlop(Origin, '!'); + unsigned char * Packet; + + + // Convert to ax.25 format + + buffptr = GetBuff(); + + // Convert to ax.25 format + + if (buffptr == 0) + return; // No buffers, so ignore + + Type = strlop(Origin, '_'); + + Packet = &buffptr->ORIGIN[0]; + + buffptr->PORT = TNC->Port; + buffptr->LENGTH = 16 + MSGHDDRLEN + Info->fileSize; + + ConvToAX25(Origin, buffptr->ORIGIN); + ConvToAX25(Dest, buffptr->DEST); + + + while (strchr(Dest, ',')) + { + Dest = strlop(Dest, ','); // Next digi + Packet += 7; + ConvToAX25(Dest, Packet); + buffptr->LENGTH += 7; + } + + Packet[6] |= 1; // Set end of address + + Packet += 7; + + *(Packet++) = 3; + *(Packet++) = 0xF0; + + memcpy(Packet, Info->Data, Info->fileSize); + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + } + } + + return; + } + } + + return; + + case 4: // FFT data for a spectrum monitor + + Modem->txFifo = Msg[1]; + Modem->rxFifo = Msg[2]; + Modem->DCD = Msg[3]; + Modem->Sync = Msg[4]; + +#ifndef LINBPQ + InvalidateRect(TNC->hDlg, &Rect1, TRUE); +#endif + +// if (Info->Sync || Info->txFifo) +// Debugprintf("%d %d %d %d", Info->txFifo, Info->rxFifo, Info->DCD, Info->Sync); + /* + Byte 0 ... 0x04 + Byte 1 ... usage of the TX fifo (used by the transmitter to sync its data + output to the modem). This is a value between 0..255. During + an active transmission keep it above 4. + Byte 2 ... usage of RX fifo (not important, but can be displayed to the + user). A very high RX fifo usage indicates the the computer + is too slow for HSmodem. + Byte 3 ... 0 or 1. Indicates that an RF level was detected + Byte 4 ... 0 or 1. Indicates that the HSmodem receiver is synchronized + with a signal + Byte 5 ... maximum audio level (0..100%) of the audio input from the + transceiver. Can be used to detect clipping. + Byte 6 ... maximum audio level (0..100%) of the audio output to the + transceiver. Can be used to detect clipping. + Byte 7 ... in RTTY mode this is the auto-locked RTTY frequency MSB + Byte 8 ... and LSB + Byte 9 ... RTTY: 0=tx off, 1=txon + Byte 10 to the end ... FFT spectrum data, beginning at 0 Hz to 4kHz with + a resolution of 10 Hz +*/ + return; + + + case 5: // IQ data for a constellation display + return; + + case 6: //received RTTY characters + return; + } + + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + +extern char LOC[7]; + +void SendMode(struct TNCINFO * TNC) +{ + unsigned char Msg[221] = ""; + int ret; + + Msg[0] = 16; + Msg[1] = TNC->HSModemInfo->Mode; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} + +void SendPoll(struct TNCINFO * TNC) +{ + struct BroadcastMsg PollMsg = {0x3c, 100, 100, 0, 50, 50, 1, 0, 0, 9}; + int ret; + + strcpy(&PollMsg.captureDevice[0], TNC->HSModemInfo->Capture); + strcpy(&PollMsg.playbackDevice[0], TNC->HSModemInfo->Playback); +// strcpy(&PollMsg.playbackDevice[0], "CABLE Input (VB-Audio Virtual Cable)"); + + strcpy(&PollMsg.Callsign[0], TNC->NodeCall); + strcpy(&PollMsg.Locator[0], LOC); + strcpy(&PollMsg.Name[0], "1234567890"); + + TNC->destaddr.sin_port = htons(TNC->TCPPort); // Command Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&PollMsg, 260, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} +/* + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[163]; +*/ + +unsigned short int compute_crc(unsigned char *buf,int len); + +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + unsigned short int crc; + int ret, fragLen = txlen; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + + char Seq[60] = ""; + + sprintf(Seq, "%04X%s", Modem->Seq++, FN); + + crc = compute_crc(Seq, 60); + + crc ^= 0xffff; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + if (txlen > 163) + { + // Need to send as multiple fragments + + fragLen = 164; + Info->txData = malloc(txlen + 512); + memcpy(Info->txData, data, txlen); + Info->txSize = txlen; + Info->txLeft = txlen - 164; + Msg.Info = 0; // First Fragment + } + + strcpy(Msg.filename, FN); + memcpy(Msg.Data, data, txlen); + memcpy(&Msg.CRC, &crc, 2); + Msg.Size[0] = txlen >> 16; + Msg.Size[1] = txlen >> 8;; + Msg.Size[2] = txlen; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + memset(&Msg, 0, sizeof(struct FileHeader)); + + return ret; +} + +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + + return 0; +} + +void HSMODEMCheckRX(struct TNCINFO * TNC) +{ + int Len = 0; + unsigned char Buff[2000]; + + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + while (1) + { + if (Len == -1) + { +// Debugprintf("%d", GetLastError()); + Len = 0; + return; + } + TNC->RXLen = Len; + HSMODEMProcessTNCMessage(TNC, Buff, Len); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + } + return; + + return; +} + +int HSMODEMWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + + +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); + + return 0; +} + + \ No newline at end of file diff --git a/HTMLCommonCode.c b/HTMLCommonCode.c new file mode 100644 index 0000000..9e2a3eb --- /dev/null +++ b/HTMLCommonCode.c @@ -0,0 +1,124 @@ +/* +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 +*/ + + +// General C Routines common to bpq32 and linbpq.mainly moved from BPQ32.c + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + + +#pragma data_seg("_BPQDATA") + +#include "CHeaders.h" + +#include "templatedefs.c" // Inline definitions from HTLMPages + + +char * GetTemplateFromFile(int Version, char * FN) +{ + int FileSize; + char * MsgBytes; + char MsgFile[265]; + FILE * hFile; + size_t ReadLen; + BOOL Special = FALSE; + struct stat STAT; + + if (strcmp(FN, "WebMailMsg.txt") == 0) + return WebMailMsgtxt(); + + if (strcmp(FN, "FwdPage.txt") == 0) + return FwdPagetxt(); + + if (strcmp(FN, "FwdDetail.txt") == 0) + return FwdDetailtxt(); + + if (strcmp(FN, "webscript.js") == 0) + return webscriptjs(); + + if (strcmp(FN, "WebMailPage.txt") == 0) + return WebMailPagetxt(); + + if (strcmp(FN, "MainConfig.txt") == 0) + return MainConfigtxt(); + + if (strcmp(FN, "MsgPage.txt") == 0) + return MsgPagetxt(); + + if (strcmp(FN, "UserDetail.txt") == 0) + return UserDetailtxt(); + + if (strcmp(FN, "UserPage.txt") == 0) + return UserPagetxt(); + + if (strcmp(FN, "Housekeeping.txt") == 0) + return Housekeepingtxt(); + + if (strcmp(FN, "WP.txt") == 0) + return WPtxt(); + + if (strcmp(FN, "ChatConfig.txt") == 0) + return ChatConfigtxt(); + + if (strcmp(FN, "ChatStatus.txt") == 0) + return ChatStatustxt(); + + sprintf(MsgFile, "%s/HTML/%s", BPQDirectory, FN); + + if (stat(MsgFile, &STAT) == -1) + { + MsgBytes = _strdup("File is missing"); + return MsgBytes; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + MsgBytes = _strdup("File is missing"); + return MsgBytes; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + MsgBytes[FileSize] = 0; + fclose(hFile); + + // Check Version + + if (Version) + { + int PageVersion = 0; + + if (memcmp(MsgBytes, ""); + if (ptr2) + { + PrevLen = (int)(ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = (int)(FileSize - (ptr1 - Buffer)); + } + } + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = (int)(StripPtr - Buffer); + + FileSize = BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (int)(ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + int KeyLen; + char * NewText; + int NewTextLen; + + ptr3 += 2; + KeyLen = (int)(ptr3 - ptr2); + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + NewText = LookupKey(Key); + + if (NewText) + { + NewTextLen = (int)strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = (int)(Buffer + FileSize - ptr3); + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + NewMessage[NewFileSize] = 0; + + strcpy(Buffer, NewMessage); + free(NewMessage); + + return NewFileSize; +} + +int SendMessageFile(SOCKET sock, char * FN, BOOL OnlyifExists, int allowDeflate) +{ + int FileSize = 0, Sent, Loops = 0; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + int ReadLen; + BOOL Special = FALSE; + int Len; + int HeaderLen; + char Header[256]; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * ptr; + char * Compressed = 0; + char Encoding[] = "Content-Encoding: deflate\r\n"; + char Type[64] = "Content-Type: text/html\r\n"; + +#ifdef WIN32 + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "SendMessageFile"); + + __try { +#endif + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strlen(FN) > 256) + { + FN[256] = 0; + Debugprintf("HTTP File Name too long %s", FN); + } + + if (strcmp(FN, "/") == 0) + if (APRSActive) + sprintf(MsgFile, "%s/HTML/index.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML/indexnoaprs.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML%s", BPQDirectory, FN); + + + // First see if file exists so we can override standard ones in code + + if (stat(MsgFile, &STAT) == 0 && (hFile = fopen(MsgFile, "rb"))) + { + FileSize = STAT.st_size; + + MsgBytes = zalloc(FileSize + 1); + + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + // ft.QuadPart -= 116444736000000000; + // ft.QuadPart /= 10000000; + + // ctime = ft.LowPart; + + FormatTime3(FileTimeString, STAT.st_ctime); + } + else + { + // See if it is a hard coded file + + MsgBytes = GetStandardPage(&FN[1], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + + FormatTime3(FileTimeString, 0); + } + else + { + if (OnlyifExists) // Set if we dont want an error response if missing + return -1; + + Len = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sock, Header, Len, 0); + return 0; + } + } + + // if HTML file, look for ##...## substitutions + + if ((strcmp(FN, "/") == 0 || strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + FileSize = ProcessSpecialPage(MsgBytes, FileSize); + FormatTime3(FileTimeString, time(NULL)); + + } + + FormatTime3(TimeString, time(NULL)); + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ptr, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (allowDeflate) + { + Compressed = Compressit(MsgBytes, FileSize, &FileSize); + } + else + { + Encoding[0] = 0; + Compressed = MsgBytes; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "%s%s" + "\r\n", FileSize, TimeString, FileTimeString, Type, Encoding); + + send(sock, Header, HeaderLen, 0); + + Sent = send(sock, Compressed, FileSize, 0); + + while (Sent != FileSize && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { +// Debugprintf("%d out of %d sent", Sent, FileSize); + FileSize -= Sent; + memmove(Compressed, &Compressed[Sent], FileSize); + } + + Sleep(30); + Sent = send(sock, Compressed, FileSize, 0); + } + +// Debugprintf("%d out of %d sent %d loops", Sent, FileSize, Loops); + + + free (MsgBytes); + if (allowDeflate) + free (Compressed); + +#ifdef WIN32 + } +#include "StdExcept.c" + Debugprintf("Sending FIle %s", FN); +} +#endif + +return 0; +} + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len) +{ + int Loops = 0; + int Sent = send(sock, Buffer, Len, 0); + char * Copy = NULL; + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, Len, Loops); + + if (Copy == NULL) + { + Copy = malloc(Len); + memcpy(Copy, Buffer, Len); + } + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Copy, &Copy[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Copy, Len, 0); + } + + if (Copy) + free(Copy); + + return; +} + +int RefreshTermWindow(struct TCPINFO * TCP, struct HTTPConnectionInfo * Session, char * _REPLYBUFFER) +{ + char Msg[400] = ""; + int HeaderLen, ReplyLen; + char Header[256]; + + PollSession(Session); // See if anything received + + if (Session->Changed) + { + int Last = Session->LastLine; + int n; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + Session->Changed = 0; + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(Session->sock, Header, HeaderLen); + sendandcheck(Session->sock, _REPLYBUFFER, ReplyLen); + + return 1; + } + else + return 0; +} + + +extern struct TNCINFO * TNCInfo[41]; + +int SetupNodeMenu(char * Buff, int LOCAL) +{ + int Len = 0, i; + struct TNCINFO * TNC; + int top = 0, left = 0; + + char NodeMenuHeader[] = "%s's BPQ32 Web Server" + "" + + "" + "

BPQ32 Node %s

" + "

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

Ports

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

Call %s not found

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

Info for Node %s:%s

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

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

Neighbours

"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "" + ""); + + NRRoute = &Dest->NRROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = NRRoute->ROUT_NEIGHBOUR; + + if (Neighbour) + { + len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); + Normcall[len] = 0; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); + } + NRRoute++; + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Qual Obs Port Call
%c %d%d%d%s
"); + + goto SendResp; + + } + /* + + MOV ESI,OFFSET32 NODEROUTEHDDR + MOV ECX,11 + REP MOVSB + + LEA ESI,DEST_CALL[EBX] + CALL DECODENODENAME ; CONVERT TO ALIAS:CALL + REP MOVSB + + CMP DEST_RTT[EBX],0 + JE SHORT @f ; TIMER NOT SET - DEST PROBABLY NOT USED + + MOVSB ; ADD SPACE + CALL DORTT + + @@: + MOV AL,CR + STOSB + + MOV ECX,3 + MOV DH,DEST_ROUTE[EBX] ; CURRENT ACTIVE ROUTE + MOV DL,1 + + push ebx + + PUBLIC CMDN110 + CMDN110: + + MOV ESI,ROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDN199 + + + MOV AX,' ' + CMP DH,DL + JNE SHORT CMDN112 ; NOT CURRENT DEST + MOV AX,' >' + + CMDN112: + + STOSW + + PUSH ECX + + MOV AL,ROUT1_QUALITY[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,ROUT1_OBSCOUNT[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + MOV AL,CR + STOSB + + ADD EBX,ROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + DEC ECX + JNZ CMDN110 + + PUBLIC CMDN199 + CMDN199: + + POP EBX + + ; DISPLAY INP3 ROUTES + + MOV ECX,3 + MOV DL,4 + + PUBLIC CMDNINP3 + CMDNINP3: + + MOV ESI,INPROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDNINPEND + + MOV AX,' ' + CMP DH,DL + JNE SHORT @F ; NOT CURRENT DEST + MOV AX,' >' + + @@: + + STOSW + + PUSH ECX + + MOV AL, Hops1[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOVZX EAX, SRTT1[EBX] + + MOV EDX,0 + MOV ECX, 100 + DIV ECX + CALL CONV_5DIGITS + MOV AL,'.' + STOSB + MOV EAX, EDX + CALL PRINTNUM + MOV AL,'s' + STOSB + MOV AL,' ' + STOSB + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + + MOV AL,CR + STOSB + + ADD EBX,INPROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + LOOP CMDNINP3 + + CMDNINPEND: + + ret + + */ + + + if (_stricmp(NodeURL, "/Node/Routes.html") == 0) + { + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int count; + char Normcall[10]; + char locked; + int NodeCount; + int Percent = 0; + int Iframes, Retries; + char Active[10]; + int Queued; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); + + for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + Normcall[len]=0; + + if ((Routes->NEIGHBOUR_FLAG & 1) == 1) + locked = '!'; + else + locked = ' '; + + NodeCount = COUNTNODES(Routes); + + if (Routes->NEIGHBOUR_LINK) + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); + else + Queued = 0; + + Iframes = Routes->NBOUR_IFRAMES; + Retries = Routes->NBOUR_RETRIES; + + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + strcpy(Active, ">"); + else + strcpy(Active, " "); + + if (Iframes) + Percent = (Retries * 100) / Iframes; + else + Percent = 0; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RouteLine, Active, Routes->NEIGHBOUR_PORT, Normcall, locked, + Routes->NEIGHBOUR_QUAL, NodeCount, Iframes, Retries, Percent, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, + Routes->NEIGHBOUR_TIME >> 8, Routes->NEIGHBOUR_TIME & 0xff, Queued, Routes->OtherendsRouteQual); + } + Routes+=1; + } + } + + if (_stricmp(NodeURL, "/Node/Links.html") == 0) + { + struct _LINKTABLE * Links = LINKS; + int MaxLinks = MAXLINKS; + int count; + char Normcall1[10]; + char Normcall2[10]; + char State[12] = "", Type[12] = "Uplink"; + int axState; + int cctType; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", LinkHddr); + + for (count=0; countLINKCALL[0] != 0) + { + int len = ConvFromAX25(Links->LINKCALL, Normcall1); + Normcall1[len] = 0; + + len = ConvFromAX25(Links->OURCALL, Normcall2); + Normcall2[len] = 0; + + axState = Links->L2STATE; + + if (axState == 2) + strcpy(State, "Connecting"); + else if (axState == 3) + strcpy(State, "FRMR"); + else if (axState == 4) + strcpy(State, "Closing"); + else if (axState == 5) + strcpy(State, "Active"); + else if (axState == 6) + strcpy(State, "REJ Sent"); + + cctType = Links->LINKTYPE; + + if (cctType == 1) + strcpy(Type, "Uplink"); + else if (cctType == 2) + strcpy(Type, "Downlink"); + else if (cctType == 3) + strcpy(Type, "Node-Node"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], LinkLine, Normcall1, Normcall2, Links->LINKPORT->PORTNUMBER, + State, Type, 2 - Links->VER1FLAG ); + + Links+=1; + } + } + } + + if (_stricmp(NodeURL, "/Node/Users.html") == 0) + { + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + int count; + char State[12] = "", Type[12] = "Uplink"; + char LHS[50] = "", MID[10] = "", RHS[50] = ""; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", UserHddr); + + for (count=0; count < MAXCIRCUITS; count++) + { + if (L4->L4USER[0]) + { + RHS[0] = MID[0] = 0; + + if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES + { + // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK + + if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END + { + // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION + // DISPLAY TO THE RIGHT FOR NOW + + strcpy(LHS, "(Closing) "); + DISPLAYCIRCUIT(L4, RHS); + goto CMDS50; + } + else + goto CMDS60; // WILL PROCESS FROM OTHER END + } + + if (L4->L4CROSSLINK == 0) + { + // Single Entry + + DISPLAYCIRCUIT(L4, LHS); + } + else + { + DISPLAYCIRCUIT(L4, LHS); + + Partner = L4->L4CROSSLINK; + + if (Partner->L4STATE == 5) + strcpy(MID, "<-->"); + else + strcpy(MID, "<~~>"); + + DISPLAYCIRCUIT(Partner, RHS); + } +CMDS50: + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], UserLine, LHS, MID, RHS); + } +CMDS60: + L4++; + } + } + /* + PUBLIC CMDUXX_1 + CMDUXX_1: + push EBX + push ESI + PUSH ECX + push EDI + + call _FINDDESTINATION + pop EDI + + jz SHORT NODE_FOUND + + push EDI ; NET/ROM not found + call CONVFROMAX25 ; CONVERT TO CALL + pop EDI + mov ESI,OFFSET32 NORMCALL + rep movsb + + jmp SHORT END_CMDUXX + + PUBLIC NODE_FOUND + NODE_FOUND: + + lea ESI,DEST_CALL[EBX] + call DECODENODENAME + + REP MOVSB + + PUBLIC END_CMDUXX + END_CMDUXX: + + POP ECX + pop ESI + pop EBX + ret + + }}} + */ + + else if (_stricmp(NodeURL, "/Node/Terminal.html") == 0) + { + if (COOKIE && Session) + { + // Already signed in as sysop + + struct UserRec * USER = Session->USER; + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, USER->Callsign); + ConvToAX25(NewSession->HTTPCall, AXCall); + ChangeSessionCallsign(NewSession->Stream, AXCall); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + NewSession->TNC = conn->TNC; + + + // if (Appl[0]) + // { + // strcat(Appl, "\r"); + // SendMsg(Session->Stream, Appl, strlen(Appl)); + // } + + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + } + else if (LOCAL) + { + // connected to 127.0.0.1 so sign in using node call + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, MYNODECALL); + ChangeSessionCallsign(NewSession->Stream, MYCALL); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = TRUE; + NewSession->TNC = conn->TNC; + } + } + else + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Signon.html") == 0) + { + ReplyLen = sprintf(_REPLYBUFFER, NodeSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Drivers") == 0) + { + int Bufferlen = SendMessageFile(sock, "/Drivers.htm", TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_stricmp(NodeURL, "/Node/OutputScreen.html") == 0) + { + struct HTTPConnectionInfo * Session = FindSession(Context); + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + Session->sock = sock; // socket to reply on + ReplyLen = RefreshTermWindow(TCP, Session, _REPLYBUFFER); + + if (ReplyLen == 0) // Nothing new + { + // Debugprintf("GET with no data avail - response held"); + Session->ResponseTimer = 1200; // Delay response for up to a minute + } + else + { + // Debugprintf("GET - outpur sent, timer was %d, set to zero", Session->ResponseTimer); + Session->ResponseTimer = 0; + } + + Session->KillTimer = 0; + return 0; // Refresh has sent any available output + } + } + + else if (_stricmp(NodeURL, "/Node/InputLine.html") == 0) + { + struct TNCINFO * TNC = conn->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, ""); + + } + + else if (_stricmp(NodeURL, "/Node/PTT") == 0) + { + struct TNCINFO * TNC = conn->TNC; + int x = atoi(Context); + } + + +SendResp: + + FormatTime3(TimeString, time(NULL)); + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += (int)strlen(Tail); + + + if (allowDeflate) + { + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + } + else + { + Encoding[0] = 0; + Compressed = _REPLYBUFFER; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Date: %s\r\n%s\r\n", ReplyLen, TimeString, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + } + return 0; + +#ifdef WIN32 + } +#include "StdExcept.c" +} +return 0; +#endif +} + +void ProcessHTTPMessage(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + InnerProcessHTTPMessage((struct ConnectionInfo *)conn); + free(conn); + return; +} + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID FormatTime3(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); + +} + +// Sun, 06 Nov 1994 08:49:37 GMT + +int StatusProc(char * Buff) +{ + int i; + char callsign[12] = ""; + char flag[3]; + UINT Mask, MaskCopy; + int Flags; + int AppNumber; + int OneBits; + int Len = sprintf(Buff, "" + "Stream Status"); + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + + for (i=1;i<65; i++) + { + callsign[0]=0; + + if (GetAllocationState(i)) + + strcpy(flag,"*"); + else + strcpy(flag," "); + + GetCallsign(i,callsign); + + Mask = MaskCopy = Get_APPLMASK(i); + + // if only one bit set, convert to number + + AppNumber = 0; + OneBits = 0; + + while (MaskCopy) + { + if (MaskCopy & 1) + OneBits++; + + AppNumber++; + MaskCopy = MaskCopy >> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + else + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + if ((i & 1) == 0) + Len += sprintf(&Buff[Len], ""); + + } + + Len += sprintf(&Buff[Len], "
    RX   TX   MON  App  Flg Callsign  Program    RX   TX   MON  App  Flg Callsign  Program
%d%s%d%d%d%x%x%s%s%d%s%d%d%d%d%x%s%s
"); + return Len; +} + +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo *Sess; + + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + Sess = *Session = AllocateSession(sock, 'N'); + Sess->USER = USER; + + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Set-Cookie: BPQSessionCookie=%s; Path = /\r\n\r\n", (int)(ReplyLen + strlen(Tail)), Sess->Key); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, NodeSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + + return ReplyLen; +} + + + + +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + struct HTTPConnectionInfo * NewSession; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && (USER->Secure || WebMail)) + { + // ok + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + { + + ReplyLen = 0; + strcpy(NewSession->Callsign, USER->Callsign); + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; +} + + +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + *Session = AllocateSession(Appl[0], 'C'); + + if (Session) + { + ReplyLen = 0; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; + +} + +#ifdef WIN32 + +#include +#include + +#define SHA1_HASH_LEN 20 + +BOOL SHA1PasswordHash(char * lpszPassword, char * Hash) +{ + HCRYPTPROV hCryptProv; // Handle to our context + HCRYPTHASH hCryptHash; // Handle to our hash + BYTE bHashValue[SHA1_HASH_LEN]; // This will hold our SHA-1 hash + DWORD dwSize = SHA1_HASH_LEN; // Size of output + BOOL bSuccess = FALSE; // We change this to TRUE if we complete the operations + // Declare all the variables at the start of our code for C89 compatability + + if(CryptAcquireContext(&hCryptProv, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { // Initiate usage of the functions + if(CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hCryptHash)) + { // Create a SHA1 hash + if(CryptHashData(hCryptHash, (PBYTE)lpszPassword, lstrlen(lpszPassword) * sizeof(TCHAR), 0)) + { // Update the hash, (process our password) + if(CryptGetHashParam(hCryptHash, HP_HASHVAL, bHashValue, &dwSize, 0)) + { // Extract the hash + + memcpy(Hash, bHashValue, 20); + bSuccess = TRUE; + } + } + CryptDestroyHash(hCryptHash); + } + CryptReleaseContext(hCryptProv, 0); + } + + return bSuccess; +} + +#else + +#include + +BOOL SHA1PasswordHash(char * data, char * Hash) +{ + SHA1(data, strlen(data), Hash); + return 1; +} +#endif + +int BuildRigCtlPage(char * _REPLYBUFFER) +{ + int ReplyLen; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int p, i; + + char Page[] = + "\r\n" + // "\r\n" + "Rigcontrol\r\n" + "" + "

Rigcontrol

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + ""; + char RigLine[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n"; + char Tail[] = + "
RadioFreqModeSTPorts
%s%s%s/1%c%c%s
\r\n" + "\r\n"; + + ReplyLen = sprintf(_REPLYBUFFER, "%s", Page); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigLine, RIG->WEB_Label, RIG->WEB_FREQ, RIG->WEB_MODE, RIG->WEB_SCAN, RIG->WEB_PTT, RIG->WEB_PORTS, RIG->Interlock); + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", Tail); + return ReplyLen; +} + + +void SendRigWebPage() +{ + int n; + struct ConnectionInfo * sockptr; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + for (n = 0; n < 33; n++) + { + TNC = TNCInfo[n]; + + if (TNC && TNC->Hardware == H_TELNET) + { + TCP = TNC->TCPInfo; + + if (TCP) + { + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode && sockptr->WebSocks && strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + char RigMsg[8192]; + int RigMsgLen = strlen(RigWebPage); + char* ptr; + + RigMsg[0] = 0x81; // Fin, Data + RigMsg[1] = 126; // Unmasked, Extended Len + RigMsg[2] = RigMsgLen >> 8; + RigMsg[3] = RigMsgLen & 0xff; + strcpy(&RigMsg[4], RigWebPage); + + // If secure session enable PTT button + + if (sockptr->WebSecure) + { + while (ptr = strstr(RigMsg, "hidden")) + memcpy(ptr, " ", 6); + } + + send(sockptr->socket, RigMsg, RigMsgLen + 4, 0); + } + } + } + } + } + } +} + + + + + diff --git a/HanksRT.c b/HanksRT.c new file mode 100644 index 0000000..4cc137b --- /dev/null +++ b/HanksRT.c @@ -0,0 +1,4404 @@ +/* +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 + +#pragma data_seg("_BPQDATA") + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + +#ifdef LINBPQ +#include "CHeaders.h" +#endif + +#include "bpqchat.h" + +#ifndef WIN32 + +iconv_t link_toUTF8 = NULL; + +#endif + +BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len); +VOID ChatClearQueue(ChatCIRCUIT * conn); +VOID ChatFlush(ChatCIRCUIT * conn); +VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen); +unsigned short int compute_crc(unsigned char *buf,int len); +char * ReadInfoFile(char * File); +void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); +extern struct SEM ChatSemaphore; +UCHAR * APIENTRY GetLogDirectory(); +char * APIENTRY GetBPQDirectory(); + +extern SOCKADDR_IN Chatreportdest; + +char OurNode[10]; +char OurAlias[10]; + +#define MaxSockets 64 + +int MaxChatStreams=0; +ChatCIRCUIT ChatConnections[MaxSockets+1]; + +ULONG ChatApplMask; + +int NumberofChatStreams=0; + +char ChatSignoffMsg[100]; + +char OtherNodesList[1000]; +char ChatWelcomeMsg[1000]; + +char Position[81] = ""; +char PopupText[260] = ""; +int PopupMode = 0; + +char RtKnown[MAX_PATH] = "RTKnown.txt"; +char RtUsr[MAX_PATH] = "STUsers.txt"; +char RtUsrTemp[MAX_PATH] = "STUsers.tmp"; + +int AXIPPort = 0; + +ChatCIRCUIT *circuit_hd = NULL; // This is a chain of RT circuits. There may be others + +CHATNODE *node_hd = NULL; // Nodes + +LINK *link_hd = NULL; // Nodes we link to +TOPIC *topic_hd = NULL; + +USER *user_hd = NULL; + +KNOWNNODE * known_hd = NULL; + +int ChatTmr = 0; + +BOOL NeedStatus = FALSE; + +char Verstring[80]; + +static void node_dec(CHATNODE *node); +static KNOWNNODE *knownnode_add(char *call); +VOID SendChatLinkStatus(); +char * lookupuser(char * call); +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user); + +static int AutoColours[20] = {0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89}; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +int NeedINFO = 1; // Send INFO Msg after 10 Secs +time_t RunningConnectScript = 0; + +//#undef free +//#define free(p) + + +int ChatIsUTF8(unsigned char *ptr, int len) +{ + int n; + unsigned char * cpt = ptr; + + // This is simpler than the Term version, as it only handles complete lines of text, so cant get split sequences + + cpt--; + + for (n = 0; n < len; n++) + { + cpt++; + + if (*cpt < 128) + continue; + + if ((*cpt & 0xF8) == 0xF0) + { // start of 4-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80) + && ((*(cpt + 3) & 0xC0) == 0x80)) + { + cpt += 3; + n += 3; + continue; + } + return FALSE; + } + else if ((*cpt & 0xF0) == 0xE0) + { // start of 3-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80)) + { + cpt += 2; + n += 2; + continue; + } + return FALSE; + } + else if ((*cpt & 0xE0) == 0xC0) + { // start of 2-byte sequence + if ((*(cpt + 1) & 0xC0) == 0x80) + { + cpt++; + n++; + continue; + } + return FALSE; + } + return FALSE; + } + + return TRUE; +} + +#ifndef LINBPQ + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + + +VOID * _zalloc_dbg(int len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + + ptr=_malloc_dbg(len, type, file, line); + + if (ptr == NULL) + CriticalErrorHandler("malloc failed"); + + memset(ptr, 0, len); + + return ptr; +} + + +#endif + +VOID __cdecl nprintf(ChatCIRCUIT * conn, const char * format, ...) + +{ + // seems to be printf to a socket + + char buff[600]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + nputs(conn, buff); +} + + +VOID nputc(ChatCIRCUIT * conn, char chr) +{ + // Seems to send chr to socket + + ChatWriteLogLine(conn, '>',&chr, 1, LOG_CHAT); + ChatQueueMsg(conn, &chr, 1); +} + +VOID nputs(ChatCIRCUIT * conn, char * buf) +{ + // Seems to send buf to socket + + ChatQueueMsg(conn, buf, (int)strlen(buf)); + + if (*buf == 0x1b) + buf += 2; // Colour Escape + + ChatWriteLogLine(conn, '>',buf, (int)strlen(buf), LOG_CHAT); +} + +int ChatQueueMsg(ChatCIRCUIT * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + if (conn->rtcflags & p_linked) + conn->u.link->lastMsgReceived = time(NULL); + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + if (conn->OutputQueueLength + len > 9999) + { + Debugprintf("Output Queue Overflow %d", conn->OutputQueueLength); + + // Shouldn't clear buffer as this will corrupt any partly recevied message - just drop it + +// conn->OutputQueueLength = 0; +// conn->OutputGetPointer = 0; + FreeSemaphore(&OutputSEM); + return 0; // or we will send a partial message + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + + FreeSemaphore(&OutputSEM); + + return len; +} + +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user) +{ + if (!rtloginu (conn, TRUE)) + { + // Already connected - close + + ChatFlush(conn); + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + +} + +VOID ChatExpandAndSendMessage(ChatCIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + int len; + char Dollar[] = "$"; + char CR[] = "\r"; + int Msgs = 0, Unread = 0; + + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = (int)(ptr - OldP); // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = (int)strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + ChatWriteLogLine(conn, '>', NewMessage, len, LOG); + ChatQueueMsg(conn, NewMessage, len); +} + + + +void chat_link_out (LINK *link) +{ + int n, p; + ChatCIRCUIT * conn; + char Msg[80]; + + for (n = NumberofChatStreams-1; n >= 0 ; n--) + { + conn = &ChatConnections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->BPQStream = p; + + conn->Active = TRUE; + circuit_new(conn, p_linkini); + conn->u.link = link; + conn->Flags = CHATMODE | CHATLINK; + + n=sprintf_s(Msg, sizeof(Msg), "Connecting to Chat Node %s", conn->u.link->alias); + + strcpy(conn->Callsign, conn->u.link->alias); + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + + link->ScriptIndex = -1; + RunningConnectScript = time(NULL); + link->MoreLines = TRUE; + link->scriptRunning = TRUE; + link->RTLSent = 0; + + ConnectUsingAppl(conn->BPQStream, ChatApplMask); + + // Connected Event will trigger connect to remote system + + return; + } + } + return; +} + + +VOID saywhat(ChatCIRCUIT *circuit) +{ + nputs(circuit, "Invalid Command\r"); +} + +VOID saydone(ChatCIRCUIT *circuit) +{ + nputs(circuit, "Ok\r"); +} + +VOID strnew(char ** new, char *f1) +{ + // seems to allocate a new string, and copy the old one to it + // how is this different to strdup?? + + *new = _strdup(f1); +} + +#define sl_ins_hd(link, hd) \ + if (hd == NULL)\ + hd=link;\ + else\ + {\ + link->next=hd->next;\ + hd->next=link;\ + } + +BOOL matchi(char * p1, char * p2) +{ + // Return TRUE is strings match + + if (_stricmp(p1, p2)) + return FALSE; + else + return TRUE; +} + + +VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffer, int len) +{ + ChatCIRCUIT *c; + char * Buffer = OrigBuffer; + WCHAR BufferW[65536]; + UCHAR BufferB[65536]; + + // Convert to UTF8 if not already in UTF-8 + + if (len == 73 && memcmp(&OrigBuffer[40], " ", 20) == 0) + { + // Chat Signon Message. If Topic is present, switch to it + + char * Context; + char * Appl; + char * topic; + + Appl = strtok_s(OrigBuffer, " ,\r", &Context); + topic = strtok_s(NULL, " ,\r", &Context); + + if (topic == NULL) + return; // Just Chat + + // Have a Topic + + if (conn->Flags & GETTINGUSER) + { + // Need to log in before switching topic, so Give a dummy name here + + conn->Flags &= ~GETTINGUSER; + strcpy(user->Name, "?_name"); + ChatSendWelcomeMsg(conn->BPQStream, conn, user); + } + + OrigBuffer[40] = 0; + sprintf(&OrigBuffer[40],"/t %s\r", topic); + strcpy(OrigBuffer, &OrigBuffer[40]); + len = (int)strlen(OrigBuffer); + } + else + { + // Normal input + + if (conn->Flags & GETTINGUSER) + { + // Check not getting *RTL in response to Name prompt + + if (memcmp(Buffer, "*RTL", 4) == 0) + { + // Other end thinks this is a node-node link + + Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", + conn->Callsign); + + knownnode_add(conn->Callsign); // So it won't happen again + + Disconnect(conn->BPQStream); + return; + } + + conn->Flags &= ~GETTINGUSER; + memcpy(user->Name, Buffer, len-1); + ChatSendWelcomeMsg(conn->BPQStream, conn, user); + + return; + } + } + + if (ChatIsUTF8(OrigBuffer, len) == FALSE) + { + // With Windows it is simple - convert using current codepage + // I think the only reliable way is to convert to unicode and back + +#ifdef WIN32 + + int wlen; + + wlen = MultiByteToWideChar(CP_ACP, 0, Buffer, len, BufferW, 65536); + len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, 63336, NULL, NULL); + Buffer = BufferB; + +#else + int left = 65536; + UCHAR * BufferBP = BufferB; + struct user_t * icu = conn->u.user; + + if (conn->rtcflags & p_user) + { + if (icu->iconv_toUTF8 == NULL) + { + icu->iconv_toUTF8 = iconv_open("UTF-8", icu->Codepage); + + if (icu->iconv_toUTF8 == (iconv_t)-1) + icu->iconv_toUTF8 = iconv_open("UTF-8", "CP1252"); + } + + iconv(icu->iconv_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu->iconv_toUTF8, &Buffer, &len, (char ** __restrict__)&BufferBP, &left); + } + else + { + if (link_toUTF8 == NULL) + link_toUTF8 = iconv_open("UTF-8", "CP1252"); + + iconv(link_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(link_toUTF8, &Buffer, &len, (char ** __restrict__)&BufferBP, &left); + } + len = 65536 - left; + Buffer = BufferB; + +#endif + + } + ChatWriteLogLine(conn, '<',Buffer, len, LOG_CHAT); + + + Buffer[len] = 0; + + strlop(Buffer, '\r'); + + if (conn->rtcflags == p_linkwait) + { + //waiting for *RTL + + if (memcmp(Buffer, "*RTL", 4) == 0) + { + // Node - Node Connect + + if (rtloginl (conn, conn->Callsign)) + { + // Accepted + + conn->Flags |= CHATLINK; + return; + } + else + { + // Connection refused. rtlogin1 has sent error message and closed link + + return; + } + } + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + return; + + nprintf(conn, "Unexpected Message on Chat Node-Node Link - Disconnecting\r"); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + + Disconnect(conn->BPQStream); + return; + } + + if (conn->Flags & CHATLINK) + { +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try + { + chkctl(conn, Buffer, len); + } + + #define EXCEPTMSG "Process Chat Line" + #include "StdExcept.c" + + Debugprintf("CHAT *** Was procesing Chat Node Message %s", Buffer); + Disconnect(conn->BPQStream); + CheckProgramErrors(); + } +#else + chkctl(conn, Buffer, len); +#endif + return; + } + + if(conn->u.user == NULL) + { + // A node link, but not activated yet, or a chat console which has dosconnected + + if (conn->BPQStream != -2) + return; + + // Log console user in + + if (rtloginu (conn, TRUE)) + conn->Flags |= CHATMODE; + + return; + + } + + if ((len <6) && (memcmp(Buffer, "*RTL", 4) == 0)) + { + // Other end thinks this is a node-node link + + Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", + conn->Callsign); + + knownnode_add(conn->Callsign); // So it won't happen again + + Disconnect(conn->BPQStream); + return; + } + + if (Buffer[0] == '/') + { + // Process Command + + if (_memicmp(&Buffer[1], "Bye", 1) == 0) + { + SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); + + if (conn->BPQStream < 0) + { + logout(conn); + conn->Flags = 0; + if (conn->BPQStream == -2) + CloseConsole(conn->BPQStream); + } + else + ReturntoNode(conn->BPQStream); + + return; + } + + if (_memicmp(&Buffer[1], "Quit", 4) == 0) + { + SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); + + if (conn->BPQStream < 0) + { + logout(conn); + conn->Flags = 0; + if (conn->BPQStream == -2) + CloseConsole(conn->BPQStream); + } + + else + { + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + } + + if (_memicmp(&Buffer[1], "Keepalive", 4) == 0) + { + conn->u.user->rtflags ^= u_keepalive; + upduser(conn->u.user); + nprintf(conn, "Keepalive is %s\r", (conn->u.user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + if (_memicmp(&Buffer[1], "AUTOCHARSET", 4) == 0) + { + conn->u.user->rtflags ^= u_auto; + upduser(conn->u.user); + nprintf(conn, "Automatic Character set selection is %s\r", (conn->u.user->rtflags & u_auto) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + if (_memicmp(&Buffer[1], "UTF-8", 3) == 0) + { + conn->u.user->rtflags ^= u_noUTF8; + upduser(conn->u.user); + nprintf(conn, "Character set is %s\r", (conn->u.user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if ((_memicmp(&Buffer[1], "CodePage", 3) == 0) || (_memicmp(&Buffer[1], "CP", 2) == 0)) + { + char * Context; + char * CP = strtok_s(&Buffer[1], " ,\r", &Context); +#ifndef WIN32 + iconv_t temp = NULL; +#else + int temp = 0; + WCHAR TempW[10]; +#endif + CP = strtok_s(NULL, " ,\r", &Context); + + if (CP == NULL || CP[0] == 0) + { +#ifndef WIN32 + if (conn->u.user->Codepage[0]) + nprintf(conn, "Codepage is %s\r", conn->u.user->Codepage); +#else + if (conn->u.user->Codepage) + nprintf(conn, "Codepage is %d\r", conn->u.user->Codepage); +#endif + else + nprintf(conn, "Codepage is not set\r"); + + return; + } + _strupr(CP); + +#ifndef WIN32 + + // Validate Code Page by trying to open an iconv descriptor + + temp = iconv_open("UTF-8", CP); + + if (temp == (iconv_t)-1) + { + nprintf(conn, "Invalid Codepage %s\r", CP); + return; + } + + iconv_close(conn->u.user->iconv_toUTF8); + iconv_close(conn->u.user->iconv_fromUTF8); + + conn->u.user->iconv_toUTF8 = temp; + conn->u.user->iconv_fromUTF8 = iconv_open(CP, "UTF-8"); + + strcpy(conn->u.user->Codepage, CP); + nprintf(conn, "Codepage set to %s\r", conn->u.user->Codepage); +#else + if (CP[0] == 'C') + CP +=2; + + // Validate by trying ot use it + + temp = atoi(CP); + + if (MultiByteToWideChar(temp, 0, "\r", 2, TempW, 10) == 0) + { + int err = GetLastError(); + + if (err == ERROR_INVALID_PARAMETER) + { + nprintf(conn, "Invalid Codepage %d\r", temp); + return; + } + } + + conn->u.user->Codepage = temp; + nprintf(conn, "Codepage set to %d\r", conn->u.user->Codepage); +#endif + upduser(conn->u.user); + + return; + } + + if (_memicmp(&Buffer[1], "Shownames", 4) == 0) + { + conn->u.user->rtflags ^= u_shownames; + upduser(conn->u.user); + nprintf(conn, "Shownames is %s\r", (conn->u.user->rtflags & u_shownames) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (_memicmp(&Buffer[1], "Time", 4) == 0) + { + conn->u.user->rtflags ^= u_showtime; + upduser(conn->u.user); + nprintf(conn, "Show Time is %s\r", (conn->u.user->rtflags & u_showtime) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (_memicmp(&Buffer[1], "colours", 4) == 0) + { + int i =0; + + while (i < 100) + { + nprintf(conn, "\x1b%c%02d XXXXX\r", i + 10, i); + i++; + if (i == 3) + i++; + } + return; + } + + rt_cmd(conn, Buffer); + + return; + } + + // Send message to all other connected users on same channel + + text_tellu(conn->u.user, Buffer, NULL, o_topic); // To local users. + + conn->u.user->lastrealmsgtime = conn->u.user->lastmsgtime = time(NULL); + + // Send to Linked nodes + + for (c = circuit_hd; c; c = c->next) + { + if ((c->rtcflags & p_linked) && c->refcnt && ct_find(c, conn->u.user->topic)) + nprintf(c, "%c%c%s %s %s\r", FORMAT, id_data, OurNode, conn->u.user->call, Buffer); + } +} + +void upduser(USER *user) +{ + FILE *in, *out; + char *c; + char Buffer[2048]; + char *buf = Buffer; + + in = fopen(RtUsr, "r"); + + if (!(in)) + { + in = fopen(RtUsr, "w"); + if (in) + fclose(in); + in = fopen(RtUsr, "r"); + } + + if (!(in)) return; + + out = fopen(RtUsrTemp, "w"); + + if (!(out)) return; + + while(fgets(buf, 128, in)) + { + if (strstr(buf, "*RTL")) // Tidy user database + continue; + + c = strchr(buf, ' '); + if (c) *c = '\0'; + if (!matchi(buf, user->call)) + { + if (c) *c = ' '; + fputs(buf, out); + } + } + +#ifndef WIN32 + fprintf(out, "%s %d %s %s¬%d¬%s\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); +#else + fprintf(out, "%s %d %s %s¬%d¬%d\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); +#endif + fclose(in); + fclose(out); + + remove(RtUsr); + rename(RtUsrTemp, RtUsr); +} + +char * lookupuser(char * call) +{ + FILE *in; + char *flags; + char Buffer[2048]; + char *buf = Buffer; + char * name; + + in = fopen(RtUsr, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + + flags = strlop(buf, ' '); + if (!matchi(buf, call)) continue; + if (!flags) break; + + fclose(in); + name = strlop(flags, ' '); + strlop(name, ' '); + return _strdup(name); + } + fclose(in); + } + + return NULL; +} + + + +void rduser(USER *user) +{ + FILE *in; + char *name, *flags, *qth; + char Buffer[2048]; + char *buf = Buffer; + char * ptr; + + user->name = _strdup("?_name"); + user->qth = _strdup("?_qth"); + + in = fopen(RtUsr, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + + flags = strlop(buf, ' '); + if (!matchi(buf, user->call)) continue; + if (!flags) break; + + name = strlop(flags, ' '); + user->rtflags = atoi(flags); + + qth = strlop(name, ' '); + strnew(&user->name, name); + + if (!qth) break; + + // Colour Code may follow QTH, and Code Page may follow Colour + + ptr = strchr(qth, '¬'); + if (ptr) + { + *ptr++ = 0; + user->Colour = atoi(ptr); + + ptr = strchr(ptr, '¬'); + + if (ptr) + { + *ptr++ = 0; +#ifndef WIN32 + strcpy(user->Codepage, ptr); +#else + user->Codepage = atoi(ptr); +#endif + } + } + + strnew(&user->qth, qth); + break; + } + fclose(in); + +#ifndef WIN32 + + // Open an iconv decriptor for each conversion + + if (user->Codepage[0]) + user->iconv_toUTF8 = iconv_open("UTF-8", user->Codepage); + else + user->iconv_toUTF8 = (iconv_t)-1; + + if (user->iconv_toUTF8 == (iconv_t)-1) + user->iconv_toUTF8 = iconv_open("UTF-8", "CP1252"); + + + if (user->Codepage[0]) + user->iconv_fromUTF8 = iconv_open(user->Codepage, "UTF-8"); + else + user->iconv_fromUTF8 = (iconv_t)-1; + + if (user->iconv_fromUTF8 == (iconv_t)-1) + user->iconv_fromUTF8 = iconv_open("CP1252", "UTF-8"); +#endif + } +} + + +void ReportBadJoin(char * ncall, char *ucall) +{ + Logprintf(LOG_CHAT, NULL, '!', "User %s Join from Node %s but already connected", ucall, ncall); +} + +void ReportBadLeave(char * ncall, char * ucall) +{ + Logprintf(LOG_CHAT, NULL, '!', "Node %s reporting Node %s as a leaving user", ncall, ucall); +} + + +struct DUPINFO DupInfo[MAXDUPS]; + +static BOOL CheckforDups(ChatCIRCUIT * circuit, char * Call, char * Msg) +{ + // Primitive duplicate suppression - see if same call and text reeived in last few secons + + time_t Now = time(NULL); + time_t DupCheck = Now - DUPSECONDS; + int i, saveindex = -1; + + for (i = 0; i < MAXDUPS; i++) + { + if (DupInfo[i].DupTime < DupCheck) + { + // too old - use first if we need to save it + + if (saveindex == -1) + { + saveindex = i; + } + continue; + } + + if ((strcmp(Call, DupInfo[i].DupUser) == 0) && (memcmp(Msg, DupInfo[i].DupText, strlen(DupInfo[i].DupText)) == 0)) + { + // Duplicate, so discard, but save time + + DupInfo[i].DupTime = Now; + Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s supressed", Call, Msg); + + return TRUE; // Duplicate + } + + } + + // Not in list + + if (saveindex == -1) // List is full + saveindex = MAXDUPS - 1; // Stick on end + + DupInfo[saveindex].DupTime = Now; + strcpy(DupInfo[saveindex].DupUser, Call); + + if (strlen(Msg) > 99) + { + memcpy(DupInfo[saveindex].DupText, Msg, 99); + DupInfo[saveindex].DupText[99] = 0; + } + else + strcpy(DupInfo[saveindex].DupText, Msg); + + return FALSE; +} + +void chkctl(ChatCIRCUIT *ckt_from, char * Buffer, int Len) +{ + CHATNODE * node, *ln; + ChatCIRCUIT * ckt_to; + USER * user, * su; + time_t Now = time(NULL); + LINK * Link = ckt_from->u.link; + + char * ncall, * ucall, * f1, * f2, * buf; + int i; + + if (Buffer[FORMAT_O] != FORMAT) return; // Not a control message. + + // Check for corruption + + for (i = 1; i < (Len - 1); i++) + { + if (Buffer[i] < 32) + { + if (Buffer[i] == 9) + { + Buffer[i] = 32; + continue; + } + Debugprintf("Corrupt Chat Link Messages %s", Buffer); + return; + } + } + + buf = _strdup(Buffer + DATA_O); + +// FORMAT and TYPE bytes are followed by node and user callsigns. + + ncall = buf; + ucall = strlop(buf, ' '); + if (!ucall) { free(buf); return; } // Not a control message. + +// There may be at least one field after the node and user callsigns. +// Node leave (id_unlink) has no F1. + + f1 = strlop(ucall, ' '); + strlop(ucall, 9); // some have tabs ?? + +// If the frame came from an unknown node ignore it. +// If the frame came from us ignore it (loop breaking). + + node = node_find(ncall); + if (!node || matchi(ncall, OurNode)) { free(buf); return; } + + if (ckt_from->rtcflags & p_linked) + ckt_from->u.link->lastMsgReceived = Now; + + switch(Buffer[TYPE_O]) + { + // Data from user ucall at node ncall. + + case id_data : + + // Check for dups + + if (CheckforDups(ckt_from, ucall, f1)) + break; + + user = user_find(ucall, ncall); + + if (!user) + break; + + user->lastrealmsgtime = user->lastmsgtime = time(NULL); + + text_tellu(user, f1, NULL, o_topic); + + for (ckt_to = circuit_hd; ckt_to; ckt_to = ckt_to->next) + { + if ((ckt_to->rtcflags & p_linked) && ckt_to->refcnt && + !cn_find(ckt_to, node) && ct_find(ckt_to, user->topic)) + nprintf(ckt_to, "%s\r", Buffer); + } + break; + + // User ucall at node ncall changed their Name/QTH info. + + case id_user : + + user = user_find(ucall, ncall); + if (!user) break; + f2 = strlop(f1, ' '); + if (!f2) break; + + if ((strcmp(user->name, f1) == 0) && (strcmp(user->qth, f2) == 0)) // No Change? + break; + + echo(ckt_from, node, Buffer); // Relay to other nodes. + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); + break; + + // User ucall logged into node ncall. + + case id_join : + + user = user_find(ucall, ncall); + + if (user) + { + // Already Here + + // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" + + if (time(NULL) - user->timeconnected > 5) + ReportBadJoin(ncall, ucall); + + //if (strcmp(user->node->call, OurNode) == 0) + //{ + // Locally connected, and at another node + //} + + user->timeconnected = time(NULL); + break; // We have this user as an active Node + } + + // update join time + + echo(ckt_from, node, Buffer); // Relay to other nodes. + f2 = strlop(f1, ' '); + if (!f2) break; + user = user_join(ckt_from, ucall, ncall, NULL, FALSE); + if (!user) break; + ckt_from->refcnt++; + text_tellu_Joined(user); + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); +// makelinks(); // Bring up our links if not already up + + break; + + // User ucall logged out of node ncall. + + case id_leave : + + user = user_find(ucall, ncall); + if (!user) + { + Debugprintf("CHAT: Leave for %s from %s when not on list", ucall, ncall); + break; + } + + // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" + // we can't just silently leave as next join will propagate + + if (time(NULL) - user->timeconnected < 3) + break; + + echo(ckt_from, node, Buffer); // Relay to other nodes. + + f2 = strlop(f1, ' '); + if (!f2) break; + + text_tellu(user, rtleave, NULL, o_all); + ckt_from->refcnt--; + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); + user_leave(user); + + cn_dec(ckt_from, node); + node_dec(node); + + break; + + // Node ncall lost its link to node ucall, alias f1. + + case id_unlink : + + // Only relay to other nodes if we had node. Could get loop otherwise. + // ?? This could possibly cause stuck nodes + + ln = node_find(ucall); + + // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" + // we can't just silently leave as next join will propagate + + if (ln) + { + if (time(NULL) - ln->timeconnected < 3) + break; + + // is it on this circuit? + + if (cn_find(ckt_from, ln)) + { + cn_dec(ckt_from, ln); + node_dec(ln); + echo(ckt_from, node, Buffer); // Relay to other nodes if we had node. COuld get loop if + } + else + { + Debugprintf("CHAT: node %s unlink for %s when not on this link", ncall, ucall); + } + } + else + { + Debugprintf("CHAT: node %s unlink for %s when not on list", ncall, ucall); + } + + break; + + // Node ncall acquired a link to node ucall, alias f1. + // If we are not linked, is no problem, don't link. + // If we are linked, is a loop, do what? (Try ignore!) + + case id_link : + + ln = node_find(ucall); + + if (!ln && !matchi(ncall, OurNode)) + { + f2 = strlop(f1, ' '); + cn_inc(ckt_from, ucall, f1, f2); + echo(ckt_from, node, Buffer); // Relay to other nodes. + } + else + { + // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" + + if (time(NULL) - ln->timeconnected > 5) + Debugprintf("CHAT: node %s link for %s when already on list", ncall, ucall); + + // update join time + + ln->timeconnected = time(NULL); + break; + } + + break; + + // User ucall at node ncall sent f2 to user f1. + + case id_send : + user = user_find(ucall, ncall); + if (!user) break; + f2 = strlop(f1, ' '); + if (!f2) break; + su = user_find(f1, NULL); + if (!su) break; + + if (su->circuit->rtcflags & p_user) + text_tellu(user, f2, f1, o_one); + else + echo(ckt_from, node, Buffer); // Relay to other nodes. + break; + + // User ucall at node ncall changed topic. + + case id_topic : + user = user_find(ucall, ncall); + if (user) + { + if (_stricmp(user->topic->name, f1) != 0) + { + echo(ckt_from, node, Buffer); // Relay to other nodes. + topic_chg(user, f1); + } + } + break; + + + case id_keepalive : + + ln = node_find(ncall); + if (ln) + { + if (ln->Version == NULL) + if (f1) + ln->Version = _strdup(f1); + } + + nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); + break; + + case id_poll: + + // Send Poll Response + + Link->supportsPolls = Now; + nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); + break; + + case id_pollresp: + + Link->supportsPolls = Now; + Link->RTT = Now - Link->timePollSent; + Link->timePollSent = 0; // Cancel Timeout + break; + + default: + break; + } + + free(buf); +} + +// Tell another node about nodes known by this node. +// Do not tell it about this node, the other node knows who it +// linked to (or who linked to it). +// Tell another node about users known by this node. +// Done at incoming or outgoing link establishment. + +void state_tell(ChatCIRCUIT *circuit, char * Version) +{ + CHATNODE *node; + USER *user; + + node = cn_inc(circuit, circuit->u.link->call, circuit->u.link->alias, Version); + node_tell(node, id_link); // Tell other nodes about this new link + + // Tell the node that just linked here about nodes known on other links. + + for (node = node_hd; node; node = node->next) + { + if (!matchi(node->call, OurNode)) + node_xmit(node, id_link, circuit); + } + + // Tell the node that just linked here about known users, and their topics. + + for (user = user_hd; user; user = user->next) + { + user_xmit(user, id_join, circuit); + topic_xmit(user, circuit); + } +} + +static void circuit_free(ChatCIRCUIT *circuit) +{ + ChatCIRCUIT *c, *cp; + CN *ncn; + CHATNODE *nn; + TOPIC *tn; + + cp = NULL; + + for (c = circuit_hd; c; cp = c, c = c->next) + { + if (c == circuit) + { + if (cp) cp->next = c->next; else circuit_hd = c->next; + + while (c->hnode) + { + ncn = c->hnode->next; + free(c->hnode); + c->hnode = ncn; + } + + break; + } + } + + if (circuit_hd) return; + +// RT has gone inactive. Clean up. + + while (node_hd) + { + nn = node_hd->next; + free(node_hd->alias); + free(node_hd->call); + free(node_hd); + node_hd = nn; + } + + while (topic_hd) + { + tn = topic_hd->next; + free(topic_hd->name); + free(topic_hd); + topic_hd = tn; + } +} + + +// Find a node in the node list. + +CHATNODE *node_find(char *call) +{ + CHATNODE *node; + + for (node = node_hd; node; node = node->next) + { + //if (node->refcnt && matchi(node->call, call)) I don't think this is right!!! + if (matchi(node->call, call)) + break; + } + + return node; +} + +// Add a reference to a node. + +static CHATNODE *node_inc(char *call, char *alias, char * Version) +{ + CHATNODE *node; + + node = node_find(call); + + if (!node) + { + knownnode_add(call); + + node = zalloc(sizeof(CHATNODE)); + sl_ins_hd(node, node_hd); + node->call = _strdup(call); + node->alias = _strdup(alias); + if (Version) + node->Version = _strdup(Version); + + node->timeconnected = time(NULL); + +// Debugprintf("New Node Rec Created at %x for %s %s", node, node->call, node->alias); + } + + node->refcnt++; + return node; +} + +// Remove a reference to a node. + +static void node_dec(CHATNODE *node) +{ + CHATNODE *t, *tp; + USER *user; + + ChatCIRCUIT *circuit; + CN *cn; + + if (--node->refcnt) return; // Other references. + + // Remove the node from the node list. + + tp = NULL; + + // Make sure there aren't any user or circuit records pointing to it + + for (user = user_hd; user; user = user->next) + { + if (user->node == node) + { + Debugprintf("Trying to remove node %s that is linked from user %s", node->call, user->call); + node->refcnt++; + } + } + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + { + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + { + Debugprintf("Trying to remove node %s that is linked from circuit %s", node->call, circuit->Callsign); + node->refcnt++; + } + } + } + } + + if (node->refcnt) return; // Now have other references. + + for (t = node_hd; t; tp = t, t = t->next) + { + if (t == node) + { + if (tp) tp->next = t->next; else node_hd = t->next; + free(t->alias); + t->alias = NULL; + free(t->call); + t->call = NULL; + free(t); + break; + } + } +} + +// User joins a topic. + +static TOPIC *topic_join(ChatCIRCUIT *circuit, char *s) +{ + CT *ct; + TOPIC *topic; + +// Look for an existing topic. + + for (topic = topic_hd; topic; topic = topic->next) + { + if (matchi(topic->name, s)) + break; + } + +// Create a new topic, if needed. + + if (!topic) + { + topic = zalloc(sizeof(TOPIC)); + sl_ins_hd(topic, topic_hd); + topic->name = _strdup(s); + } + + topic->refcnt++; // One more user in this topic. + + Logprintf(LOG_CHAT, circuit, '?', "topic_join complete user %s topic %s addr %x ref %d", + circuit->u.user->call, topic->name, topic, topic->refcnt); + + +// Add the circuit / topic association. + + for (ct = circuit->topic; ct; ct = ct->next) + { + if (ct->topic == topic) + { + ct->refcnt++; + return topic; + } + } + + ct = zalloc(sizeof(CT)); + sl_ins_hd(ct, circuit->topic); + ct->topic = topic; + ct->refcnt = 1; + return topic; +} + +// User leaves a topic. + +static void topic_leave(ChatCIRCUIT *circuit, TOPIC *topic) +{ + CT *ct, *ctp; + TOPIC *t, *tp; + + Logprintf(LOG_CHAT, circuit, '?', "topic_leave user %s topic %s addr %x ref %d", + circuit->u.user->call, topic->name, topic, topic->refcnt); + + topic->refcnt--; + + ctp = NULL; + + for (ct = circuit->topic; ct; ctp = ct, ct = ct->next) + { + if (ct->topic == topic) + { + if (!--ct->refcnt) + { + if (ctp) ctp->next = ct->next; else circuit->topic = ct->next; + free(ct); + break; + } + } + } + + tp = NULL; + + for (t = topic_hd; t; tp = t, t = t->next) + { + if (!t->refcnt && (t == topic)) + { + if (tp) tp->next = t->next; else topic_hd = t->next; + free(t->name); + free(t); + break; + } + } +} + +// Find a circuit/topic association. + +int ct_find(ChatCIRCUIT *circuit, TOPIC *topic) +{ + CT *ct; + + for (ct = circuit->topic; ct; ct = ct->next) + { + if (ct->topic == topic) + return ct->refcnt; + } + return 0; +} + +// Nodes reached from each circuit. Used only if the circuit is a link. + +// Remove a circuit/node association. + +static void cn_dec(ChatCIRCUIT *circuit, CHATNODE *node) +{ + CN *c, *cp; + +// Debugprintf("CHAT: Remove c/n %s ", node->call); + + cp = NULL; + + for (c = circuit->hnode; c; cp = c, c = c->next) + { + if (c->node == node) + { +// CN * cn; +// int len; +// char line[1000]=""; + + if (--c->refcnt) + { +// Debugprintf("CHAT: Remove c/n Node %s still in use refcount %d", node->call, c->refcnt); + return; // Still in use + } + + if (cp) + cp->next = c->next; + else + circuit->hnode = c->next; + + free(c); + + break; + } + } + + if (c == NULL) + { + CN * cn; + int len; + char line[1000]=""; + + // not found?? + + Debugprintf("CHAT: !! Remove c/n Node %s addr %x not found cn chain follows", node->call, node); + + line[0] = 0; + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->call) + { +#ifndef LINBPQ + __try + { +#endif + len = sprintf(line, "%s %p %s", line, cn->node, cn->node->alias); + if (len > 80) + { + Debugprintf("%s", line); + len = sprintf(line, " "); + } +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len = sprintf("%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);} +#endif + } + else + { + len = sprintf("%s Corrupt Rec %x %x ", line, cn, cn->node); + } + } + Debugprintf("%s", line); + + } + + +} + +// Add a circuit/node association. + +static CHATNODE *cn_inc(ChatCIRCUIT *circuit, char *call, char *alias, char * Version) +{ + CHATNODE *node; + CN *cn; + + node = node_inc(call, alias, Version); + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + { + cn->refcnt++; +// Debugprintf("cn_inc cn Refcount for %s->%s incremented to %d - adding Call %s", +// circuit->Callsign, node->call, cn->refcnt, call); + + return node; + } + } + + cn = zalloc(sizeof(CN)); + sl_ins_hd(cn, circuit->hnode); + cn->node = node; + cn->refcnt = 1; + +// Debugprintf("cn_inc New cn for %s->%s - adding Call %s", +// circuit->Callsign, node->call, call); + + return node; +} + +// Find a circuit/node association. + +static int cn_find(ChatCIRCUIT *circuit, CHATNODE *node) +{ + CN *cn; + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + return cn->refcnt; + } + return 0; +} + +// From a local user to a specific user at another node. + +static void text_xmit(USER *user, USER *to, char *text) +{ + nprintf(to->circuit, "%c%c%s %s %s %s\r", + FORMAT, id_send, OurNode, user->call, to->call, text); +} + +void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) +{ + UCHAR BufferB[4096]; + + // Text is UTF-8 internally. If use doen't want UTF-8. convert to Node's locale + + if (circuit->u.user->rtflags & u_noUTF8) + { +#ifdef WIN32 + char * Buffer = buf; + WCHAR BufferW[4096]; + int wlen, blen; + BOOL DefaultUsed = FALSE; + char Subst = '?'; + + wlen = MultiByteToWideChar(CP_UTF8, 0, buf, (int)strlen(buf) + 1, BufferW, 4096); + blen = WideCharToMultiByte(circuit->u.user->Codepage, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); + + if (blen == 0) // Probably means invalid code page + blen = WideCharToMultiByte(CP_ACP, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); + + buf = BufferB + 2; + BufferB[blen + 2] = 0; +#else + + int left = 4096; + UCHAR * BufferBP = BufferB; + int len = strlen(buf) + 1; + struct user_t * icu = circuit->u.user; + + if (icu->iconv_fromUTF8 == NULL) + { + icu->iconv_fromUTF8 = iconv_open(icu->Codepage, "UTF-8"); + + if (icu->iconv_fromUTF8 == (iconv_t)-1) + icu->iconv_fromUTF8 = iconv_open("CP1252", "UTF-8"); + } + + iconv(icu->iconv_fromUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu->iconv_fromUTF8, (char ** __restrict__)&buf, &len, (char ** __restrict__)&BufferBP, &left); + + len = 4096 - left; + buf = BufferB; + +#endif + + } + + + if (circuit->u.user->rtflags & u_colour) // Use Colour + { + // Put a colour header on message + + *(--buf) = user->Colour; + *(--buf) = 0x1b; + nputs(circuit, buf); + buf +=2; + } + else + nputs(circuit, buf); + + + + circuit->u.user->lastsendtime = time(NULL); +} + +void text_tellu(USER *user, char *text, char *to, int who) +{ + ChatCIRCUIT *circuit; + UCHAR Buffer[2048]; + UCHAR *buf = &Buffer[4]; + char * Time; + struct tm * tm; + char Stamp[20]; + time_t T; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + +// Send it to all connected users in the same topic. +// Echo to originator if requested. + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. + + if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; + + if (circuit->u.user->rtflags & u_showtime) + Time = Stamp; + else + Time = ""; + + if (circuit->u.user->rtflags & u_shownames) + sprintf(buf, "%s%-6.6s %s %c %s\r", Time, user->call, user->name, (who == o_one) ? '>' : ':', text); + else + sprintf(buf, "%s%-6.6s %c %s\r", Time, user->call, (who == o_one) ? '>' : ':', text); + + + switch(who) + { + case o_topic : + if (circuit->u.user->topic == user->topic) + put_text(circuit, user, buf); // Send adding Colour if wanted + + break; + + case o_all: + + put_text(circuit, user, buf); // Send adding Colour if wanted + + break; + + case o_one : + if (matchi(circuit->u.user->call, to)) + put_text(circuit, user, buf); // Send adding Colour if wanted + break; + } + } +} + +extern int FlashOnConnect; + +void text_tellu_Joined(USER * user) +{ + ChatCIRCUIT *circuit; + UCHAR Buffer[200]; + UCHAR *buf = &Buffer[4]; + char * Time; + struct tm * tm; + char Stamp[20]; + time_t T; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + + sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Stamp, user->call, user->name, user->topic->name); + +// Send it to all connected users in the same topic. +// Echo to originator if requested. + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. + if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; + + if (circuit->u.user->rtflags & u_showtime) + Time = Stamp; + else + Time = ""; + + sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Time, user->call, user->name, user->topic->name); + + put_text(circuit, user, buf); // Send adding Colour if wanted + + if (circuit->u.user->rtflags & u_bells) + if (circuit->BPQStream < 0) // Console + { +#ifndef LINBPQ + if (FlashOnConnect) FlashWindow(ConsHeader[1]->hConsole, TRUE); +#endif + nputc(circuit, 7); +// PlaySound ("BPQCHAT_USER_LOGIN", NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC); + } + else + nputc(circuit, 7); + + nputc(circuit, 13); + } +} +// Tell one link circuit about a local user change of topic. + +static void topic_xmit(USER *user, ChatCIRCUIT *circuit) +{ + nprintf(circuit, "%c%c%s %s %s\r", + FORMAT, id_topic, OurNode, user->call, user->topic->name); +} + +// Tell another node about one known node on a link add or drop +// if that node is from some other link. + +static void node_xmit(CHATNODE *node, char kind, ChatCIRCUIT *circuit) +{ +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; + + __try + { +#endif + if (!cn_find(circuit, node)) + if (node->Version && (kind == id_link)) + nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias, node->Version); + else + nprintf(circuit, "%c%c%s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias); + +#ifndef LINBPQ + } + + #define EXCEPTMSG "node_xmit" + #include "StdExcept.c" + + Debugprintf("Corrupt Rec %x %x %x", node, node->call, node->alias); + } +#endif +} + +// Tell all other nodes about one node known by this node. + +static void node_tell(CHATNODE *node, char kind) +{ + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + node_xmit(node, kind, circuit); + } +} + +// Tell another node about a user login/logout at this node. + +static void user_xmit(USER *user, char kind, ChatCIRCUIT *circuit) +{ + CHATNODE *node; + + node = user->node; + + if (!cn_find(circuit, node)) + nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, node->call, user->call, user->name, user->qth); +} + +// Tell all other nodes about a user login/logout at this node. + +static void user_tell(USER *user, char kind) +{ + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + user_xmit(user, kind, circuit); + } +} + +// Find the user record for call@node. Node can be NULL, meaning any node + +USER *user_find(char *call, char * node) +{ + USER *user; + + for (user = user_hd; user; user = user->next) + { + if (node) + { + if (matchi(user->call, call) && matchi(user->node->call, node)) + break; + } + else + { + if (matchi(user->call, call)) + break; + } + } + + return user; +} + +static void user_leave(USER *user) +{ + USER *t, *tp; + + topic_leave(user->circuit, user->topic); + + tp = NULL; + + for (t = user_hd; t; tp = t, t = t->next) + { + if (t == user) + { + if (tp) tp->next = t->next; else user_hd = t->next; + + free(t->name); + free(t->call); + free(t->qth); +#ifndef WIN32 + if (t->iconv_fromUTF8) + iconv_close(t->iconv_fromUTF8); + if (t->iconv_toUTF8) + iconv_close(t->iconv_toUTF8); +#endif + free(t); + break; + } + } + + if (user_hd == NULL) + ChatTmr = 59; // If no users, disconnect links after 10-20 secs +} + +// User changed to a different topic. + +static BOOL topic_chg(USER *user, char *s) +{ + char buf[128]; + + if (_stricmp(user->topic->name, s) == 0) return FALSE; // Not Changed + + sprintf(buf, "*** Left Topic: %s", user->topic->name); + text_tellu(user, buf, NULL, o_topic); // Tell everyone in the old topic. + topic_leave(user->circuit, user->topic); + user->topic = topic_join(user->circuit, s); + sprintf(buf, "*** Joined Topic: %s", user->topic->name); + text_tellu(user, buf, NULL, o_topic); // Tell everyone in the new topic. + + return TRUE; +} + +// Create a user record for this user. + +static USER *user_join(ChatCIRCUIT *circuit, char *ucall, char *ncall, char *nalias, BOOL Local) +{ + CHATNODE *node; + USER *user; + + if (Local) + { + node = cn_inc(circuit, ncall, nalias, Verstring); + } + else + node = cn_inc(circuit, ncall, nalias, NULL); + +// Is this user already logged in at this node? + + for (user = user_hd; user; user = user->next) + { + if (matchi(user->call, ucall) && (user->node == node)) + return user; + } + +// User is not logged in, create a user record for them. + + user = zalloc(sizeof(USER)); + sl_ins_hd(user, user_hd); + user->circuit = circuit; + user->call = _strdup(ucall); + _strupr(user->call); + user->node = node; + rduser(user); + + if (user->Colour == 0 || user->Colour == 11) // None or default + { + // Allocate Random + int sum = 0, i; + + for (i = 0; i < 9; i++) + sum += user->call[i]; + sum %= 20; + + user->Colour = AutoColours[sum] + 10; // Best 20 colours + } + + if (circuit->rtcflags & p_user) + circuit->u.user = user; + + user->timeconnected = user->lastrealmsgtime = user->lastmsgtime = time(NULL); + + user->topic = topic_join(circuit, deftopic); + return user; +} + +// Link went away. We dropped it, or the other node dropped it. +// Drop nodes and users connected from this link. +// Tell other (still connected) links what was dropped. + +void link_drop(ChatCIRCUIT *circuit) +{ + USER *user, *usernext; + CN *cn; + +// So we don't try and send anything on this circuit. + + if (circuit->u.link) + if (circuit->rtcflags == p_linkini) + Debugprintf("Chat link %s Link Setup Failed", circuit->u.link->call); + + if (circuit->rtcflags == p_linkini) + circuit->u.link->flags = p_linkfailed; + else + circuit->u.link->flags = 0; + + circuit->rtcflags = p_nil; + +// Users connected on the dropped link are no longer connected. + + for (user = user_hd; user; user = usernext) + { + usernext = user->next; // Save next pointer in case entry is free'd + + if (user->circuit == circuit) + { + CHATNODE *node; + + node = user->node; + + text_tellu(user, rtleave, NULL, o_all); + user_tell(user, id_leave); + user_leave(user); + + circuit->refcnt--; + if (node) + node_dec(node); + } + } + +// Any node known from the dropped link is no longer known. + + for (cn = circuit->hnode; cn; cn = cn->next) + { + node_tell(cn->node, id_unlink); + node_dec(cn->node); + } + +// The circuit is no longer used. + + circuit_free(circuit); + NeedStatus = TRUE; +} + +// Handle an incoming control frame from a linked RT system. + +static void echo(ChatCIRCUIT *fc, CHATNODE *node, char * Buffer) +{ + ChatCIRCUIT *tc; + + for (tc = circuit_hd; tc; tc = tc->next) + { + if ((tc != fc) && (tc->rtcflags & p_linked) && !cn_find(tc, node)) + nprintf(tc, "%s\r", Buffer); + } +} + +char ** SeparateConnectScript(char * MultiString) +{ + char * ptr1 = MultiString; + char ** Value; + int Count = 0; + char * ptr; + + // Convert to string array + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + ptr = MultiString; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) + { + Value = realloc(Value, (Count + 2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + +// Add an entry to list of link partners + +int rtlink (char * Call) +{ + LINK *link, *temp; + char *c; + char * script; + + _strupr(Call); + script = strlop(Call, '|'); + + c = strlop(Call, ':'); + if (!c) return FALSE; + + link = zalloc(sizeof(LINK)); + + link->alias = _strdup(Call); + link->call = _strdup(c); + + if (script) + { + link->ConnectScript = SeparateConnectScript(script); + link->Lines = 0; + while (link->ConnectScript[++link->Lines]); + } + else + { + // Create Script with one entry to call partner direct; + + link->ConnectScript = zalloc(sizeof(void *) * 2); // always NULL entry on end + link->ConnectScript[0] = malloc(32); + sprintf(link->ConnectScript[0], "C %s", c); + link->Lines = 1; + } + + if (link_hd == NULL) + link_hd = link; + else + { + temp = link_hd; + while(temp->next) + temp = temp->next; + + temp->next = link; + } + + return TRUE; +} + +VOID removelinks() +{ + LINK *link, *nextlink; + + for (link = link_hd; link; link = nextlink) + { + nextlink = link->next; + + if (link->ConnectScript) + { + int n = 0; + while(link->ConnectScript[n]) + free(link->ConnectScript[n++]); + + free(link->ConnectScript); + } + + free(link->alias); + link->alias = 0; + free(link->call); + link->call = 0; + free(link); + link = 0; + } + link_hd = NULL; +} +VOID removeknown() +{ + // Save Known Nodes list and free struct + + KNOWNNODE *node, *nextnode; + FILE *out; + + out = fopen(RtKnown, "w"); + + if (!out) + return; + + for (node = known_hd; node; node = nextnode) + { + fprintf(out, "%s %u\n", node->call, (unsigned int)node->LastHeard); + + nextnode = node->next; + free(node->call); + free(node); + } + known_hd = NULL; + + fclose(out); +} + +VOID LoadKnown() +{ + // Reload Known Nodes list + + FILE *in; + char buf[128]; + char * ptr; + + in = fopen(RtKnown, "r"); + + if (in == NULL) + return; + + while(fgets(buf, 128, in)) + { + ptr = strchr(buf, ' '); + if (ptr) + { + *(ptr) = 0; + knownnode_add(buf); + } + } + + fclose(in); +} + +// We don't allocate memory for circuit, but we do chain it + +ChatCIRCUIT *circuit_new(ChatCIRCUIT *circuit, int flags) +{ + // Make sure circuit isn't already on list + + ChatCIRCUIT *c; + + circuit->rtcflags = flags; + circuit->next = NULL; + + for (c = circuit_hd; c; c = c->next) + { + if (c == circuit) + { + Debugprintf("CHAT: Attempting to add Circuit when already on list"); + return circuit; + } + } + + sl_ins_hd(circuit, circuit_hd); + + return circuit; +} + +// Handle an incoming link. We should only get here if we think the station is a node. + +int rtloginl (ChatCIRCUIT *conn, char * call) +{ + LINK * link; + + if (node_find(call)) + { + Logprintf(LOG_CHAT, conn, '|', "Refusing link from %s to %s to prevent a loop", conn->Callsign, OurNode); + + nprintf(conn, "Refusing link from %s to %s to prevent a loop.\n", conn->Callsign, OurNode); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + return FALSE; // Already linked. + } + + for (link = link_hd; link; link = link->next) + { + if (matchi(call, link->call)) + break; + } + + if (!link) + { + // We don't link with this system. Shouldn't happen, as we checked earlier + + nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + return FALSE; + } + + if (link->flags & (p_linked | p_linkini)) + { + // Already Linked. Used to Disconnect, but that can cause sync errors + // Try closing old link and keeping new + + ChatCIRCUIT *c; + int len; + char Msg[80]; + + for (c = circuit_hd; c; c = c->next) + { + if (c->u.link == link) + { + len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Connect when Connected - Old Connection Closed", call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + + c->Active = FALSE; // So we don't try to clear circuit again + Disconnect(c->BPQStream); + link_drop(c); + RefreshMainWindow(); + break; + } + } + } + +// Accept the link request. + + circuit_new(conn, p_linked); + conn->u.link = link; + nputs(conn, "OK\r"); + link->flags = p_linked; + link->delay = 0; // Dont delay first restart + state_tell(conn, NULL); + conn->u.link->timePollSent = time(NULL); // Keepalive is a poll + nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); + + NeedStatus = TRUE; + + return TRUE; +} + +// User connected to chat, or did chat command from BBS + +int rtloginu (ChatCIRCUIT *circuit, BOOL Local) +{ + USER *user; + +// Is this user already logged in to RT somewhere else? + + user = user_find(circuit->UserPointer->Call, NULL); + + if (user) + { + // if connected at this node, kill old connection and allow new login + + if (user->node == node_find(OurNode)) + { + nputs(circuit, "*** Already connected at this node - old session will be closed.\r"); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + } + else + { + Disconnect(user->circuit->BPQStream); + } + } + else + nputs(circuit, "*** Already connected at another node.\r"); + + return FALSE; + } + +// Create the user entry. + + circuit_new(circuit, p_user); + + user = user_join(circuit, circuit->UserPointer->Call, OurNode, OurAlias, Local); + circuit->u.user = user; + + if (strcmp(user->name, "?_name") == 0) + { + user->name = _strdup(circuit->UserPointer->Name); + } + upduser(user); + + ChatExpandAndSendMessage(circuit, ChatWelcomeMsg, LOG_CHAT); + text_tellu_Joined(user); + user_tell(user, id_join); + show_users(circuit); + user->lastsendtime = time(NULL); +// makelinks(); + + return TRUE; +} + +void logout(ChatCIRCUIT *circuit) +{ + USER *user; + CHATNODE *node; + + circuit->rtcflags = p_nil; + user = circuit->u.user; + + if (user) // May not have logged in if already conencted + { + node = user->node; + + user_tell(user, id_leave); + text_tellu(user, rtleave, NULL, o_all); + user_leave(user); + + // order changed so node_dec can check if a node that is about the be deleted has eny users + + if (node) + { + cn_dec(circuit, node); + node_dec(node); + } + + circuit->u.user = NULL; + } + + circuit_free(circuit); +} + +void show_users(ChatCIRCUIT *circuit) +{ + USER *user; + char * Alias; + char * Topic; + + int i = 0; + + // First count them + + for (user = user_hd; user; user = user->next) + { + i++; + } + + nprintf(circuit, "%d Station(s) connected:\r", i); + + for (user = user_hd; user; user = user->next) + { + if ((user->node == 0) || (user->node->alias == 0)) + Alias = "(Corrupt Alias)"; + else + Alias = user->node->alias; + + if ((user->topic == 0) || (user->topic->name == 0)) + Topic = "(Corrupt Topic)"; + else + Topic = user->topic->name; + +#ifndef LINBPQ + __try + { +#endif + if (circuit->u.user->rtflags & u_colour) // Use Colour + nprintf(circuit, "\x1b%c%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", + user->Colour, user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); + else + nprintf(circuit, "%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", + user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + Debugprintf("MAILCHAT *** Program Error in show_users"); + CheckProgramErrors(); + } +#endif + } +} + + +static void show_nodes(ChatCIRCUIT *circuit) +{ + CHATNODE *node; + + nputs(circuit, "Known Nodes:\r"); + + for (node = node_hd; node; node = node->next) + { + if (node->refcnt) + if (node->Version) + nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, node->Version, node->refcnt); + else + nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, "Not Known", node->refcnt); + } +} + +// /P Command: List circuits and remote RT on them. + +#define xxx "\r " + +static void show_circuits(ChatCIRCUIT *conn, char Flag) +{ + ChatCIRCUIT *circuit; + CHATNODE *node; + LINK *link; + char line[1000]; + int len; + CN *cn; + + int i = 0; + + // First count them + + for (node = node_hd; node; node = node->next) + { + i++; + } + + nprintf(conn, "%d Node(s)\r", i); + + if (Flag == 'c') + sprintf(line, "Here %-6.6s <-", OurNode); + else + sprintf(line, "Here %-6.6s <-", OurAlias); + + for (node = node_hd; node; node = node->next) if (node->refcnt) + { + if (Flag == 'c') + len = sprintf(line, "%s %s", line, node->call); + else + len = sprintf(line, "%s %s", line, node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + + nprintf(conn, "%s\r", line); + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + { + if (Flag == 'c') + len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->call, circuit->refcnt); + else + len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->alias, circuit->refcnt); + +#ifndef LINBPQ + __try{ + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->alias) + { + __try + { + if (Flag == 'c') + len = sprintf(line, "%s %s", line, cn->node->call); + else + len = sprintf(line, "%s %s", line, cn->node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len = sprintf(line, "%s *PE* Corrupt Rec %x %x", line, cn, cn->node);} + } + else + len = sprintf(line, "%s Corrupt Rec %x %x ", line, cn, cn->node); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len = sprintf(line, "%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);} +#else + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->alias) + { + if (Flag == 'c') + len = sprintf(line, "%s %s", line, cn->node->call); + else + len = sprintf(line, "%s %s", line, cn->node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + else + len = sprintf(line, "%s Corrupt Rec %p %p ", line, cn, cn->node); + } +#endif + nprintf(conn, "%s\r", line); + + } + else if (circuit->rtcflags & p_user) + nprintf(conn, "User %-6.6s\r", circuit->u.user->call); + else if (circuit->rtcflags & p_linkini) + { + if (circuit->u.link) + { if (Flag == 'c') + nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->call); + else + nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->alias); + + } + else + nprintf(conn, "Link ?? (setup)\r"); + } + } + + nprintf(conn, "Links Defined:\r"); + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked ) + if (link->supportsPolls) + nprintf(conn, " %-10.10s Open RTT %d\r", link->call, link->RTT); + else + nprintf(conn, " %-10.10s Open\r", link->call); + else if (link->flags & (p_linked | p_linkini)) + nprintf(conn, " %-10.10s Connecting\r", link->call); + else if (link->flags & p_linkfailed) + nprintf(conn, " %-10.10s Connect failed\r", link->call); + else + nprintf(conn, " %-10.10s Idle\r", link->call); + } +} + +// /T Command: List topics and users in them. + +static void show_topics(ChatCIRCUIT *conn) +{ + TOPIC *topic; + USER *user; + + nputs(conn, "Active Topics are:\r"); + + for (topic = topic_hd; topic; topic = topic->next) + { + nprintf(conn, "%s\r", topic->name); + + if (topic->refcnt) + { + nputs(conn, " "); + for (user = user_hd; user; user = user->next) + { + if (user->topic == topic) + nprintf(conn, " %s", user->call); + } + nputc(conn, '\r'); + } + } +} + +static void show_users_in_topic(ChatCIRCUIT *conn) +{ + TOPIC *topic; + USER *user; + + nputs(conn, "Users in Topic:\r"); + + topic = conn->u.user->topic; + { + if (topic->refcnt) + { + for (user = user_hd; user; user = user->next) + { + if (user->topic == topic) + nprintf(conn, "%s ", user->call); + } + nputc(conn, '\r'); + } + } +} + +// Do a user command. + +int rt_cmd(ChatCIRCUIT *circuit, char * Buffer) +{ + ChatCIRCUIT *c; + USER *user, *su; + char *f1, *f2; + + user = circuit->u.user; + +// user->lastsendtime = time(NULL); + + switch(tolower(Buffer[1])) + { + case 'a' : + user->rtflags ^= u_bells; + upduser(user); + nprintf(circuit, "Alert %s\r", (user->rtflags & u_bells) ? "Enabled" : "Disabled"); + return TRUE; + + case 'b' : return FALSE; + + case 'c' : + user->rtflags ^= u_colour; + upduser(user); + nprintf(circuit, "Colour Mode %s\r", (user->rtflags & u_colour) ? "Enabled" : "Disabled"); + return TRUE; + + case 'e' : + user->rtflags ^= u_echo; + upduser(user); + nprintf(circuit, "Echo %s\r", (user->rtflags & u_echo) ? "Enabled" : "Disabled"); + return TRUE; + + case 'f' : makelinks(); return TRUE; + + case 'h' : + case '?' : + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("chathelp.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + ChatQueueMsg(circuit, MsgBytes, Length); + free(Save); + } + else + { + nputs(circuit, "Commands can be in upper or lower case.\r"); + nputs(circuit, "/U - Show Users.\r/N - Enter your Name.\r/Q - Enter your QTH.\r/T - Show Topics.\r"); + nputs(circuit, "/T Name - Join Topic or Create new Topic. Topic Names are not case sensitive\r/P - Show Ports and Links.\r"); + nprintf(circuit, "/A - Toggle Alert on user join - %s.\r", + (user->rtflags & u_bells) ? "Enabled" : "Disabled"); + nprintf(circuit, "/C - Toggle Colour Mode on or off (only works on Console or BPQTerminal - %s.\r", + (user->rtflags & u_colour) ? "Enabled" : "Disabled"); + nputs(circuit, "/Codepage CPnnnn - Set Codepage to use if UTF-8 is disabled.\r"); + nprintf(circuit, "/E - Toggle Echo - %s .\r", + (user->rtflags & u_echo) ? "Enabled" : "Disabled"); + nprintf(circuit, "/Keepalive - Toggle sending Keepalive messages every 10 minutes - %s.\r", + (user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); + nprintf(circuit, "/ShowNames - Toggle displaying name as well as call on each message - %s\r", + (user->rtflags & u_shownames) ? "Enabled" : "Disabled"); + nprintf(circuit, "/Auto - Toggle Automatic character set selection - %s.\r", + (user->rtflags & u_auto) ? "Enabled" : "Disabled"); + nprintf(circuit, "/UTF-8 - Character set Selection - %s.\r", + (user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); + nprintf(circuit, "/Time - Toggle displaying timestamp on each message - %s.\r", + (user->rtflags & u_showtime) ? "Enabled" : "Disabled"); + nputs(circuit, "/S CALL Text - Send Text to that station only.\r"); + nputs(circuit, "/F - Force all links to be made.\r/K - Show Known nodes.\r"); + nputs(circuit, "/B - Leave Chat and return to node.\r/QUIT - Leave Chat and disconnect from node.\r"); + } + } + return TRUE; + + case 'k' : show_nodes(circuit); return TRUE; + + case 'n' : + + f1 = &Buffer[2]; + + while ((*f1 != 0) && (*f1 == ' ')) + f1++; + + if (*f1 == 0) + { + nprintf(circuit, "Name is %s\r", user->name); + return TRUE; + } + + strnew(&user->name, f1); + nprintf(circuit, "Name set to %s\r", user->name); + upduser(user); + user_tell(user, id_user); + return TRUE; + + case 'p' : show_circuits(circuit, Buffer[3]); return TRUE; + + case 'q' : + + f1 = &Buffer[2]; + + while ((*f1 != 0) && (*f1 == ' ')) + f1++; + + if (*f1 == 0) + { + nprintf(circuit, "QTH is %s\r", user->qth); + return TRUE; + } + + strnew(&user->qth, f1); + + nprintf(circuit, "QTH set to %s\r", user->qth); + upduser(user); + user_tell(user, id_user); + return TRUE; + + case 's' : + strcat(Buffer, "\r"); + f1 = strlop(Buffer, ' '); // To. + if (!f1) break; + f2 = strlop(f1, ' '); // Text to send. + if (!f2) break; + _strupr(f1); + su = user_find(f1, NULL); + + if (!su) + { + nputs(circuit, "*** That user is not logged in.\r"); + return TRUE; + } + + // Send to the desired user only. + + if (su->circuit->rtcflags & p_user) + text_tellu(user, f2, f1, o_one); + else + text_xmit(user, su, f2); + + return TRUE; + + case 't' : + f1 = strlop(Buffer, ' '); + if (f1) + { + if (topic_chg(user, f1)) + { + nprintf(circuit, "Switched to Topic %s\r", user->topic->name); + show_users_in_topic(circuit); + + // Tell all link circuits about the change of topic. + + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked) + topic_xmit(user, c); + } + } + else + { + // Already in topic + + nprintf(circuit, "You were already in Topic %s\r", user->topic->name); + } + } + else + show_topics(circuit); + return TRUE; + + case 'u' : show_users(circuit); return TRUE; + + default : break; + } + + saywhat(circuit); + return TRUE; +} + +void makelinks(void) +{ + LINK *link; + ChatCIRCUIT *circuit; + + // Make the links. Called every 10 seconds + + // Make sure previous link has completed or failed + + if (RunningConnectScript) + { + // Make sure Connect Script isn't taking too long + + if (time(NULL) - RunningConnectScript < 30) + return; + + // Running too long - close it + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + // Find the link + + if (circuit->rtcflags & (p_linkini)) + { + link = circuit->u.link; + link->flags = p_linkfailed; + RunningConnectScript = 0; + link->scriptRunning = 0; // so it doesn't get reentered + Logprintf(LOG_CHAT, circuit, '|', "Connect to %s timed out", circuit->Callsign); + + Disconnect(circuit->BPQStream); + } + } + RunningConnectScript = 0; + } + + for (link = link_hd; link; link = link->next) + { + // Is this link already established? + + if (link->flags & (p_linked | p_linkini)) + continue; + + // Already linked through some other node? + // If so, making this link would create a loop. + + if (node_find(link->call)) + continue; + + // Fire up the process to handle this link. + + if (link->delay == 0) + { + link->flags = p_linkini; + link->delay = 12; // 2 mins + chat_link_out(link); + return; // One at a time + } + else + link->delay--; + } +} + +VOID node_close() +{ + // Close all Node-Node Links + + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & (p_linked | p_linkini | p_linkwait)) + Disconnect(circuit->BPQStream); + } +} + +// Send Keepalives to all connected nodes + +static void node_keepalive() +{ + ChatCIRCUIT *circuit; + + NeedStatus = TRUE; // Send Report to Monitor + + if (user_hd) // Any Users? + { + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked && circuit->u.link) + { + nprintf(circuit, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, circuit->u.link->call, Verstring); + circuit->u.link->timePollSent = time(NULL); // Also acts as poll + } + } + } + else + { + // No users. Close links + + node_close(); + } +} + +VOID ChatTimer() +{ + // Entered every 10 seconds + + int i = 0; + ChatCIRCUIT *c; + +#ifndef LINBPQ + int len; + CHATNODE *node; + TOPIC *topic; + char Msg[256]; +#endif + USER *user; + time_t NOW = time(NULL); + + GetSemaphore(&ChatSemaphore, 0); + + if (NeedStatus) + { + NeedStatus = FALSE; + SendChatLinkStatus(); + } + +#ifndef LINBPQ + + ClearDebugWindow(); + + WritetoDebugWindow("Chat Nodes\r\n", 12); + + for (node = node_hd; node; node = node->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s Version %s Count %d\r\n", + node->call, node->Version, node->refcnt); + WritetoDebugWindow(Msg, len); + + i++; + } + + SetDlgItemInt(hWnd, IDC_NODES, i, FALSE); + + WritetoDebugWindow("Chat Links\r\n", 12); + + i = 0; + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked) + { + char buff[1000]; + int ptr; + CT * ct; + ptr = sprintf_s(buff, sizeof(buff), "%s Topics: ", c->u.user->call); + + if (c->topic) + { + for (ct = c->topic; ct; ct = ct->next) + { + ptr+= sprintf_s(&buff[ptr], sizeof(buff) - ptr, "%s ", ct->topic->name); + } + } + WritetoDebugWindow(buff, ptr); + WritetoDebugWindow("\r\n", 2); + + i++; + } + } + + SetDlgItemInt(hWnd, IDC_LINKS, i, FALSE); + + WritetoDebugWindow("Chat Topics\r\n", 12); + + i = 0; + for (topic = topic_hd; topic; topic = topic->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s %d\r\n", topic->name, topic->refcnt); + WritetoDebugWindow(Msg, len); + i++; + } + + WritetoDebugWindow("Chat Users\r\n", 12); + + i = 0; + for (user = user_hd; user; user = user->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s Topic %s\r\n", user->call, + (user->topic) ? user->topic->name : "** Missing Topic **"); + WritetoDebugWindow(Msg, len); + i++; + + if (user->circuit && user->circuit->rtcflags & p_user) // Local User + { + time_t Idle = NOW - user->lastmsgtime; + + if (Idle > 7200) + { + nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); + Sleep(1000); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + break; + } + else + { + Disconnect(user->circuit->BPQStream); + break; + } + } + + if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) + { + nprintf(user->circuit, "Chat Keepalive\r"); + user->lastsendtime = NOW; + } + } + } + + SetDlgItemInt(hWnd, IDC_USERS, i, FALSE); + +#else + + for (user = user_hd; user; user = user->next) + { + if (user->circuit && user->circuit->rtcflags & p_user) // Local User + { + if ((NOW - user->lastmsgtime) > 7200) + { + nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); + Sleep(1000); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + break; + } + else + { + Disconnect(user->circuit->BPQStream); + break; + } + } + + if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) + { + nprintf(user->circuit, "Chat Keepalive\r"); + user->lastsendtime = NOW; + } + } + } + +#endif + + // if no message on a Node-Node link, send poll + + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked && c->u.link) + { + time_t Now = time(NULL); + LINK * Link = c->u.link; + + if (Now - Link->lastMsgReceived > 60) + { + // if we have a poll outstanding for ? 30 secs close link + // but check other end can handle polls + + if (Link->supportsPolls && Link->timePollSent && Now - Link->timePollSent > 30) + { + Logprintf(LOG_CHAT, c, '|', "%s No Poll Response for %d Secs - Dropping Link", + c->Callsign, Now - Link->timePollSent); + + Disconnect(c->BPQStream); + continue; + } + + Link->timePollSent = Now; + nprintf(c, "%c%c%s %s\r", FORMAT, id_poll, OurNode, Link->call); + } + } + } + + ChatTmr++; + + if (user_hd) // Any Users? + makelinks(); + + if (ChatTmr > 60) // 10 Mins + { + ChatTmr = 1; + node_keepalive(); + } + + FreeSemaphore(&ChatSemaphore); + + if (NeedINFO) + { + NeedINFO--; + + if (NeedINFO == 0) + { + // Send INFO to Chatmap + + char Msg[500]; + int len; + + NeedINFO = 360; // Send Every Hour + + if (Position[0]) + { + len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode); + + if (len < 256) + Send_MON_Datagram(Msg, len); + } + } + } +} + +VOID FreeChatMemory() +{ + removelinks(); + removeknown(); +} + +// Find a call in the known node list. + +KNOWNNODE *knownnode_find(char *call) +{ + KNOWNNODE *node; + + for (node = known_hd; node; node = node->next) + { + if (matchi(node->call, call)) + break; + } + + return node; +} + +// Add a known node. + +static KNOWNNODE *knownnode_add(char *call) +{ + KNOWNNODE *node; + + node = knownnode_find(call); + + if (!node) + { + node = zalloc(sizeof(KNOWNNODE)); + sl_ins_hd(node, known_hd); + node->call = _strdup(call); + } + + node->LastHeard = time(NULL); + return node; +} + +static char UIDEST[10] = "DUMMY"; +static char AXDEST[7]; +static char ChatMYCALL[7]; + +#pragma pack(1) + + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + +}MESSAGEX, *PMESSAGEX; + +#pragma pack() + +SOCKET ChatReportSocket = 0; + + +VOID SetupChat() +{ + u_long param=1; + BOOL bcopt=TRUE; + + ConvToAX25(OurNode, ChatMYCALL); + ConvToAX25(UIDEST, AXDEST); + + sprintf(Verstring, "%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]); + + LoadKnown(); + + ChatReportSocket = socket(AF_INET,SOCK_DGRAM,0); + + if (ChatReportSocket == INVALID_SOCKET) + { + Debugprintf("Failed to create Chat Reporting socket"); + ChatReportSocket = 0; + return; + } + + ioctlsocket (ChatReportSocket, FIONBIO, ¶m); + setsockopt (ChatReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); +} + + +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len) +{ + MESSAGEX AXMSG; + PMESSAGEX AXPTR = &AXMSG; + + if (Len > 256) + { + Debugprintf("Send_MON_Datagram Error Msg = %s Len = %d", Msg, Len); + return; + } + +// ConvToAX25("GM4OAS-5", ChatMYCALL); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, AXDEST, 7); + memcpy(AXPTR->ORIGIN, ChatMYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->DATA, Msg, Len); + + SendChatReport(ChatReportSocket, (char *)&AXMSG.DEST, Len + 16); + + return; + +} + +VOID SendChatLinkStatus() +{ + char Msg[256] = {0}; + LINK * link; + int len = 0; + ChatCIRCUIT *circuit; + + if (ChatApplNum == 0) + return; + +// if (AXIPPort == 0) +// return; + + if (ChatMYCALL[0] == 0) + return; + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked) + { + // Verify connection + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (strcmp(circuit->Callsign, link->alias) == 0) + { + if (circuit->Active == 0) + { + // BPQ Session is dead - Simulate a Disconnect + + circuit->Active = TRUE; // So disconnect will work + Disconnected(circuit->BPQStream); + NeedStatus = TRUE; // Reenter + return; // Link Chain has changed + } + break; + } + } + + if (circuit == 0) + { + // No BPQ Session - is the only answer to restart the node? + + // Logprintf(LOG_DEBUGx, NULL, '!', "Stuck Chat Sesion Detected"); + // Logprintf(LOG_DEBUGx, NULL, '!', "Chat is a mess - forcing a restart"); + // ProgramErrors = 26; + // CheckProgramErrors(); + } + } + + len = sprintf(Msg, "%s%s %c ", Msg, link->call, '0' + link->flags); + + if (len > 240) + break; + } + Msg[len++] = '\r'; + + Send_MON_Datagram(Msg, len); +} + +VOID ClearChatLinkStatus() +{ + LINK * link; + + for (link = link_hd; link; link = link->next) + { + link->flags = 0; + } +} + +BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len) +{ + LINK * link = conn->u.link; + char ** Scripts; + + ChatWriteLogLine(conn, '<', Buffer, len-1, LOG_CHAT); + + Buffer[len] = 0; + _strupr(Buffer); + + Scripts = link->ConnectScript; + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + link->flags = p_linkfailed; + RunningConnectScript = 0; + link->scriptRunning = 0; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + if (link->RTLSent) + { + RunningConnectScript = 0; + link->scriptRunning = 0; + link->RTLSent = 0; + + if (memcmp(Buffer, "OK", 2) == 0) + { + // Reply to *RTL + + // Make sure node isn't known. There is a window here that could cause a loop + + if (node_find(conn->u.link->call)) + { + Logprintf(LOG_CHAT, conn, '|', "Dropping link with %s to prevent a loop", conn->Callsign); + Disconnect(conn->BPQStream); + return FALSE; + } + + conn->u.link->flags = p_linked; + conn->rtcflags = p_linked; + state_tell(conn, conn->FBBReplyChars); + NeedStatus = TRUE; + + return TRUE; + } + + // Some other response to *RTL - disconnect + + Logprintf(LOG_CHAT, conn, '|', "Unexpected Response %s to *RTL - Dropping link", conn->Callsign), Buffer; + Disconnect(conn->BPQStream); + return FALSE; + + } + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + char * Cmd; + +LoopBack: + + Cmd = Scripts[++link->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd == 0 || link->ScriptIndex >= link->Lines) + { + link->MoreLines = FALSE; + return TRUE; + } + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nprintf(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nprintf(conn, "%s\r", Cmd); + + return TRUE; + } + + if (memcmp(Buffer, "[BPQCHATSERVER-", 15) == 0) + { + char * ptr = strchr(Buffer, ']'); + if (ptr) + { + *ptr = 0; + strcpy(conn->FBBReplyChars, &Buffer[15]); + } + else + conn->FBBReplyChars[0] = 0; + + // Connected - Send *RTL + + nputs(conn, "*RTL\r"); // Log in to the remote RT system. + + conn->u.link->timePollSent = time(NULL); // Keepalive is a poll + nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); + link->RTLSent = 1; + conn->u.link->lastMsgSent = time(NULL); + + return TRUE; + } + +// Anthing else could be ctext. etc. Ignore + + return TRUE; +} + + + +#ifdef LINBPQ + +// LINCHAT specific code + +extern struct SEM OutputSEM; + +static config_t cfg; +static config_setting_t * group; + +extern char pgm[256]; + +char ChatSYSOPCall[50] = ""; + +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user); + + +int ChatConnected(int Stream) +{ + int n; + ChatCIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + LINK *link; + KNOWNNODE *node; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + if (conn->rtcflags == p_linkini) + { + conn->paclen = 236; + + // Run first line of connect script + + ProcessChatConnectScript(conn, ConnectedMsg, 15); +// nprintf(conn, "c %s\r", conn->u.link->call); + } + return 0; + } + + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + if (paclen == 0) + paclen = 256; + + conn->paclen = paclen; + + strlop(callsign, ' '); // Remove trailing spaces + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = zalloc(sizeof(struct UserInfo)); + + strcpy(user->Call, callsign); + + conn->UserPointer = user; + + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + conn->Flags |= CHATMODE; + + nprintf(conn, ChatSID, Ver[0], Ver[1], Ver[2], Ver[3]); + + // See if from a defined node + + for (link = link_hd; link; link = link->next) + { + if (matchi(conn->Callsign, link->call)) + { + conn->rtcflags = p_linkwait; + return 0; // Wait for *RTL + } + } + + // See if from a previously known node + + node = knownnode_find(conn->Callsign); + + if (node) + { + // A node is trying to link, but we don't have it defined - close + + Logprintf(LOG_CHAT, conn, '!', "Node %s connected, but is not defined as a Node - closing", + conn->Callsign); + + nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + + ChatFlush(conn); + + Sleep(500); + conn->rtcflags = p_nil; + + Disconnect(conn->BPQStream); + + return 0; + } + + if (user->Name[0] == 0) + { + char * Name = lookupuser(user->Call); + + if (Name) + { + if (strlen(Name) > 17) + Name[17] = 0; + + strcpy(user->Name, Name); + free(Name); + } + else + { + conn->Flags |= GETTINGUSER; + nputs(conn, NewUserPrompt); + return TRUE; + } + } + + ChatSendWelcomeMsg(Stream, conn, user); + RefreshMainWindow(); + ChatFlush(conn); + + return 0; + } + } + + return 0; +} + +int ChatDisconnected (ChatCIRCUIT * conn) +{ + struct UserInfo * user = NULL; + int Stream = conn->BPQStream; + char Msg[255]; + int len; + + if (conn->Active == FALSE) + return 0; + + ChatClearQueue(conn); + + conn->Active = FALSE; + + if (conn->Flags & CHATMODE) + { + if (conn->Flags & CHATLINK && conn->u.link) + { + // if running connect script, clear script active + + if (conn->u.link->flags & p_linkini) + { + RunningConnectScript = 0; + conn->u.link->scriptRunning = 0; + } + + len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Disconnected", conn->u.link->call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + link_drop(conn); + + } + else + { + len=sprintf_s(Msg, sizeof(Msg), "Chat User %s Disconnected", conn->Callsign); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + + logout(conn); + + } + + conn->Flags = 0; + conn->u.link = NULL; + conn->UserPointer = NULL; + return 0; + } + + return 0; +} + +int ChatDoReceivedData(ChatCIRCUIT * conn) +{ + int count, InputLen; + UINT MsgLen; + int Stream = conn->BPQStream; + struct UserInfo * user; + char * ptr, * ptr2; + char Buffer[10000]; + + + // May have several messages per packet, or message split over packets + + if (conn->InputLen + 1000 > 10000) // Shouldnt have lines longer than this in text mode + conn->InputLen = 0; // discard + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + conn->InputLen += InputLen; + +loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + + if (conn->u.user->circuit && conn->u.user->circuit->rtcflags & p_user) // Local User + conn->u.user->lastmsgtime = time(NULL); + + return 0; + } + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + + if (ptr) // CR in buffer + { + user = conn->UserPointer; + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single meg in buffer + + if (conn->rtcflags == p_linkini) // Chat Connect + ProcessChatConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessChatLine(conn, user, conn->InputBuffer, conn->InputLen); + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + if (conn->rtcflags == p_linkini) + ProcessChatConnectScript(conn, Buffer, MsgLen); + else + ProcessChatLine(conn, user, Buffer, MsgLen); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + conn->InputLen -= MsgLen; + + goto loop; + + } + } + return 0; +} + + +int ChatPollStreams() +{ + int state,change; + ChatCIRCUIT * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + ChatConnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + ChatDisconnected(conn); + FreeSemaphore(&ConSemaphore); + } + } + + ChatDoReceivedData(conn); + } + + return 0; +} + + +BOOL GetChatConfig(char * ConfigName) +{ + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "%d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return(EXIT_FAILURE); + } + + group = config_lookup (&cfg, "Chat"); + + if (group == NULL) + return EXIT_FAILURE; + + ChatApplNum = GetIntValue(group, "ApplNum"); + MaxChatStreams = GetIntValue(group, "MaxStreams"); + GetStringValue(group, "OtherChatNodes", OtherNodesList); + GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); + GetStringValue(group, "MapPosition", Position); + GetStringValue(group, "MapPopup", PopupText); + PopupMode = GetIntValue(group, "PopupMode"); + + + return EXIT_SUCCESS; +} + +VOID SaveChatConfigFile(char * ConfigName) +{ + config_setting_t *root, *group; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "Chat", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "ApplNum", ChatApplNum); + SaveIntValue(group, "MaxStreams", MaxChatStreams); + SaveStringValue(group, "OtherChatNodes", OtherNodesList); + SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); + + SaveStringValue(group, "MapPosition", Position); + SaveStringValue(group, "MapPopup", PopupText); + SaveIntValue(group, "PopupMode", PopupMode); + + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + config_destroy(&cfg); +} + +BOOL ChatInit() +{ + char * ptr1 = GetApplCall(ChatApplNum); + char * ptr2; + char * Context; + int i; + ChatCIRCUIT * conn; + + + if (*ptr1 < 0x21) + { + printf("No APPLCALL for Chat APPL\n"); + return FALSE; + } + + memcpy(OurNode, ptr1, 10); + strlop(OurNode, ' '); + + ptr1 = GetApplAlias(ChatApplNum); + memcpy(OurAlias, ptr1,10); + strlop(OurAlias, ' '); + + if (ChatSYSOPCall[0] == 0) + { + strcpy(ChatSYSOPCall, OurNode); + strlop(ChatSYSOPCall, '-'); + } + + sprintf(ChatSignoffMsg, "73 de %s\r", ChatSYSOPCall); + + if (ChatWelcomeMsg[0] == 0) + sprintf(ChatWelcomeMsg, "%s's Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$W" + "This may take a minute or two.$WThe /p command shows what nodes are linked.$W", ChatSYSOPCall); + + ChatApplMask = 1<<(ChatApplNum-1); + + // Set up other nodes list. rtlink messes with the string so pass copy + + // On first run config will have spaces not newlines + + if (strchr(OtherNodesList, '\r')) // Has connect script entries + { + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); + + while (ptr1 && ptr1[0]) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, "\r\n", &Context); + } + } + else + { + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context); + + while (ptr1) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, " ,\r", &Context); + } + } + + free(ptr2); + + SetupChat(); + + // Allocate Streams + + strcpy(pgm, "CHAT"); + + for (i = 0; i < MaxChatStreams; i++) + { + conn = &ChatConnections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofChatStreams++; + + SetAppl(conn->BPQStream, 3, ChatApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + return TRUE; +} + + +void ChatFlush(ChatCIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent = 0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 4) + return; // Busy + + if (tosend <= conn->paclen) + len = tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer += len; + + FreeSemaphore(&OutputSEM); + + tosend -= len; + sent++; + + if (sent > 4) + return; + } + + // All Sent. Free buffers and reset pointers + + ChatClearQueue(conn); +} + +VOID ChatClearQueue(ChatCIRCUIT * conn) +{ + if (conn->OutputQueue == NULL) + return; + + GetSemaphore(&OutputSEM, 0); + + conn->OutputGetPointer = 0; + conn->OutputQueueLength = 0; + + FreeSemaphore(&OutputSEM); +} + +void ChatTrytoSend() +{ + // call Flush on any connected streams with queued data + + ChatCIRCUIT * conn; + + int n; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (conn->Active == TRUE) + ChatFlush(conn); + } +} + +VOID CloseChat() +{ + int BPQStream, n; + + for (n = 0; n < NumberofChatStreams; n++) + { + BPQStream = ChatConnections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + ClearChatLinkStatus(); + SendChatLinkStatus(); + Sleep(1000); // A bit of time for links to close + SendChatLinkStatus(); // Send again to reduce chance of being missed + FreeChatMemory(); +} + +VOID SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); + +} + +#endif + + +#ifndef WIN32 +#define INVALID_HANDLE_VALUE (void *)-1 +#endif + +static FILE * LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +static time_t LastLogTime[4] = {0, 0, 0, 0}; + +static char FilesNames[4][100] = {"", "", "", ""}; + +static char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + + +BOOL ChatOpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t T; + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_CHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUGx) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + + } + +#endif + + if (Flags == LOG_CHAT && !LogCHAT) + return; + + if (LogHandle[Flags] == INVALID_HANDLE_VALUE) ChatOpenLogfile(Flags); + + if (LogHandle[Flags] == INVALID_HANDLE_VALUE) return; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + fclose(LogHandle[Flags]); + LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + + diff --git a/Housekeeping.c b/Housekeeping.c new file mode 100644 index 0000000..8188793 --- /dev/null +++ b/Housekeeping.c @@ -0,0 +1,1184 @@ +/* +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 +// +// Housekeeping Module + +#include "bpqmail.h" + +char * APIENTRY GetBPQDirectory(); + +int LogAge = 7; + +BOOL DeletetoRecycleBin = FALSE; +BOOL SuppressMaintEmail = FALSE; +BOOL GenerateTrafficReport = TRUE; +BOOL SaveRegDuringMaint = FALSE; +BOOL OverrideUnsent = FALSE; +BOOL SendNonDeliveryMsgs = TRUE; +VOID UpdateWP(); + +double PR = 30; +double PUR = 30; +double PF = 30; +double PNF = 30; + +int BF = 30; +int BNF = 30; +//int AP; +//int AB; +int NTSD = 30; +int NTSF = 30; +int NTSU = 30; + +char LTFROMString[2048]; +char LTTOString[2048]; +char LTATString[2048]; + +struct Override ** LTFROM; +struct Override ** LTTO; +struct Override ** LTAT; + +int DeleteLogFiles(); + +VOID SendNonDeliveryMessage(struct MsgInfo * OldMsg, BOOL Forwarded, int Age); +int CreateWPMessage(); +int DeleteRedundantMessages(); +VOID CreateUserReport(); +UCHAR * APIENTRY GetLogDirectory(); + +time_t LastHouseKeepingTime; + +time_t LastTrafficTime; + +void DeletetoRecycle(char * FN) +{ +#ifdef WIN32 + SHFILEOPSTRUCT FileOp; + + FileOp.hwnd = NULL; + FileOp.wFunc = FO_DELETE; + FileOp.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR | FOF_ALLOWUNDO; + FileOp.pFrom = FN; + FileOp.pTo = NULL; + + SHFileOperation(&FileOp); +#else + + // On Linux move to Deleted under current directory + + char newName[256]; + char oldName[256]; + + strcpy(oldName, FN); + + char * old = FN; + + mkdir("Deleted", S_IRWXU | S_IRWXG | S_IRWXO); // Make sure exists + + while(strchr(old, '/')) + { + old = strlop(old, '/'); + } + sprintf(newName, "Deleted/%s", old); + + rename(oldName, newName); + +#endif +} + +VOID FreeOverride(struct Override ** Hddr) +{ + struct Override ** Save; + + if (Hddr) + { + Save = Hddr; + while(Hddr[0]) + { + free(Hddr[0]->Call); + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + +VOID FreeOverrides() +{ + FreeOverride(LTFROM); + FreeOverride(LTTO); + FreeOverride(LTAT); +} + +VOID * GetOverrides(config_setting_t * group, char * ValueName) +{ + char * ptr1; + char * MultiString = NULL; + char * ptr; + int Count = 0; + struct Override ** Value; + char * Val; + + config_setting_t *setting; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + setting = config_setting_get_member (group, ValueName); + + if (setting) + { + ptr = (char *)config_setting_get_string (setting); + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count] = zalloc(sizeof(struct Override)); + Val = strlop(ptr, ','); + if (Val == NULL) + break; + + Value[Count]->Call = _strupr(_strdup(ptr)); + Value[Count++]->Days = atoi(Val); + ptr = ptr1; + } + } + + Value[Count] = NULL; + return Value; +} + +VOID * RegGetOverrides(HKEY hKey, char * ValueName) +{ +#ifdef LINBPQ + return NULL; +#else + int retCode,Type,Vallen; + char * MultiString; + int ptr, len; + int Count = 0; + struct Override ** Value; + char * Val; + + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + Value[0] = NULL; + + Vallen=0; + + retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if ((retCode != 0) || (Vallen == 0)) + return FALSE; + + MultiString = malloc(Vallen); + + retCode = RegQueryValueEx(hKey, ValueName, 0, + (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); + + ptr=0; + + while (MultiString[ptr]) + { + len=strlen(&MultiString[ptr]); + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count] = zalloc(sizeof(struct Override)); + Val = strlop(&MultiString[ptr], ','); + if (Val == NULL) + break; + + Value[Count]->Call = _strupr(_strdup(&MultiString[ptr])); + Value[Count++]->Days = atoi(Val); + ptr+= (len + 1); + } + + Value[Count] = NULL; + + free(MultiString); + + return Value; +#endif +} + +int Removed; +int Killed; +int BIDSRemoved; + +#ifndef LINBPQ + +INT_PTR CALLBACK HKDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command; + + switch (message) + { + case WM_INITDIALOG: + + SetDlgItemInt(hDlg, IDC_REMOVED, Removed, FALSE); + SetDlgItemInt(hDlg, IDC_KILLED, Killed, FALSE); + SetDlgItemInt(hDlg, IDC_LIVE, NumberofMessages - Killed, FALSE); + SetDlgItemInt(hDlg, IDC_TOTAL, NumberofMessages, FALSE); + SetDlgItemInt(hDlg, IDC_BIDSREMOVED, BIDSRemoved, FALSE); + SetDlgItemInt(hDlg, IDC_BIDSLEFT, NumberofBIDs, FALSE); + + return (INT_PTR)TRUE; + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + } + break; + } + + return 0; +} +#endif + +VOID DoHouseKeeping(BOOL Manual) +{ + time_t NOW; + + CreateUserReport(); + + UpdateWP(); + + DeleteLogFiles(); + + RemoveKilledMessages(); + ExpireMessages(); + + GetSemaphore(&AllocSemaphore, 0); + ExpireBIDs(); + FreeSemaphore(&AllocSemaphore); + + if (LatestMsg > MaxMsgno) + { + GetSemaphore(&MsgNoSemaphore, 0); + GetSemaphore(&AllocSemaphore, 0); + + Renumber_Messages(); + + FreeSemaphore(&AllocSemaphore); + FreeSemaphore(&MsgNoSemaphore); + } + + if (!SuppressMaintEmail) + MailHousekeepingResults(); + + LastHouseKeepingTime = NOW = time(NULL); + + SaveMessageDatabase(); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + GetBadWordFile(); // Reread Badwords + +#ifndef LINBPQ + + if (Manual) + DialogBox(hInst, MAKEINTRESOURCE(IDD_MAINTRESULTS), hWnd, HKDialogProc); + +#endif + + if (SendWP) + CreateWPMessage(); + + return; +} + +VOID ExpireMessages() +{ + struct MsgInfo * Msg; + int n; + time_t PRLimit; + time_t PURLimit; + time_t PFLimit; + time_t PNFLimit; + time_t BFLimit; + time_t BNFLimit; + time_t BLimit; + time_t NTSDLimit; + time_t NTSULimit; + time_t NTSFLimit; + + struct Override ** Calls; + + time_t now = time(NULL); + time_t Future = now + (7 * 86400); + + Killed = 0; + + PRLimit = now - PR*86400; + PURLimit = now - PUR*86400; + PFLimit = now - PF*86400; + PNFLimit = now - PNF*86400; + BFLimit = now - BF*86400; + BNFLimit = now - BNF*86400; + + if (NTSU == 0) + { + // Assume all unset + + NTSD = 30; + NTSU = 30; + NTSF = 30; + } + + NTSDLimit = now - NTSD*86400; + NTSULimit = now - NTSU*86400; + NTSFLimit = now - NTSF*86400; + + for (n = 1; n <= NumberofMessages; n++) + { + Msg = MsgHddrPtr[n]; + + // If from the future, Kill it + + if (Msg->datecreated > Future) + { + KillMsg(Msg); + continue; + } + + switch (Msg->type) + { + case 'P': + + switch (Msg->status) + { + case 'N': + case 'H': + + // Is it unforwarded or unread? + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + if (Msg->datecreated < PURLimit) + { + if (SendNonDeliveryMsgs) + SendNonDeliveryMessage(Msg, TRUE, PUR); + + KillMsg(Msg); + } + } + else + { + if (Msg->datecreated < PNFLimit) + { + if (SendNonDeliveryMsgs) + SendNonDeliveryMessage(Msg, FALSE, PNF); + + KillMsg(Msg); + } + } + continue; + + case 'F': + + if (Msg->datechanged < PFLimit) KillMsg(Msg); + + continue; + + case 'Y': + + if (Msg->datechanged < PRLimit) KillMsg(Msg); + + continue; + + default: + + continue; + + } + + case 'T': + + switch (Msg->status) + { + case 'F': + + if (Msg->datechanged < NTSFLimit) + KillMsg(Msg); + + continue; + + case 'D': + + if (Msg->datechanged < NTSDLimit) + KillMsg(Msg); + + continue; + + default: + + if (Msg->datecreated < NTSULimit) + { + if (SendNonDeliveryMsgs) + SendNonDeliveryMessage(Msg, TRUE, NTSU); + + KillMsg(Msg); + } + + continue; + + } + + case 'B': + + BLimit = BF; + BNFLimit = now - BNF*86400; + + // Check FROM Overrides + + if (LTFROM) + { + Calls = LTFROM; + + while(Calls[0]) + { + if (strcmp(Calls[0]->Call, Msg->from) == 0) + { + BLimit = Calls[0]->Days; + goto gotit; + } + Calls++; + } + } + + // Check TO Overrides + + if (LTTO) + { + Calls = LTTO; + + while(Calls[0]) + { + if (strcmp(Calls[0]->Call, Msg->to) == 0) + { + BLimit = Calls[0]->Days; + goto gotit; + } + Calls++; + } + } + + // Check AT Overrides + + if (LTAT) + { + Calls = LTAT; + + while(Calls[0]) + { + if (strcmp(Calls[0]->Call, Msg->via) == 0) + { + BLimit = Calls[0]->Days; + goto gotit; + } + Calls++; + } + } + + gotit: + + BFLimit = now - BLimit*86400; + + if (OverrideUnsent) + if (BLimit != BF) // Have we an override? + BNFLimit = BFLimit; + + switch (Msg->status) + { + case '$': + case 'N': + case ' ': + case 'H': + + + if (Msg->datecreated < BNFLimit) + KillMsg(Msg); + break; + + case 'F': + case 'Y': + + if (Msg->datecreated < BFLimit) + KillMsg(Msg); + break; + } + } + } +} + + +VOID KillMsg(struct MsgInfo * Msg) +{ + FlagAsKilled(Msg, FALSE); + Killed++; +} + +BOOL RemoveKilledMessages() +{ + struct MsgInfo * Msg; + struct MsgInfo ** NewMsgHddrPtr; + char MsgFile[MAX_PATH]; + int i, n; + + Removed = 0; + + GetSemaphore(&MsgNoSemaphore, 0); + GetSemaphore(&AllocSemaphore, 0); + + FirstMessageIndextoForward = 0; + + NewMsgHddrPtr = zalloc((NumberofMessages+1) * sizeof(void *)); + NewMsgHddrPtr[0] = MsgHddrPtr[0]; // Copy Control Record + + i = 0; + + for (n = 1; n <= NumberofMessages; n++) + { + Msg = MsgHddrPtr[n]; + + if (Msg->status == 'K') + { + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes%c", MailDir, Msg->number, 0); + if (DeletetoRecycleBin) + DeletetoRecycle(MsgFile); + else + DeleteFile(MsgFile); + + MsgnotoMsg[Msg->number] = NULL; + free(Msg); + + Removed++; + } + else + { + NewMsgHddrPtr[++i] = Msg; + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = i; + } + } + } + + NumberofMessages = i; + NewMsgHddrPtr[0]->number = i; + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; + + free(MsgHddrPtr); + + MsgHddrPtr = NewMsgHddrPtr; + + FreeSemaphore(&MsgNoSemaphore); + FreeSemaphore(&AllocSemaphore); + + return TRUE; + +} + +#define MESSAGE_NUMBER_MAX 100000 + +VOID Renumber_Messages() +{ + int * NewNumber = (int *)0; + struct MsgInfo * Msg; + struct UserInfo * user = NULL; + char OldMsgFile[MAX_PATH]; + char NewMsgFile[MAX_PATH]; + int j, lastmsg, result; + + int i, n, s; + + s = sizeof(int)* MESSAGE_NUMBER_MAX; + + NewNumber = malloc(s); + + if (!NewNumber) return; + + DeleteRedundantMessages(); // Make sure there aren't any old mail files, or renumber may fail + + memset(NewNumber, 0, s); + + for (i = 0; i < 100000; i++) + { + MsgnotoMsg[i] = NULL; + } + + i = 0; // New Message Number + + for (n = 1; n <= NumberofMessages; n++) + { + Msg = MsgHddrPtr[n]; + + NewNumber[Msg->number] = ++i; // Save so we can update users' last listed count + + // New will always be >= old unless something has gone horribly wrong, + // so can rename in place without risk of losing a message + + if (Msg->number < i) + { +#ifndef LINBPQ + MessageBox(MainWnd, "Invalid message number detected, quitting", "BPQMailChat", MB_OK); +#else + Debugprintf("Invalid message number detected, quitting"); +#endif + SaveMessageDatabase(); + if (NewNumber) free(NewNumber); + + return; + } + + if (Msg->number != i) + { + sprintf(OldMsgFile, "%s/m_%06d.mes", MailDir, Msg->number); + sprintf(NewMsgFile, "%s/m_%06d.mes", MailDir, i); + result = rename(OldMsgFile, NewMsgFile); + if (result) + { + char Errmsg[100]; + sprintf(Errmsg, "Could not rename message no %d to %d, quitting", Msg->number, i); +#ifndef LINBPQ + MessageBox(MainWnd,Errmsg , "BPQMailChat", MB_OK); +#else + Debugprintf(Errmsg); +#endif + SaveMessageDatabase(); + if (NewNumber) free(NewNumber); + + return; + } + Msg->number = i; + MsgnotoMsg[i] = Msg; + } + + } + + for (n = 0; n <= NumberofUsers; n++) + { + user = UserRecPtr[n]; + lastmsg = user->lastmsg; + + if (lastmsg <= 0) + user->lastmsg = 0; + else + { + j = NewNumber[lastmsg]; + + if (j == 0) + { + // Last listed has gone. Find next above + + while(++lastmsg < 65536) + { + if (NewNumber[lastmsg] != 0) + { + user->lastmsg = NewNumber[lastmsg]; + break; + } + } + + // Not found, so use latest + + user->lastmsg = i; + break; + } + user->lastmsg = NewNumber[lastmsg]; + } + } + + MsgHddrPtr[0]->length = LatestMsg = i; + + SaveMessageDatabase(); + SaveUserDatabase(); + + if (NewNumber) free(NewNumber); + + return; + +} + +BOOL ExpireBIDs() +{ + BIDRec * BID; + BIDRec ** NewBIDRecPtr; + unsigned short now = LOWORD(time(NULL)/86400); + + int i, n; + + NewBIDRecPtr = zalloc((NumberofBIDs + 1) * sizeof(BIDRec)); + NewBIDRecPtr[0] = BIDRecPtr[0]; // Copy Control Record + + i = 0; + + for (n = 1; n <= NumberofBIDs; n++) + { + BID = BIDRecPtr[n]; + +// Debugprintf("%d %d", BID->u.timestamp, now - BID->u.timestamp); + + if ((now - BID->u.timestamp) < BidLifetime) + NewBIDRecPtr[++i] = BID; + } + + BIDSRemoved = NumberofBIDs - i; + + NumberofBIDs = i; + NewBIDRecPtr[0]->u.msgno = i; + + free(BIDRecPtr); + + BIDRecPtr = NewBIDRecPtr; + + SaveBIDDatabase(); + + return TRUE; + +} + +VOID MailHousekeepingResults() +{ + int Length=0; + char * MailBuffer = malloc(10000); + + Length += sprintf(&MailBuffer[Length], "Killed Messages Removed %d\r\n", Removed); + Length += sprintf(&MailBuffer[Length], "Messages Killed %d\r\n", Killed); + Length += sprintf(&MailBuffer[Length], "Live Messages %d\r\n", NumberofMessages - Killed); + Length += sprintf(&MailBuffer[Length], "Total Messages %d\r\n", NumberofMessages); + Length += sprintf(&MailBuffer[Length], "BIDs Removed %d\r\n", BIDSRemoved); + Length += sprintf(&MailBuffer[Length], "BIDs Left %d\r\n", NumberofBIDs); + + SendMessageToSYSOP("Housekeeping Results", MailBuffer, Length); +} + +#ifdef WIN32 + +extern UCHAR LogDirectory[260]; + +int DeleteLogFiles() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + UCHAR * ptr; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + + ptr = GetLogDirectory(); + + strcpy(szDir, ptr); + strcat(szDir, "/logs/Log_*.txt"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return dwError; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (int)((now - ft.LowPart) / 86400); + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + if (DeletetoRecycleBin) + DeletetoRecycle(File); + else + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + dwError = GetLastError(); + + FindClose(hFind); + return dwError; +} + +#else + +#include + +int Filter(const struct dirent * dir) +{ + return memcmp(dir->d_name, "log", 3) == 0 && strstr(dir->d_name, ".txt"); +} + +int DeleteLogFiles() +{ + struct dirent **namelist; + int n; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("logs", &namelist, Filter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + sprintf(FN, "logs/%s", namelist[n]->d_name); + if (stat(FN, &STAT) == 0) + { + Age = (now - STAT.st_mtime) / 86400; + + if (Age > LogAge) + { + printf("Deleting %s\n", FN); + unlink(FN); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + + +VOID SendNonDeliveryMessage(struct MsgInfo * OldMsg, BOOL Unread, int Age) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char MailBuffer[1000]; + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + char From[100]; + char * Via; + struct UserInfo * FromUser; + + // Try to create a from Address. ( ? check RMS) + + strcpy(From, OldMsg->from); + + if (strcmp(From, "SYSTEM") == 0) + return; // Don't send non-deliverys SYSTEM messages + + // Dont send NDN for NDN + + if (strcmp(OldMsg->title, "Non-delivery Notification") == 0) + return; + + FromUser = LookupCall(OldMsg->from); + + if (FromUser) + { + if (FromUser->HomeBBS[0]) + sprintf(From, "%s@%s", OldMsg->from, FromUser->HomeBBS); + else + sprintf(From, "%s@%s", OldMsg->from, BBSName); + } + else + { + WPRecP WP = LookupWP(OldMsg->from); + + if (WP) + sprintf(From, "%s@%s", OldMsg->from, WP->first_homebbs); + } + + Msg = AllocateMsgRecord(); + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, SYSOPCall); + + Via = strlop(From, '@'); + + strcpy(Msg->to, From); + if (Via) + strcpy(Msg->via, Via); + + if (strcmp(From, "RMS:") == 0) + { + strcpy(Msg->to, "RMS"); + strcpy(Msg->via, OldMsg->emailfrom); + } + + if (strcmp(From, "smtp:") == 0) + { + Msg->to[0] = 0; + strcpy(Msg->via, OldMsg->emailfrom); + } + + if (Msg->to[0] == 0) + return; + + strcpy(Msg->title, "Non-delivery Notification"); + + if (Unread) + Msg->length = sprintf(MailBuffer, "Your Message ID %s Subject %s to %s has not been read for %d days.\r\nMessage had been deleted.\r\n", OldMsg->bid, OldMsg->title, OldMsg->to, Age); + else + Msg->length = sprintf(MailBuffer, "Your Message ID %s Subject %s to %s could not be delivered in %d days.\r\nMessage had been deleted.\r\n", OldMsg->bid, OldMsg->title, OldMsg->to, Age); + + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); +} + +VOID CreateBBSTrafficReport() +{ + struct UserInfo * User; + int i, n; + char Line[200]; + int len; + char File[MAX_PATH]; + FILE * hFile; + time_t NOW = time(NULL); + + int ConnectsIn; + int ConnectsOut; +// int MsgsReceived; +// int MsgsSent; +// int MsgsRejectedIn; +// int MsgsRejectedOut; +// int BytesForwardedIn; +// int BytesForwardedOut; + int TotMsgsReceived[4] = {0,0,0,0}; + int TotMsgsSent[4] = {0,0,0,0}; + + int TotBytesForwardedIn[4] = {0,0,0,0}; + int TotBytesForwardedOut[4] = {0,0,0,0}; + + char MsgsIn[80]; + char MsgsOut[80]; + char BytesIn[80]; + char BytesOut[80]; + char RejIn[80]; + char RejOut[80]; + + struct tm tm; + struct tm last; + + memcpy(&tm, gmtime(&NOW), sizeof(tm)); + memcpy(&last, gmtime((const time_t *)&LastTrafficTime), sizeof(tm)); + + sprintf(File, "%s/Traffic_%02d%02d%02d.txt", BaseDir, tm.tm_year-100, tm.tm_mon+1, tm.tm_mday); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + { + Debugprintf("Failed to create traffic.txt"); + return; + } + + len = sprintf(Line, " Traffic Report for %s From: %04d/%02d/%02d %02d:%02dz To: %04d/%02d/%02d %02d:%02dz\r\n", + BBSName, last.tm_year+1900, last.tm_mon+1, last.tm_mday, last.tm_hour, last.tm_min, + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min); + + fwrite(Line, 1, len, hFile); + + len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); + fwrite(Line, 1, len, hFile); + len = sprintf(Line, " In Out Rxed(P/B/T) Sent Rxed Sent In Out\r\n\r\n"); + + fwrite(Line, 1, len, hFile); + + for (i=1; i <= NumberofUsers; i++) + { + User = UserRecPtr[i]; + + ConnectsIn = User->Total.ConnectsIn - User->Last.ConnectsIn; + ConnectsOut = User->Total.ConnectsOut - User->Last.ConnectsOut; + +/* + MsgsReceived = MsgsSent = MsgsRejectedIn = MsgsRejectedOut = BytesForwardedIn = BytesForwardedOut = 0; + + for (n = 0; n < 4; n++) + { + MsgsReceived += User->Total.MsgsReceived[n] - User->Last.MsgsReceived[n]; + MsgsSent += User->Total.MsgsSent[n] - User->Last.MsgsSent[n]; + BytesForwardedIn += User->Total.BytesForwardedIn[n] - User->Last.BytesForwardedIn[n]; + BytesForwardedOut += User->Total.BytesForwardedOut[n] - User->Last.BytesForwardedOut[n]; + MsgsRejectedIn += User->Total.MsgsRejectedIn[n] - User->Last.MsgsRejectedIn[n]; + MsgsRejectedOut += User->Total.MsgsRejectedOut[n] - User->Last.MsgsRejectedOut[n]; + } + + len = sprintf(Line, "%s %-7s %5d %8d %10d %10d %10d %10d %10d %10d\r\n", + (User->flags & F_BBS)? "(B)": " ", + User->Call, ConnectsIn, + ConnectsOut, + MsgsReceived, + MsgsSent, + BytesForwardedIn, + BytesForwardedOut, + MsgsRejectedIn, + MsgsRejectedOut); +*/ + + for (n = 0; n < 4; n++) + { + TotMsgsReceived[n] += User->Total.MsgsReceived[n] - User->Last.MsgsReceived[n]; + TotMsgsSent[n] += User->Total.MsgsSent[n] - User->Last.MsgsSent[n]; + + TotBytesForwardedIn[n] += User->Total.BytesForwardedIn[n] - User->Last.BytesForwardedIn[n]; + TotBytesForwardedOut[n] += User->Total.BytesForwardedOut[n] - User->Last.BytesForwardedOut[n]; + } + + sprintf(MsgsIn,"%d/%d/%d", User->Total.MsgsReceived[1] - User->Last.MsgsReceived[1], + User->Total.MsgsReceived[2] - User->Last.MsgsReceived[2], + User->Total.MsgsReceived[3] - User->Last.MsgsReceived[3]); + + sprintf(MsgsOut,"%d/%d/%d", User->Total.MsgsSent[1] - User->Last.MsgsSent[1], + User->Total.MsgsSent[2] - User->Last.MsgsSent[2], + User->Total.MsgsSent[3] - User->Last.MsgsSent[3]); + + sprintf(BytesIn,"%d/%d/%d", User->Total.BytesForwardedIn[1] - User->Last.BytesForwardedIn[1], + User->Total.BytesForwardedIn[2] - User->Last.BytesForwardedIn[2], + User->Total.BytesForwardedIn[3] - User->Last.BytesForwardedIn[3]); + + sprintf(BytesOut,"%d/%d/%d", User->Total.BytesForwardedOut[1] - User->Last.BytesForwardedOut[1], + User->Total.BytesForwardedOut[2] - User->Last.BytesForwardedOut[2], + User->Total.BytesForwardedOut[3] - User->Last.BytesForwardedOut[3]); + + sprintf(RejIn,"%d/%d/%d", User->Total.MsgsRejectedIn[1] - User->Last.MsgsRejectedIn[1], + User->Total.MsgsRejectedIn[2] - User->Last.MsgsRejectedIn[2], + User->Total.MsgsRejectedIn[3] - User->Last.MsgsRejectedIn[3]); + + sprintf(RejOut,"%d/%d/%d", User->Total.MsgsRejectedOut[1] - User->Last.MsgsRejectedOut[1], + User->Total.MsgsRejectedOut[2] - User->Last.MsgsRejectedOut[2], + User->Total.MsgsRejectedOut[3] - User->Last.MsgsRejectedOut[3]); + + len = sprintf(Line, "%s %-7s %5d %8d%16s%16s%16s%16s%16s%16s\r\n", + (User->flags & F_BBS)? "(B)": " ", + User->Call, ConnectsIn, + ConnectsOut, + MsgsIn, + MsgsOut, + BytesIn, + BytesOut, + RejIn, + RejOut); + + fwrite(Line, 1, len, hFile); + + User->Last.ConnectsIn = User->Total.ConnectsIn; + User->Last.ConnectsOut = User->Total.ConnectsOut; + + for (n = 0; n < 4; n++) + { + User->Last.MsgsReceived[n] = User->Total.MsgsReceived[n]; + User->Last.MsgsSent[n] = User->Total.MsgsSent[n]; + User->Last.BytesForwardedIn[n] = User->Total.BytesForwardedIn[n]; + User->Last.BytesForwardedOut[n] = User->Total.BytesForwardedOut[n]; + User->Last.MsgsRejectedIn[n] = User->Total.MsgsRejectedIn[n]; + User->Last.MsgsRejectedOut[n] = User->Total.MsgsRejectedOut[n]; + } + + } + + sprintf(MsgsIn,"%d/%d/%d", TotMsgsReceived[1], TotMsgsReceived[2], TotMsgsReceived[3]); + + sprintf(MsgsOut,"%d/%d/%d", TotMsgsSent[1], TotMsgsSent[2], TotMsgsSent[3]); + + sprintf(BytesIn,"%d/%d/%d", TotBytesForwardedIn[1], TotBytesForwardedIn[2], TotBytesForwardedIn[3]); + + sprintf(BytesOut,"%d/%d/%d", TotBytesForwardedOut[1], TotBytesForwardedOut[2], TotBytesForwardedOut[3]); + + len = sprintf(Line, "\r\n Totals %s Messages In %s Messages Out %s" + " Bytes In %s Bytes Out\r\n", MsgsIn, MsgsOut, BytesIn, BytesOut); + + fwrite(Line, 1, len, hFile); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + SaveUserDatabase(); + fclose(hFile); +} diff --git a/IPCode.c b/IPCode.c new file mode 100644 index 0000000..8ddab32 --- /dev/null +++ b/IPCode.c @@ -0,0 +1,5362 @@ +/* +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 +*/ + + +// Module to provide a basic Gateway between IP over AX.25 and the Internet. + +// Uses WinPcap on Windows, TAP Driver on Linux + +// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet. +// ARP frames are also reformatted, and monitored to build a simple routing table. +// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one), +// so the default route must be configured. + +// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router. +// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router. +// The ax.25 applications will appear as single addresses on the Ethernet LAN + +// The code can also switch packets between ax.25 interfaces + +// First Version, July 2008 + +// Version 1.2.1 January 2009 + +// Add IP Address Mapping option + +// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder +// so packets can be routed from RF to/from encapsulated 44 net subnets. +// Routes may also be learned from received RF packets, or added from config file + +/* +TODo ?Multiple Adapters +*/ + +/* + Windows uses PCAP to send to both the local host (the machine running BPQ) and + to other machines on the same LAN. I may be able to add the 44/8 route but + dont at the moment. + + On Linux, the local machine doesn't see packets sent via pcap, so it uses a TAP + device for the local host, and pcap for other addresses on the LAN. The TAP is + created dynamically - it doesn't have to be predefined. A route to 44/8 via the + TAP and an ARP entry for it are also added. The TAP runs unnumbered + + 44 addresses can be NAT'ed to the local LAN address, so hosts don't have to have + both an ISP and a 44 address. You can run your local LAN as 44, but I would expect + most uses to prefer to keep their LAN with its normal (usually 192.168) addresses. + + If the PC address isn't the same as the IPGateway IPAddr a NAT entry is created + automaticaly. + + In these cases the NAT line for jnos should have TAP appended to tell + LinBPQ it is reached over the TAP. + + NAT 44.131.11.x 192.168.x.y TAP + +*/ + + +//int _winver = 0x0600; + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "CHeaders.h" + +#include "ipcode.h" + +#ifdef WIN32 +#include +#define read _read +#define write _write +#define close _close +#include +// Link with Iphlpapi.lib +#pragma comment(lib, "IPHLPAPI.lib") +#endif + +#include "pcap.h" + +int pcap_sendpacket(pcap_t *p, u_char *buf, int size); + +#ifndef LINBPQ +#include "kernelresource.h" +LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +//#define s_addr S_un.S_addr + +extern BPQVECSTRUC * IPHOSTVECTORPTR; + +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID SENDSABM(struct _LINKTABLE * LINK); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +BOOL ProcessConfig(); +VOID RemoveARP(PARPDATA Arp); +VOID AddToRoutes(PARPDATA Arp, UINT IPAddr, char Type); + +VOID ProcessTunnelMsg(PIPMSG IPptr); +VOID ProcessRIP44Message(PIPMSG IPptr); +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found); +BOOL ProcessROUTELine(char * buf, BOOL Locked); +VOID DoRouteTimer(); +PROUTEENTRY FindRoute(uint32_t IPADDR); +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap); +USHORT Generate_CHECKSUM(VOID * ptr1, int Len); +VOID RecalcTCPChecksum(PIPMSG IPptr); +VOID RecalcUDPChecksum(PIPMSG IPptr); +BOOL Send_ETH(VOID * Block, DWORD len, BOOL SendToTAP); +VOID ProcessEthARPMsg(PETHARP arpptr, BOOL FromTAP); +VOID WriteIPRLine(PROUTEENTRY RouteRecord, FILE * file); +int CountBits(uint64_t in); +VOID SendARPMsg(PARPDATA ARPptr, BOOL ToTAP);; +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls); +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); +int CountBits(uint64_t in); + +#define ARPTIMEOUT 3600 + +// ARP REQUEST (AX.25) + +AXARP AXARPREQMSG = {0}; + +// ARP REQUEST/REPLY (Eth) + +ETHARP ETHARPREQMSG = {0}; + +ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed + +int NumberofARPEntries = 0; + +ROUTEENTRY ** RouteRecords = NULL; + +int NumberofRoutes = 0; + +time_t LastRIP44Msg = 0; + +//HANDLE hBPQNET = INVALID_HANDLE_VALUE; + +uint32_t OurIPAddr = 0; +char IPAddrText[20]; // Text form of Our Address + +uint32_t EncapAddr = INADDR_NONE; // Virtual Host for IPIP PCAP Mode. +char EncapAddrText[20]; // Text form of Our Address + +UCHAR RouterMac[6] = {0}; // Mac Address of our Internet Gateway. + +//uint32_t HostIPAddr = INADDR_NONE; // Makes more sense to use same addr for host + +uint32_t HostNATAddr = INADDR_NONE; // LAN address (not 44 net) of our host +char HostNATAddrText[20]; + +uint32_t OurNetMask = 0xffffffff; + +BOOL WantTAP = FALSE; +BOOL WantEncap = 0; // Run RIP44 and Net44 Encap +BOOL NoDefaultRoute = FALSE; // Don't add route to 44/8 + +int WantUDPTunnel = 0; + +SOCKET EncapSock = 0; +SOCKET UDPSendSock = 0; + +BOOL UDPEncap = FALSE; + +BOOL IPv6 = FALSE; +int UDPPort = 4473; // RX Port, Send on +1 + +BOOL BPQSNMP = FALSE; // If set process SNMP in BPQ, else pass to host + +int IPTTL = 128; + +int tap_fd = 0; + +int FramesForwarded = 0; +int FramesDropped = 0; +int ARPTimeouts = 0; +int SecTimer = 10; + +extern char * PortConfig[]; + +int baseline=0; + +unsigned char hostaddr[64]; + +static int nat_table_len = 0; + +static struct nat_table_entry nat_table[MAX_ENTRIES]; + + +uint32_t UCSD44 = 0; // RIP SOurce - Normally 44.0.0.1 + +// Following two fields used by stats to get round shared memmory problem + +ARPDATA Arp={0}; +int ARPFlag = -1; + +// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer, +// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25 +// Enet Header ia 14 bytes, AX.25 UI is 16 + +// Also used to reassemble NOS Fragmented ax.25 packets + +static UCHAR Buffer[4096] = {0}; + +#define EthOffset 30 // Should be plenty + +DWORD IPLen = 0; + +UCHAR QST[7]={'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; //QST IN AX25 + +#ifdef WIN32 +UCHAR ourMACAddr[6] = {02,'B','P','Q',2,2}; +#else +UCHAR ourMACAddr[6] = {02,'B','P','Q',1,1}; +#endif + +UCHAR RealMacAddress[6]; + +int IPPortMask = 0; + +IPSTATS IPStats = {0}; + +UCHAR BPQDirectory[260]; + +char ARPFN[MAX_PATH]; +char IPRFN[MAX_PATH]; + +HANDLE handle; + +//#ifdef WIN32 +pcap_t *adhandle = 0; +pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +int pcap_reopen_delay; +//#endif + +static char Adapter[256]; + +int Promiscuous = 1; // Default to Promiscuous + +#ifdef WIN32 + +HINSTANCE PcapDriver=0; + +typedef void * (FAR *FARPROCX)(); +typedef int (FAR *FARPROCI)(); // Routines that return int + +int (FAR * pcap_sendpacketx)(); + +FARPROCI pcap_findalldevsx; + +FARPROCX pcap_compilex; +FARPROCX pcap_setfilterx; +FARPROCI pcap_datalinkx; +FARPROCI pcap_next_exx; +FARPROCX pcap_geterrx; +FARPROCX pcap_closex; +FARPROCX pcap_setnonblockx; + + +char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); +FARPROCI GetAddressI(char * Proc); +#else +#define pcap_findalldevsx pcap_findalldevs +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#define pcap_closex pcap_close +#define pcap_setnonblockx pcap_setnonblock +#endif + +VOID __cdecl Debugprintf(const char * format, ...); + +// Out Address Space (now only have lower 3/4 of 44 net) + +DWORD Lower44; // 44.0 to 44.127 +DWORD Upper44 ; // 44.128 to 44.191 +DWORD Lower44Mask; +DWORD Upper44Mask; + + +#ifdef WIN32 + +// Routine to check if a route to 44.0.0.0/9 exists and points to us + +BOOL Check44Route(int Interface) +{ + PMIB_IPFORWARDTABLE pIpForwardTable = NULL; + PMIB_IPFORWARDROW Row; + int Size = 0; + DWORD n; + int gotLower = 0; + int gotUpper = 0; + + // First call gets the required size + + n = GetIpForwardTable(pIpForwardTable, &Size, FALSE); + + pIpForwardTable = malloc(Size); + + n = GetIpForwardTable(pIpForwardTable, &Size, FALSE); + + if (n) + return FALSE; // Couldnt read table + + Row = pIpForwardTable->table; + + for (n = 0; n < pIpForwardTable->dwNumEntries; n++) + { + if (Row->dwForwardIfIndex == Interface) + { + if (Row->dwForwardDest == Lower44 && Row->dwForwardMask == Lower44Mask) + gotLower = 1; + + if (Row->dwForwardDest == Upper44 && Row->dwForwardMask == Upper44Mask) + gotUpper = 1; + } + Row++; + } + + free(pIpForwardTable); + + if (gotLower & gotUpper) + return TRUE; + + return FALSE; +} + +BOOL Setup44Route(int Interface, char * Gateway) +{ + // Better just to call route.exe, so we can set -p flag and use runas + + char Params[256]; + + // delete old 44/8 route + + sprintf(Params, " -delete 44.0.0.0/8"); + ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL); + + sprintf(Params, " -p add 44.0.0.0 mask 255.128.0.0 %s if %d", Gateway, Interface); + ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL); + + sprintf(Params, " -p add 44.128.0.0 mask 255.192.0.0 %s if %d", Gateway, Interface); + ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL); + + return 1; +} + +#endif + +char FormatIPWork[20]; + +char * FormatIP(uint32_t Addr) +{ + unsigned char work[4]; + + memcpy(work, &Addr, 4); + sprintf(FormatIPWork, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + return FormatIPWork; +} + +int CompareRoutes (const VOID * a, const VOID * b) +{ + PROUTEENTRY x; + PROUTEENTRY y; + + unsigned long r1, r2; + + x = * (PROUTEENTRY const *) a; + y = * (PROUTEENTRY const *) b; + + r1 = x->NETWORK; + r2 = y->NETWORK; + + r1 = htonl(r1); + r2 = htonl(r2); + + if (r1 < r2 ) return -1; + if (r1 == r2 ) return 0; + return 1; +} + + +int CompareMasks (const VOID * a, const VOID * b) +{ + PROUTEENTRY x; + PROUTEENTRY y; + + uint32_t m1, m2; + uint32_t r1, r2; + + x = * (PROUTEENTRY const *) a; + y = * (PROUTEENTRY const *) b; + + r1 = x->NETWORK; + r2 = y->NETWORK; + + m1 = x->SUBNET; + m2 = y->SUBNET; + + m1 = htonl(m1); + m2 = htonl(m2); + + r1 = htonl(r1); + r2 = htonl(r2); + + if (m1 > m2) return -1; + if (m1 == m2) + { + if (r1 < r2) return -1; + if (r1 == r2 ) return 0; + } + return 1; +} + + + +void OpenTAP(); + +BOOL GetPCAP() +{ +#ifdef WIN32 + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) return(FALSE); + + if ((pcap_findalldevsx=GetAddressI("pcap_findalldevs")) == 0 ) return FALSE; + + if ((pcap_sendpacketx = GetAddressI("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddressI("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live"); + + if (pcap_open_livex == NULL) return FALSE; + + if ((pcap_geterrx = GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx = GetAddressI("pcap_next_ex")) == 0 ) return FALSE; + + if ((pcap_closex = GetAddress("pcap_close")) == 0 ) return FALSE; + + if ((pcap_setnonblockx = GetAddress("pcap_setnonblock")) == 0 ) return FALSE; + +#endif + return TRUE; +} +Dll BOOL APIENTRY Init_IP() +{ + int ret; + + if (BPQDirectory[0] == 0) + { + strcpy(ARPFN,"BPQARP.dat"); + } + else + { + strcpy(ARPFN,BPQDirectory); + strcat(ARPFN,"/"); + strcat(ARPFN,"BPQARP.dat"); + } + + if (BPQDirectory[0] == 0) + { + strcpy(IPRFN,"BPQIPR.dat"); + } + else + { + strcpy(IPRFN,BPQDirectory); + strcat(IPRFN,"/"); + strcat(IPRFN,"BPQIPR.dat"); + } + + Lower44 = inet_addr("44.0.0.0"); + Upper44 = inet_addr("44.128.0.0"); // Loer Half + Lower44Mask = inet_addr("255.128.0.0"); // 44.128 to 44.192 + Upper44Mask = inet_addr("255.192.0.0"); + + + + // Clear fields in case of restart + + ARPRecords = NULL; // ARP Table - malloc'ed as needed + NumberofARPEntries=0; + + RouteRecords = NULL; + NumberofRoutes = 0; + + nat_table_len = 0; + + ReadConfigFile(); + + ourMACAddr[5] = (UCHAR)(OurIPAddr >> 24) & 255; + + // Clear old packets + + IPHOSTVECTORPTR->HOSTAPPLFLAGS = 0x80; // Request IP frames from Node + + // Set up static fields in ARP messages + + AXARPREQMSG.HWTYPE=0x0300; // AX25 + memcpy(AXARPREQMSG.MSGHDDR.DEST, QST, 7); + memcpy(AXARPREQMSG.MSGHDDR.ORIGIN, MYCALL, 7); + AXARPREQMSG.MSGHDDR.ORIGIN[6] |= 1; // Set End of Call + AXARPREQMSG.MSGHDDR.PID = 0xcd; // ARP + AXARPREQMSG.MSGHDDR.CTL = 03; // UI + + AXARPREQMSG.PID=0xcc00; // TYPE + AXARPREQMSG.HWTYPE=0x0300; + AXARPREQMSG.HWADDRLEN = 7; + AXARPREQMSG.IPADDRLEN = 4; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + AXARPREQMSG.SENDIPADDR = OurIPAddr; + + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP + + ETHARPREQMSG.HWTYPE=0x0100; // Eth + ETHARPREQMSG.PID=0x0008; + ETHARPREQMSG.HWADDRLEN = 6; + ETHARPREQMSG.IPADDRLEN = 4; + +//#ifdef WIN32 + + if (Adapter[0]) + if (GetPCAP() == FALSE) + return FALSE; + + // on Windows create a NAT entry for IPADDR. + // on linux enable the TAP device (on Linux you can't use pcap to talk to + // the local host, whereas on Windows you can. + +#ifndef MACBPQ +#ifndef FREEBSD + { + pcap_if_t * ifs = NULL, * saveifs; + char Line[80]; + char ErrBuf[256]; + + // Find IP Addr of Adapter Interface + + pcap_findalldevsx(&ifs, ErrBuf); + + saveifs = ifs; // Save for release + + while (ifs) + { + if (strcmp(ifs->name, Adapter) == 0) + break; + + ifs = ifs->next; + } + + if (ifs) + { + struct pcap_addr *address; + + address = ifs->addresses; + + while (address) + { + if (address->addr->sa_family == 2) + break; + + address = address->next; + } + + if (address) + { + memcpy(&HostNATAddr, &address->addr->sa_data[2], 4); + + sprintf(HostNATAddrText, "%d.%d.%d.%d", (UCHAR)address->addr->sa_data[2], + (UCHAR)address->addr->sa_data[3], + (UCHAR)address->addr->sa_data[4], + (UCHAR)address->addr->sa_data[5]); + } + + // We need to create a NAT entry. + + // For now do for both Windows and Linux + + sprintf(Line, "NAT %s %s", IPAddrText, HostNATAddrText); + Debugprintf("Generated NAT %s\n", Line); + ProcessLine(Line); +#ifdef WIN32 +#else + // Linux, need TAP + + WantTAP = TRUE; + +#endif + } + } + +#endif +#endif + // + // Open PCAP Driver + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + char buf[80]; + + if (OpenPCAP()) + sprintf(buf,"IP Using %s\n", Adapter); + else + sprintf(buf," IP Unable to open %s\n", Adapter); + + WritetoConsoleLocal(buf); + + if (adhandle == NULL) + { + WritetoConsoleLocal("Failed to open pcap device - IP Support Disabled\n"); + return FALSE; + } + } + +//#else + + // Linux - if TAP requested, open it + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (WantTAP) + OpenTAP(); + +#endif +#endif +#endif + + + ReadARP(); + ReadIPRoutes(); + + // if we are running as Net44 encap, open a socket for IPIP + + if (WantUDPTunnel) + { + struct sockaddr_in sinx; + u_long param = 1; + + UDPSendSock = socket(AF_INET,SOCK_DGRAM,0); + + ioctl (UDPSendSock,FIONBIO,¶m); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(WantUDPTunnel); + + ret = bind(UDPSendSock, (struct sockaddr *) &sinx, sizeof(sinx)); + + + } + + if (WantEncap || WantUDPTunnel) + { + union + { + struct sockaddr_in sinx; + struct sockaddr_in6 sinx6; + } sinx = {0}; + u_long param = 1; + int err, ret; + char Msg[80]; + + if (UCSD44 == 0) + UCSD44 = inet_addr("44.0.0.1"); + +#ifdef WIN32 + + // Find Interface number for PCAP Device + + { + UINT ulOutBufLen; + PIP_ADAPTER_INFO pAdapterInfo; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + int Interface = 0; + + // Make an initial call to GetAdaptersInfo to get + // the necessary size into the ulOutBufLen variable + + GetAdaptersInfo(NULL, &ulOutBufLen); + + pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + + if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) + { + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (strstr(Adapter, pAdapter->AdapterName)) + { + Interface = pAdapter->Index; + break; + } + pAdapter = pAdapter->Next; + } + free(pAdapterInfo); + } + if (NoDefaultRoute == FALSE) + { + // Check Route to 44 and if not there add + + if (Check44Route(Interface)) + WritetoConsoleLocal("Route to 44/9 and 44.128/10 found\n"); + else + { +#ifndef _winver +#define _winver 0x0600 +#pragma warning(push) +#pragma warning(disable : 4996) + if (_winver >= 0x0600) +#pragma warning(pop) + Setup44Route(Interface, "0.0.0.0"); + else + Setup44Route(Interface, EncapAddrText); +#else + Setup44Route(Interface, "0.0.0.0"); +#endif + Sleep(2000); + if (Check44Route(Interface)) + WritetoConsoleLocal("Route to44/9 and 44.128/10 added\n"); + else + WritetoConsoleLocal("Adding route to 44/9 and 44.128/10 Failed\n"); + } + } + } +#endif + + if (WantEncap) + { + + if (EncapAddr != INADDR_NONE) + { + // Using Virtual Host on PCAP Adapter (Windows) + + WritetoConsoleLocal("Net44 Tunnel opened on PCAP device\n"); + WritetoConsoleLocal("IP Support Enabled\n"); + return TRUE; + } + + if (UDPEncap) + { + // Open UDP Socket + + if (IPv6) + EncapSock = socket(AF_INET6,SOCK_DGRAM,0); + else + EncapSock = socket(AF_INET,SOCK_DGRAM,0); + + sinx.sinx.sin_port = htons(UDPPort); + } + else + { + // Open Raw Socket + + EncapSock = socket(AF_INET, SOCK_RAW, 4); + sinx.sinx.sin_port = 0; + } + + if (EncapSock == INVALID_SOCKET) + { + err = WSAGetLastError(); + sprintf(Msg, "Failed to create socket for IPIP Encap - error code = %d\n", err); + WritetoConsoleLocal(Msg); + } + else + { + + ioctl (EncapSock,FIONBIO,¶m); + + if (IPv6) + { + sinx.sinx.sin_family = AF_INET6; + memset (&sinx.sinx6.sin6_addr, 0, 16); + ret = bind(EncapSock, (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx6)); + } + else + { + sinx.sinx.sin_family = AF_INET; + sinx.sinx.sin_addr.s_addr = INADDR_ANY; + ret = bind(EncapSock, (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx)); + } + + if (ret) + { + // Bind Failed + + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for IPIP Encap socket - error code = %d\n", err); + WritetoConsoleLocal(Msg); + } + else + { + WritetoConsoleLocal("Net44 Tunnel opened\n"); + } + } + } + } + WritetoConsoleLocal("IP Support Enabled\n"); + + return TRUE; + +} + +VOID IPClose() +{ + SaveIPRoutes(); + + if (adhandle) + pcap_closex(adhandle); + +#ifdef LINBPQ + if (WantTAP && tap_fd) + close(tap_fd); +#endif + + if (EncapSock) + closesocket(EncapSock); +} + +union +{ + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; +} RXaddr; + + +Dll BOOL APIENTRY Poll_IP() +{ + int res; + struct pcap_pkthdr *header; + const u_char *pkt_data; + + // Entered every 100 mS + + // if ARPFlag set, copy requested ARP record (For BPQStatus GUI) + + if (ARPFlag != -1) + { + memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA)); + ARPFlag = -1; + } + + SecTimer--; + + if (SecTimer == 0) + { + SecTimer = 10; + DoARPTimer(); + DoRouteTimer(); + } + +Pollloop: + +//#ifdef WIN32 + + if (adhandle) + { + res = (int)pcap_next_exx(adhandle, &header, &pkt_data); + + if (res > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (header->len > 1514) + { +// Debugprintf("Ether Packet Len = %d", header->len); + goto Pollloop; + } + + memcpy(&Buffer[EthOffset],pkt_data, header->len); + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + // PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14]; + // ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255); + goto Pollloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr, FALSE); + goto Pollloop; + } + + // Ignore anything else + + goto Pollloop; + } + else + { + if (res < 0) + { + char * error = (char *)pcap_geterrx(adhandle) ; + Debugprintf(error); + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; + } + } + } + else + { + // No handle. + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + // Try reopening periodically + + pcap_reopen_delay --; + + if (pcap_reopen_delay < 0) + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; // Retry every 30 seconds + } + } + +//#endif + +#ifdef LINBPQ + +PollTAPloop: + + if (WantTAP && tap_fd) + { + int nread; + + nread = read(tap_fd, &Buffer[EthOffset], 1600); + + if (nread > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + goto PollTAPloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr, TRUE); + goto PollTAPloop; + } + + // if 08FF pass to BPQETHER Driver +/* + if (ethptr->ETYPE == 0xFF08) + { + PBUFFHEADER axmsg; + PBUFFHEADER savemsg; + int len; + + // BPQEther Encap + + len = Buffer[EthOffset + 15]*256 + Buffer[EthOffset + 14]; + + axmsg = (PBUFFHEADER)&Buffer[EthOffset + 9]; + axmsg->LENGTH = len; + axmsg->PORT = 99; // Dummy for IP Gate + + printf("BPQ Eth Len %d PID %d\n", len, axmsg->PID); + + if ((len < 16) || (len > 320)) + goto PollTAPloop; // Probably RLI Mode Frame + + + //len-=3; + + //memcpy(&buff[7],&pkt_data[16],len); + + // len+=5; + + savemsg=axmsg; + + // Packet from AX.25 + + if (CompareCalls(axmsg->ORIGIN, MYCALL)) + return 0; // Echoed packet + + switch (axmsg->PID) + { + case 0xcc: + + // IP Message + + { + PIPMSG ipptr = (PIPMSG)++axmsg; + axmsg--; + ProcessIPMsg(ipptr, axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT); + break; + } + + case 0xcd: + + // ARP Message + + ProcessAXARPMsg((PAXARP)axmsg); + SaveARP(); + break; + + // case 0x08: + } + + goto PollTAPloop; + } +*/ + // Ignore anything else + +// printf("TAP ype %X\n", ntohs(ethptr->ETYPE)); + + goto PollTAPloop; + } + } + +#endif + +PollEncaploop: + + if (EncapSock) + { + int nread; + int addrlen = sizeof(struct sockaddr_in6); + + nread = recvfrom(EncapSock, &Buffer[EthOffset], 1600, 0, (struct sockaddr *)&RXaddr.rxaddr,&addrlen); + + if (nread > 0) + { + PIPMSG IPptr = (PIPMSG)&Buffer[EthOffset]; + + if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet + { + ProcessTunnelMsg(IPptr); + } + + goto PollEncaploop; + } + else + res = GetLastError(); + } + + if (UDPSendSock) + { + int nread; + int addrlen = sizeof(struct sockaddr_in6); + + nread = recvfrom(UDPSendSock, &Buffer[EthOffset], 1600, 0, (struct sockaddr *)&RXaddr.rxaddr,&addrlen); + + if (nread > 0) + { + PIPMSG IPptr = (PIPMSG)&Buffer[EthOffset]; + + // RouteIPMsg(IPptr); + ProcessIPMsg(IPptr, Buffer, 'E', 255); + + goto PollEncaploop; + } + else + res = GetLastError(); + } + + + + + if (IPHOSTVECTORPTR->HOSTTRACEQ != 0) + { + PBUFFHEADER axmsg; + PBUFFHEADER savemsg; + + axmsg = (PBUFFHEADER)IPHOSTVECTORPTR->HOSTTRACEQ; + + IPHOSTVECTORPTR->HOSTTRACEQ = axmsg->CHAIN; + + savemsg=axmsg; + + // Packet from AX.25 + + if (axmsg->DEST[0] == 0xCF) // Netrom + { + // Could we use the More bit for fragmentation?? + // Seems a good idea, but would'nt be compatible with NOS + + // Have to move message down buffer + + UCHAR TEMP[7]; + + memmove(TEMP, &axmsg->DEST[1], 7); + memmove(axmsg->DEST, &axmsg->ORIGIN[1], 7); + memmove(axmsg->ORIGIN, TEMP, 7); + axmsg->PID = 0xCC; + + memmove(&axmsg->PID + 1, &axmsg->PID + 6, axmsg->LENGTH); + axmsg->PORT = 0; + } + + if (CompareCalls(axmsg->ORIGIN, MYCALL)) + { + ReleaseBuffer(axmsg); + return 0; // Echoed packet + } + + switch (axmsg->PID) + { + case 0xcc: + + // IP Message, + + { + PIPMSG ipptr = (PIPMSG)++axmsg; + axmsg--; + ProcessIPMsg(ipptr, axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT); + break; + } + + case 0xcd: + + // ARP Message + + ProcessAXARPMsg((PAXARP)axmsg); + SaveARP(); + break; + + case 0x08: + + // Fragmented message + + // The L2 code ensures that the last fragment is present before passing the + // message up to us. It is just possible that bits are missing + { + UCHAR * ptr = &axmsg->PID; + UCHAR * nextfrag; + + int frags; + int len; + + ptr++; + + if (!(*ptr & 0x80)) + break; // Not first fragment??? + + frags=*ptr++ & 0x7f; + + len = axmsg->LENGTH; + + len-= sizeof(BUFFHEADER); + len--; // Remove Frag Control Byte + + memcpy(&Buffer[EthOffset], ptr, len); + + nextfrag = &Buffer[EthOffset]+len; + + // Release Buffer +fragloop: + ReleaseBuffer(savemsg); + + if (IPHOSTVECTORPTR->HOSTTRACEQ == 0) goto Pollloop; // Shouldn't happen + + axmsg = (PBUFFHEADER)IPHOSTVECTORPTR->HOSTTRACEQ; + IPHOSTVECTORPTR->HOSTTRACEQ = axmsg->CHAIN; + savemsg=axmsg; + + ptr = &axmsg->PID; + ptr++; + + if (--frags != (*ptr++ & 0x7f)) + break; // Out of sequence + + len = axmsg->LENGTH; + + len-= sizeof(BUFFHEADER); + len--; // Remove Frag Control Byte + + memcpy(nextfrag, ptr, len); + + nextfrag+=len; + + if (frags != 0) goto fragloop; + + ProcessIPMsg((PIPMSG)&Buffer[EthOffset+1],axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT); + + break; + } + + } + // Release the buffer + + ReleaseBuffer(savemsg); + + goto Pollloop; + } + return TRUE; +} + + +BOOL Send_ETH(VOID * Block, DWORD len, BOOL SendtoTAP) +{ + // On Windows we don't use TAP so everything goes to pcap + + if (len < 60) len = 60; + +#ifndef WIN32 + if (SendtoTAP) + { + if (tap_fd) + write(tap_fd, Block, len); + + return TRUE; + } +#endif + + // On Windows we don't use TAP so everything goes to pcap + + if (adhandle) + { + // Send down the packet + + pcap_sendpacketx(adhandle, // Adapter + Block, // buffer with the packet + len); // size + } + return TRUE; +} + +#define AX25_P_SEGMENT 0x08 +#define SEG_REM 0x7F +#define SEG_FIRST 0x80 + +static VOID Send_AX_Datagram(PMESSAGE Block, DWORD Len, UCHAR Port, UCHAR * HWADDR) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(Block->DEST, HWADDR, 7); + memcpy(Block->ORIGIN, MYCALL, 7); + Block->DEST[6] &= 0x7e; // Clear End of Call + Block->ORIGIN[6] |= 1; // Set End of Call + Block->CTL = 3; //UI + +#ifdef LINBPQ + + if (Port == 99) // BPQETHER over BPQTUN Port + { + // Add BPQETHER Header + + int txlen = Block->LENGTH; + UCHAR txbuff[1600]; + + if (txlen < 1 || txlen > 400) + return; + + // Length field is little-endian + + // BPQEther Header is 14 bytes before the Length + + txbuff[14]=(txlen & 0xff); + txbuff[15]=(txlen >> 8); + + + memcpy(&txbuff[16],&Block[7],txlen); + + //memcpy(&txbuff[0],&EthDest[0],6); + //memcpy(&txbuff[6],&EthSource[0],6); + //memcpy(&txbuff[12],&EtherType,2); + + write(tap_fd, Block, Len); + return; + } + +#endif + + Send_AX(Block, Len, Port); + return; + +} +VOID Send_AX_Connected(VOID * Block, DWORD Len, UCHAR Port, UCHAR * HWADDR) +{ + DWORD PACLEN = 256; + int first = 1, fragno, txlen; + UCHAR * p; + + // Len includes the 16 byte ax header (Addr CTL PID) + + // if Len - 16 is greater than PACLEN, then fragment (only possible on Virtual Circuits, + // as fragmentation relies upon reliable delivery + + + if ((Len - 16) <= PACLEN) // No need to fragment + { + SendNetFrame(HWADDR, MYCALL, Block, Len, Port); + return; + } + + Len = Len-16; // Back to real length + + PACLEN-=2; // Allow for fragment control info) + + fragno = Len / PACLEN; + + if (Len % PACLEN == 0) fragno--; + + p = Block; + p += 20; + + while (Len > 0) + { + *p++ = AX25_P_SEGMENT; + + *p = fragno--; + + if (first) + *p |= SEG_FIRST; + + txlen = (PACLEN > Len) ? Len : PACLEN; + + Debugprintf("Send IP to VC Fragment, Len = %d", txlen); + + // Sobsequent fragments only add one byte (the PID is left in place) + + if (first) + SendNetFrame(HWADDR, MYCALL, p-23, txlen+18, Port); + else + { + SendNetFrame(HWADDR, MYCALL, p-23, txlen+17, Port); // only one frag byte + p--; + } + first = 0; + p += (txlen); + Len -= txlen; + } + return; +} + + +static VOID SendNetFrame(UCHAR * ToCall, UCHAR * FromCall, UCHAR * Block, DWORD Len, UCHAR Port) +{ +// ATTACH FRAME TO OUTBOUND L3 QUEUES (ONLY USED FOR IP ROUTER) + + struct DATAMESSAGE * buffptr; + struct _LINKTABLE * LINK; + struct PORTCONTROL * PORT; + + if (Len > MAXDATA) + return; + + if (QCOUNT < 100) + return; + + memcpy(&Block[7],ToCall, 7); + memcpy(&Block[14],FromCall, 7); + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers + + Len -= 15; // We added 16 before (for UI Header) but L2 send includes the PID, so is one more than datalength + + buffptr->LENGTH = (USHORT)Len + MSGHDDRLEN; + +// SEE IF L2 OR L3 + + if (Port == 0) // L3 + { + struct DEST_LIST * DEST; + L3MESSAGE * L3Header; + + if (FindDestination(ToCall, &DEST) == 0) + { + ReleaseBuffer(buffptr); + return; + } + + // We have to build the Netrom Header + + L3Header = (L3MESSAGE *)&buffptr->L2DATA[0]; + + buffptr->PID = 0xCF; // NETROM + + memcpy(L3Header->L3SRCE, FromCall, 7); + memcpy(L3Header->L3DEST, ToCall, 7); + L3Header->L3TTL = L3LIVES; + L3Header->L4FLAGS = 0; // Opcode + L3Header->L4ID = 0x0C; // IP + L3Header->L4INDEX = 0x0C; + + memcpy(L3Header->L4DATA, &Block[23], Len); // Dond Send PID - implied by OCOC above + + buffptr->LENGTH += 20; // Netrom Header + + C_Q_ADD(&DEST->DEST_Q, buffptr); + return; + } + +// SEND FRAME TO L2 DEST, CREATING A LINK ENTRY IF NECESSARY + + memcpy(&buffptr->PID, &Block[22], Len); + + if (FindLink(ToCall, FromCall, Port, &LINK)) + { + // Have a link + + C_Q_ADD(&LINK->TX_Q, buffptr); + return; + } + + if (LINK == NULL) // No spare space + { + ReleaseBuffer(buffptr); + return; + } + + LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + return; // maybe port has been deleted + + LINK->L2TIME = PORT->PORTT1; // SET TIMER VALUE + + if (ToCall[7]) + LINK->L2TIME += PORT->PORTT1; // Extend Timer for Digis + + LINK->LINKWINDOW = PORT->PORTWINDOW; + + LINK->L2STATE = 2; + + memcpy(LINK->LINKCALL, ToCall, 7); + memcpy(LINK->OURCALL, FromCall, 7); + memcpy(LINK->DIGIS, &ToCall[7], 56); + + LINK->LINKTYPE = 2; // Dopwnlink + + SENDSABM(LINK); + + C_Q_ADD(&LINK->TX_Q, buffptr); + return; +} + +VOID ProcessEthIPMsg(PETHMSG Buffer) +{ + PIPMSG ipptr = (PIPMSG)&Buffer[1]; + struct nat_table_entry * NAT = NULL; + int index; + + if (memcmp(Buffer, ourMACAddr,6 ) != 0) + return; // Not for us + + if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0) + return; // Discard our sends + + // See if from a NAT'ed address + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->mappedipaddr == ipptr->IPSOURCE.addr) + { + ipptr->IPSOURCE.addr = NAT->origipaddr; + + ipptr->IPCHECKSUM = 0; // to force cksum recalc below + break; + } + } + + // if Checkum offload is active we get the packet before the NIC sees it (from PCAP) + + if (ipptr->IPCHECKSUM == 0) // Windows seems to do this + { + // Generate IP and TCP/UDP checksums + + int Len = ntohs(ipptr->IPLENGTH); + + if (Len == 0) + { + return; + } + Len-=20; + + ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20); + + if (ipptr->IPPROTOCOL == 6) // TCP + { + PTCPMSG TCP = (PTCPMSG)&ipptr->Data; + PHEADER PH = {0}; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &ipptr->IPDEST, 4); + + TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len); + } + } + + if (ipptr->IPDEST.addr == EncapAddr) + { + if (ipptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet + { + memcpy(RouterMac, Buffer->SOURCE, 6); + ProcessTunnelMsg(ipptr); + } + return; // Ignore Others + } + + ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255); +} + +VOID ProcessEthARPMsg(PETHARP arpptr, BOOL FromTAP) +{ + int i=0, Mask=IPPortMask; + PARPDATA Arp; + PROUTEENTRY Route; + BOOL Found; + + if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 ) + return; // Discard our sends + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + + // Is it for our ENCAP (not on our 44 LAN) + +// printf("ARP Request for %08x Tell %08x\n", +// arpptr->TARGETIPADDR, arpptr->SENDIPADDR); + + // Process anything for 44 from TAP + // (or should the be from either..???) + +// if (FromTAP && (arpptr->TARGETIPADDR & 0xff) == 44) + if ((arpptr->TARGETIPADDR & 0xff) == 44) + goto ARPOk; + + if (arpptr->TARGETIPADDR != EncapAddr) + { + // We should only accept requests from our subnet - we might have more than one net on iterface + + if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask)) + { + // Discard Unless it is from a NAT'ed Host + + struct nat_table_entry * NAT = NULL; + int index; + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->mappedipaddr == arpptr->SENDIPADDR) + break; + } + + if (index >= nat_table_len) + return; + + // Also check it is for a 44. address or we send all LAN ARPS to + // RF + + if ((arpptr->TARGETIPADDR & 0xff) != 44) + return; + + } + + if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0 + return; + + } + + // Add to our table, as we will almost certainly want to send back to it +ARPOk: +// Debugprintf("ARP Request for %08x Tell %08x\n", +// arpptr->TARGETIPADDR, arpptr->SENDIPADDR); + + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp == NULL) return; // No point if table full + + Arp->IPADDR = arpptr->SENDIPADDR; + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + // Also add to routes + + AddToRoutes(Arp,arpptr->SENDIPADDR, 'E'); + + SaveARP(); + +AlreadyThere: + + if (arpptr->TARGETIPADDR == OurIPAddr || arpptr->TARGETIPADDR == EncapAddr) + { + uint32_t Save = arpptr->TARGETIPADDR; + + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6); + memcpy(arpptr->SENDHWADDR, ourMACAddr ,6); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = Save; + + memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6); + memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6); + +// Debugprintf("Forus ARP Reply for %08x Targ %08x HNAT %08x\n", +// arpptr->SENDIPADDR, arpptr->TARGETIPADDR, HostNATAddr); + + Send_ETH(arpptr,60, FromTAP); + + return; + } + + // If for our Ethernet Subnet, Ignore or we send loads of unnecessary msgs to ax.25 + + // Actually our subnet could be subnetted further + + // So respond for NAT'ed addresses + + // Why not just see if we have a route first?? + + Route = FindRoute(arpptr->TARGETIPADDR); + + if (Route) + { + if (Route->TYPE == 'T' || Route->TYPE == 'U') + goto ProxyARPReply; // Assume we can always reach via tunnel + + Arp = LookupARP(Route->GATEWAY, FALSE, &Found); + + if (Arp) + { + if(Arp->ARPVALID && (Arp->ARPTYPE == 'E')) + return; // On LAN, so should reply direct + + goto ProxyARPReply; + } + } + + if ((arpptr->TARGETIPADDR & OurNetMask) == (OurIPAddr & OurNetMask)) + { + // Unless for a NAT'ed address, in which case we reply with our virtual MAC + + struct nat_table_entry * NAT = NULL; + int index; + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->origipaddr == arpptr->TARGETIPADDR) + break; + } + + if (index >= nat_table_len) + return; + + goto ProxyARPReply; + + } + // Should't we just reply if we know it ?? (Proxy ARP) + + // Maybe, but that may mean dowstream nodes dont learnit + + // Sould we look in routes table, as we may have a gateway to it. + + Route = FindRoute(arpptr->TARGETIPADDR); + + if (Route) + { + if (Route->TYPE == 'T') + goto ProxyARPReply; // Assume we can always reach via tunnel + + Arp = LookupARP(Route->GATEWAY, FALSE, &Found); + + if (Arp) + { + if(Arp->ARPVALID && (Arp->ARPTYPE == 'E')) + return; // On LAN, so should reply direct +ProxyARPReply: + ETHARPREQMSG.TARGETIPADDR = arpptr->SENDIPADDR; + ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply + ETHARPREQMSG.SENDIPADDR = arpptr->TARGETIPADDR; + + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + memcpy(ETHARPREQMSG.MSGHDDR.DEST, arpptr->SENDHWADDR, 6); + +// Debugprintf("Proxy ARP Reply for %08x Targ %08x HNAT %08x\n", +// ETHARPREQMSG.SENDIPADDR, ETHARPREQMSG.TARGETIPADDR, HostNATAddr); + + // We send to TAP if request from TAP +// Send_ETH(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); + Send_ETH(ÐARPREQMSG, 42, FromTAP); + return; + } + } + + // Not in our cache, so send to all other ports enabled for IP, reformatting as necessary + + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + memset(AXARPREQMSG.TARGETHWADDR, 0, 7); + AXARPREQMSG.ARPOPCODE = 0x0100; + + for (i=1; i<=NUMBEROFPORTS; i++) + { + if (Mask & 1) + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, i, QST); + + Mask>>=1; + } + + break; + + + case 0x0200: + + if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 ) + return; // Not for us + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + + // Also add to routes + + AddToRoutes(Arp, arpptr->SENDIPADDR, 'E'); +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + SaveARP(); + +SendBack: + + // Send Back to Originator of ARP Request + + if (Arp && arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + { + struct DATAMESSAGE * buffptr; + PIPMSG IPptr; + + while (Arp->ARP_Q) + { + buffptr = Q_REM_NP(&Arp->ARP_Q); + IPptr = (PIPMSG)&buffptr->L2DATA[30]; + RouteIPMsg(IPptr); + free(buffptr); + } + + break; + } + + Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found); + + if (Found) + { + if (Arp->ARPINTERFACE == 255) + { + ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply + ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + memcpy(ETHARPREQMSG.MSGHDDR.DEST, Arp->HWADDR, 6); + + Send_ETH(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); + return; + } + else + { + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.ARPOPCODE = 0x0200; // Reply + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, Arp->HWADDR); + + return; + } + } + break; + + default: + break; + } + return; +} + +VOID ProcessAXARPMsg(PAXARP arpptr) +{ + int i=0, Mask=IPPortMask; + PARPDATA Arp; + PROUTEENTRY Route; + + BOOL Found; + + arpptr->MSGHDDR.ORIGIN[6] &= 0x7e; // Clear end of Call + + if (memcmp(arpptr->MSGHDDR.ORIGIN, MYCALL, 7) == 0) // ?Echoed packet? + return; + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + { + // Add to our table, as we will almost certainly want to send back to it + + if (arpptr->TARGETIPADDR == 0) + return; // Ignore 0.0.0.0 + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp != NULL) + { + // ENTRY NOT FOUND - IF ANY SPARE ENTRIES, USE ONE + + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,7); + + Arp->ARPTYPE = 'D'; + Arp->ARPINTERFACE = arpptr->MSGHDDR.PORT; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + // Also add to routes + + AddToRoutes(Arp, arpptr->SENDIPADDR, 'D'); + } + +AlreadyThere: + + if (arpptr->TARGETIPADDR == OurIPAddr) + { + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR, 7); + memcpy(arpptr->SENDHWADDR, MYCALL, 7); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = OurIPAddr; + + Send_AX_Datagram((PMESSAGE)arpptr, 46, arpptr->MSGHDDR.PORT, arpptr->MSGHDDR.ORIGIN); + + return; + } + + // Should't we just reply if we know it ?? (Proxy ARP) + + // Maybe, but that may mean dowstream nodes dont learnit + + // Sould we look in routes table, as we may have a gateway to it. + + Route = FindRoute(arpptr->TARGETIPADDR); + + if (Route) + { + if (Route->TYPE == 'T') + goto AXProxyARPReply; // Assume we can always reach via tunnel + + + Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found); + + if (Found) + { + // if Trarget is the station we got the request from, there is a loop + // KIll the ARP entry, and ignore + + if (memcmp(Arp->HWADDR, arpptr->SENDHWADDR, 7) == 0) + { + RemoveARP(Arp); + return; + } + +AXProxyARPReply: + + AXARPREQMSG.ARPOPCODE = 0x0200; // Reply + AXARPREQMSG.TARGETIPADDR = arpptr->SENDIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->TARGETIPADDR; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, arpptr->SENDHWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, arpptr->MSGHDDR.PORT, arpptr->SENDHWADDR); + + return; + } + } + + // Not in our cache, so send to all other ports enabled for IP, reformatting as necessary + + AXARPREQMSG.ARPOPCODE = 0x0100; + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + for (i=1; i<=NUMBEROFPORTS; i++) + { + if (i != arpptr->MSGHDDR.PORT) + if (Mask & 1) + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, i, QST); + + Mask>>=1; + } + + memset(ETHARPREQMSG.MSGHDDR.DEST, 0xff, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + + ETHARPREQMSG.ARPOPCODE = 0x0100; + ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + memcpy(ETHARPREQMSG.SENDHWADDR, ourMACAddr, 6); + + Send_ETH(ÐARPREQMSG, 42, FALSE); + + break; + } + + case 0x0200: + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + + // Also add to routes + + AddToRoutes(Arp, arpptr->SENDIPADDR, 'D'); +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,7); + Arp->ARPTYPE = 'D'; + Arp->ARPINTERFACE = arpptr->MSGHDDR.PORT; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + +SendBack: + + // Send Back to Originator of ARP Request + + if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + break; + + Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found); + + if (Found) + { + if (Arp->ARPINTERFACE == 255) + { + ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply + + ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr,6); + memcpy(ETHARPREQMSG.TARGETHWADDR, Arp->HWADDR, 6); + memcpy(ETHARPREQMSG.MSGHDDR.DEST, Arp->HWADDR, 6); + + Send_ETH(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); + return; + } + else + { + AXARPREQMSG.ARPOPCODE = 0x0200; // Reply + + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, Arp->HWADDR); + + return; + } + } + + break; + + + default: + + return; + } +} + +VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port) +{ + uint32_t Dest; + PARPDATA Arp; + PROUTEENTRY Route; + BOOL Found; + PUDPMSG UDPptr; + + if (IPptr->VERLEN != 0x45) + { + Dest = IPptr->IPDEST.addr; + Debugprintf("IP Pkt not 45 for %s\n", FormatIP(Dest)); + return; // Only support Type = 4, Len = 20 + } + if (!CheckIPChecksum(IPptr)) + { + Dest = IPptr->IPDEST.addr; + Debugprintf("IP Pkt Bad CKSUM for %s\n", FormatIP(Dest)); + return; + } + // Make sure origin ia routable. If not, add to ARP + + Route = FindRoute(IPptr->IPSOURCE.addr); + + if (Route == NULL) + { + Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPptr->IPSOURCE.addr; + + if (Type == 'E') + { + memcpy(Arp->HWADDR, MACADDR, 6); + } + else + { + memcpy(Arp->HWADDR, MACADDR, 7); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = Type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + // Also add to routes + + AddToRoutes(Arp, IPptr->IPSOURCE.addr, Type); + + SaveARP(); + } + } + } + + if (Route && Route->ARP) + Route->ARP->ARPTIMER = ARPTIMEOUT; // Refresh + + // See if for us - if not pass to router + + Dest = IPptr->IPDEST.addr; +// Debugprintf("IP Pkt for %s\n", FormatIP(Dest)); + + if (Dest == OurIPAddr) + goto ForUs; + + RouteIPMsg(IPptr); + + return; + +ForUs: + + // We now pass stuff addressed to us to the host, unless it is a reponse + // to our ping request + + // Not sure what to do with snmp (maybe option) + +// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet +// { +// ProcessTunnelMsg(IPptr); +// return; +// } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Debugprintf("FORUS ICMP Type %d", ICMPptr->ICMPTYPE); + + if (Len == 28 && ICMPptr->ICMPTYPE == 0 && memcmp(ICMPptr->ICMPData, "*BPQ", 4) == 0) + { + // Probably our response + + ProcessICMPMsg(IPptr); + return; + } + } + + // Support UDP for SNMP + + if (BPQSNMP && IPptr->IPPROTOCOL == 17) // UDP + { + UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // Pass rest to host + + RouteIPMsg(IPptr); + return; +} + +unsigned short cksum(unsigned short *ip, int len) +{ + uint32_t sum = 0; /* assume 32 bit long, 16 bit short */ + unsigned short copy [2048]; + + // if not word aligned copy + + if (len > 1024 || len < 0 || ip == 0) + Debugprintf("%d", len); + + +// if (&ip & 1) + { + memcpy(copy, ip, len); + ip = copy; + } + + while(len > 1) + { + sum += *(ip++); + if(sum & 0x80000000) /* if high order bit set, fold */ + sum = (sum & 0xFFFF) + (sum >> 16); + len -= 2; + } + + if(len) /* take care of left over byte */ + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (unsigned short)sum; +} + +BOOL CheckIPChecksum(PIPMSG IPptr) +{ + USHORT checksum=0; + + if (IPptr->IPCHECKSUM == 0) + return TRUE; //Not used + + checksum = cksum((unsigned short *)IPptr, 20); + + if (checksum == 0xffff) return TRUE; else return FALSE; + +} +BOOL Check_Checksum(VOID * ptr1, int Len) +{ + USHORT checksum; + + checksum = cksum((unsigned short *)ptr1, Len); + + if (checksum == 0xffff) return TRUE; else return FALSE; + +} +USHORT Generate_CHECKSUM(VOID * ptr1, int Len) +{ + USHORT checksum=0; + + checksum = cksum((unsigned short *)ptr1, Len); + + return ~checksum ; +} + +VOID ProcessTunnelMsg(PIPMSG IPptr) +{ + UCHAR * ptr; + PIPMSG Outer = IPptr; // Save tunnel header + int Origlen; +// int InnerLen; + + // Check header length - for now drop any message with options + + if (IPptr->VERLEN != 0x45) + return; + + Origlen = htons(Outer->IPLENGTH); + + ptr = (UCHAR *)IPptr; + ptr += 20; // Skip IPIP Header + IPptr = (PIPMSG) ptr; + + // If we are relaying it from a DMZ host there will be another header + + if (IPptr->IPPROTOCOL == 4) // IPIP + { + Outer = IPptr; + ptr = (UCHAR *)IPptr; + ptr += 20; // Skip IPIP Header + IPptr = (PIPMSG) ptr; + Origlen -= 20; + } + + // First check for RIP44 Messages + + if (IPptr->IPPROTOCOL == 17) // UDP + { + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + + if (IPptr->IPSOURCE.addr == UCSD44 && UDPptr->DESTPORT == htons(520)) + { + ProcessRIP44Message(IPptr); + return; + } + } + + // for now drop anything not from a 44 address. + // now also need to drop 44.192/10 as it has been sold to Amazon + + if (IPptr->IPSOURCE.S_un_b.s_b1 != 44) + return; + + // Drop 44.192 - 44.255 + + if (IPptr->IPSOURCE.S_un_b.s_b2 >= 192) + return; + + // See if for us + + // Handle Pings, our Ping responses and SNMP in BPQ + // pass rest to host + + if (IPptr->IPDEST.addr == OurIPAddr) + { + if (IPptr->IPPROTOCOL == 1) // ICMP + { + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + if (Len == 28 && ICMPptr->ICMPTYPE == 0 && memcmp(ICMPptr->ICMPData, "*BPQ", 4) == 0) + { + // Probably ours + + ProcessICMPMsg(IPptr); + return; + } + } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + if (ICMPptr->ICMPTYPE == 8) + { + ProcessICMPMsg(IPptr); + return; + } + } + + // Support UDP for SNMP + + if (IPptr->IPPROTOCOL == 17) // UDP + { + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // Others should be passed to our host + + // I think we can just drop through to RouteIPMsg + + } + + // I think anything else is just passed to the router + + RouteIPMsg(IPptr); +} + +VOID ProcessRIP44Message(PIPMSG IPptr) +{ + int Len; + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + PRIP2HDDR HDDR = (PRIP2HDDR)&UDPptr->UDPData; + PRIP2ENTRY RIP2; + + Len = ntohs(IPptr->IPLENGTH); + Len -= 20; + + if (UDPptr->CHECKSUM) + if (Check_Checksum(UDPptr, Len) == FALSE) + return; + + if (HDDR->Command != 2 || HDDR->Version != 2) + return; + + RIP2 = (PRIP2ENTRY) ++HDDR; + + Len -= 12; // UDP and RIP Headers + + if (RIP2->AddrFamily == 0xFFFF) + { + // Authentication Entry + + Len -= 20; + RIP2++; + } + + while (Len >= 20) // Entry Length + { + // See if already in table + + PROUTEENTRY Route; + BOOL Found; + + // if for our subnet, ignore + + // Actually don't need to, as we won't overwrite the preconfigured + // interface route + + Route = LookupRoute(RIP2->IPAddress, RIP2->Mask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL && RIP2->Metric < 16) + { + Route->NETWORK = RIP2->IPAddress; + Route->SUBNET = RIP2->Mask; + Route->METRIC = RIP2->Metric; + Route->Encap = RIP2->NextHop; + Route->TYPE = 'T'; + Route->RIPTIMOUT = 3600; // 1 hour for now + } + } + else + { + // Already in table + + // Should we replace an RF route with an ENCAP route?? + // For now, no. May make an option later + // Should never replace our interface routes + + if (Route->TYPE == 'T') + { + // See if same Encap, and if not, is this better metric? + + // Is this possible with RIP44?? + + if (Route->Encap != RIP2->NextHop) + { + if (Route->METRIC >= RIP2->Metric) + { + // Should also change if equal, as dynamic address could have changed + + Route->METRIC = RIP2->Metric; + Route->Encap = RIP2->NextHop; + } + } + + Route->METRIC = RIP2->Metric; + + if (RIP2->Metric >= 15) + { + // HE IS TELLING US ITS UNREACHABLE - START DELETE TIMER + + Route->RIPTIMOUT = 0; + if (Route->GARTIMOUT == 0) + Route ->GARTIMOUT = 4; + } + else + { + Route->RIPTIMOUT = 3600; // 1 hour for now + Route->GARTIMOUT = 0; // In case started to delete + } + } + } + + Len -= 20; + RIP2++; + } + + SaveIPRoutes(); + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); + LastRIP44Msg = time(NULL); +} + + +VOID ProcessICMPMsg(PIPMSG IPptr) +{ + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(ICMPptr, Len); + + if (ICMPptr->ICMPTYPE == 8) + { + // ICMP_ECHO + + ICMPptr->ICMPTYPE = 0; // Convert to Reply + + // CHECKSUM IT + + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPTTL = IPTTL; + + // RouteIPMsg redoes checksum + + RouteIPMsg(IPptr); // Send Back + return; + } + + if (ICMPptr->ICMPTYPE == 0) + { + // ICMP_REPLY: + + // It could be a reply to our request + // or from a our host pc + + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + TRANSPORTENTRY * Session = L4TABLE; + char IP[20]; + unsigned char work[4]; + + // Internal Pings have Length 28 and Circuit Index as ID + + if (Len > 28 || ICMPptr->ICMPID >= MAXCIRCUITS) + { + // For Host + + ReleaseBuffer(BUFFER); + RouteIPMsg(IPptr); + return; + } + + Session += ICMPptr->ICMPID; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[7]; + + memcpy(work, &IPptr->IPSOURCE, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "Ping Response from %s", IP); + + *ptr1++ = 0x0d; // CR + + Len = (int)(ptr1 - BUFFER); + + Msg = (struct _MESSAGE *)BUFFER; + Msg->LENGTH = Len; + Msg->CHAIN = NULL; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(Session); + + return; + } +} + + +VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2) +{ + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + UCHAR * ptr; + + if (OurIPAddr == 0) + return; // Can't do much without one + + if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11) + return; // Don't send Time Exceeded for TimeExceded + + // Copy the Original IP Header and first 8 bytes of packet down the buffer + + ptr = (UCHAR *) ICMPptr; + + memmove(ptr + 8, IPptr, 28); // IP header plus 8 data + +// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data + + memset (ICMPptr, 0, 8); + ICMPptr->ICMPTYPE = Type; + ICMPptr->ICMPCODE = Code; + ICMPptr->ICMPSEQUENCE = htons(P2); + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36); + + RouteIPMsg(IPptr); +} + +VOID SendICMPTimeExceeded(PIPMSG IPptr) +{ + SendICMPMessage(IPptr, 11, 0, 0); + return; +} + +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap) +{ + union + { + struct sockaddr_in txaddr; + struct sockaddr_in6 txaddr6; + } TXaddr = {0}; + + int sent; + int addrlen = sizeof(struct sockaddr_in6); + int Origlen; + + TXaddr.txaddr.sin_family = AF_INET; + Origlen = htons(IPptr->IPLENGTH); + + // If we are using PCAP interface we have to add IPIP and MAC Headers. + + if (EncapAddr != INADDR_NONE) + { + UCHAR IPCopy[2048]; // Need Space to add headers + PETHMSG Ethptr = (PETHMSG)IPCopy; + PIPMSG Outer = (PIPMSG)&IPCopy[14]; + + memset(IPCopy, 0, 34); // Eth + IP Headers + + Outer->VERLEN = 0x45; + Outer->IPDEST.addr = Encap; + Outer->IPSOURCE.addr = EncapAddr; + Outer->IPPROTOCOL = 4; + Outer->IPTTL = IPTTL; + Outer->IPID = IPptr->IPID; + Outer->IPLENGTH = htons(Origlen + 20); + memcpy(&Outer->Data, IPptr, Origlen); + + Outer->IPCHECKSUM = 0; + Outer->IPCHECKSUM = Generate_CHECKSUM(Outer, 20); + + memcpy(Ethptr->DEST, RouterMac, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + Send_ETH(Ethptr, Origlen + 34, FALSE); + + return; + + } + if (UDPEncap) + { + UCHAR * ptr; + + memcpy(&TXaddr, &RXaddr, sizeof(struct sockaddr_in6)); + TXaddr.txaddr.sin_port = htons(UDPPort); + + // UDP Processor Needs the Encap Address, but we don't need the IPIP hearer + // as that is added by the raw send later. Just stick it on the end. + + + ptr = (UCHAR *)IPptr; + memcpy(ptr + Origlen, &Encap, 4); + Origlen += 4; + } + else + memcpy(&TXaddr.txaddr.sin_addr, &Encap, 4); + + + sent = sendto(EncapSock, (char *)IPptr, Origlen, 0, (struct sockaddr *)&TXaddr, addrlen); + sent = GetLastError(); + +} + + +BOOL RouteIPMsg(PIPMSG IPptr) +{ + PARPDATA Arp, ARPptr; + PROUTEENTRY Route; + BOOL Found; + struct nat_table_entry * NAT = NULL; + int index; + BOOL SendtoTAP = FALSE; // used on LinBPQ for NAT to This Host + + // Decremnent TTL and Recalculate header checksum + + IPptr->IPTTL--; + + if (IPptr->IPTTL == 0) + { + SendICMPTimeExceeded(IPptr); + return FALSE; + } + + // See if for a NATed Address + +// Debugprintf("RouteIPFrame IP %s\n", FormatIP(IPptr->IPDEST.addr)); + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + // for the moment only map all ports + + if (NAT->origipaddr == IPptr->IPDEST.addr) + { + char Msg[80]; + int ptr; + IPLen = htons(IPptr->IPLENGTH); + + ptr = sprintf(Msg, "NAT %s to ", FormatIP(IPptr->IPDEST.addr)); + sprintf(&Msg[ptr], "%s\n", FormatIP(NAT->mappedipaddr)); + +// Debugprintf("%s", Msg); + + IPptr->IPDEST.addr = NAT->mappedipaddr; + + if (IPptr->IPPROTOCOL == 6) + RecalcTCPChecksum(IPptr); + else if (IPptr->IPPROTOCOL == 17) + RecalcUDPChecksum(IPptr); + else if (IPptr->IPPROTOCOL == 1) + { + // ICMP. If it has an inner packet (Time Exceeded or Need to Fragment) + // we also need to un-NAT the inner packet. + + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + if (ICMPptr->ICMPTYPE == 3 || ICMPptr->ICMPTYPE == 11) + { + PIPMSG IPptr = (PIPMSG)ICMPptr->ICMPData; + + Debugprintf("NAT ICMP Unreachable or Time Exceeded %d", ICMPptr->ICMPTYPE); + IPptr->IPSOURCE.addr = NAT->mappedipaddr; + + // Dest could also need to be de-natted + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->mappedipaddr == IPptr->IPDEST.addr) + { + IPptr->IPDEST.addr = NAT->origipaddr; + break; + } + } + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, IPLen - 20); + } + } + + + SendtoTAP = NAT->ThisHost; + break; + } + } + + if (IPptr->IPDEST.addr == HostNATAddr) + SendtoTAP = TRUE; + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Everything is in the routes table, even arp-derived routes. so just look there + + Route = FindRoute(IPptr->IPDEST.addr); + + if (Route == NULL) + return FALSE; // ?? Dest unreachable ?? + + Route->FRAMECOUNT++; + + Arp = Route->ARP; + + if (Arp == NULL) + { + if (Route->TYPE == 'T') + { + SendIPtoEncap(IPptr, Route->Encap); + return TRUE; + } + + else if (Route->TYPE == 'U') + { + int sent; + int addrlen = sizeof(struct sockaddr_in6); + int Origlen; + + Origlen = htons(IPptr->IPLENGTH); + + sent = sendto(UDPSendSock, (char *)IPptr, Origlen, 0, (struct sockaddr *)&Route->UDPADDR, addrlen); + return TRUE; + } + + + // Look up target address in ARP + + if (Route->GATEWAY) + Arp = LookupARP(Route->GATEWAY, FALSE, &Found); + else + Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found); + + if (!Found) + { + if (Route->GATEWAY == 0) // Interace Route + { + ARPptr = AllocARPEntry(); + if (ARPptr != NULL) + { + struct DATAMESSAGE * buffptr; + Route->ARP = ARPptr; + ARPptr->ARPROUTE = Route; + ARPptr->ARPINTERFACE = 255; + ARPptr->ARPTIMER = 5; + ARPptr->ARPTYPE = 'E'; + ARPptr->IPADDR = IPptr->IPDEST.addr; + + // Save a copy to send on if ARP reply received + + buffptr = malloc(2048); + + if (buffptr) + { + if (ntohs(IPptr->IPLENGTH) > 1600) + { + Debugprintf("Overlength IP Packet %d" , ntohs(IPptr->IPLENGTH)); + return TRUE; + } + memcpy(&buffptr->L2DATA[30], IPptr, ntohs(IPptr->IPLENGTH)); + C_Q_ADD_NP(&ARPptr->ARP_Q, buffptr); + } + + SendARPMsg(ARPptr, SendtoTAP); + + return TRUE; + } + } + return FALSE; + } + } + + if (Arp->ARPVALID) + { + if (Arp->ARPTYPE == 'T') + SendIPtoEncap(IPptr, Route->Encap); + else + if (Arp->ARPTYPE == 'E') + SendIPtoEther(IPptr, Arp->HWADDR, SendtoTAP); + else + SendIPtoAX25(IPptr, Arp->HWADDR, Arp->ARPINTERFACE, Arp->ARPTYPE); + + return TRUE; + } + return FALSE; +} + +VOID SendIPtoEther(PIPMSG IPptr, UCHAR * HWADDR, BOOL SendtoTAP) +{ + // AX.25 headers are bigger, so there will always be room in buffer for enet header + + PETHMSG Ethptr = (PETHMSG)IPptr; + int Len; + + (UCHAR *)Ethptr--; + + Len = ntohs(IPptr->IPLENGTH); + + Len+=14; // Add eth Header + + memcpy(Ethptr->DEST, HWADDR, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + + Send_ETH(Ethptr,Len, SendtoTAP); + + return; +} + +VOID SendIPtoAX25(PIPMSG IPptr, UCHAR * HWADDR, int Port, char Mode) +{ + PBUFFHEADER Msgptr = (PBUFFHEADER)IPptr; + int Len; + USHORT FRAGWORD = ntohs(IPptr->FRAGWORD); + USHORT OrigFragWord = FRAGWORD; + int PACLEN = 256; + + + (UCHAR *)Msgptr--; + Msgptr->PID = 0xcc; //IP + + if (Port == 0) // NETROM + PACLEN = 235; + + Len = ntohs(IPptr->IPLENGTH); + + // Don't fragment VC stuff + + if (Mode == 'V') // Virtual Circuit + { + Send_AX_Connected((PMESSAGE)Msgptr, Len + 16, Port, HWADDR); + return; + } + + while (Len > PACLEN) + { + // Need to Frgament + + USHORT Fraglen; // Max Fragment Size (PACLEN rounded down to 8 boundary)) + USHORT Datalen; // Data Content)) + + UCHAR * ptr1 = &IPptr->Data; + + //Bit 0: reserved, must be zero + //Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. + //Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. + //Fragment Offset: 13 bits + +// FRAGWORD &= 0x3fff; // Clear Dont Fragment bit + + if (FRAGWORD & (1 << 14)) + { + SendICMPMessage(IPptr, 3, 4, PACLEN); // type 3 (dest unreachable), code 4 (frag needed but don't-fragment bit set)) + return; + } + + FRAGWORD |= (1 << 13); // Set More Fragments bit + IPptr->FRAGWORD = htons(FRAGWORD); + + Datalen = (PACLEN - 20) & 0xFFF8; // Must be multiple of 8 bytes + Fraglen = Datalen + 20; + + IPptr->IPLENGTH = htons(Fraglen); + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Send First Fragment + + if (Mode == 'D') // Datagram + Send_AX_Datagram((PMESSAGE)Msgptr, Fraglen + 16, Port, HWADDR); + else + Send_AX_Connected((PMESSAGE)Msgptr, Fraglen + 16, Port, HWADDR); + + // Update Header + + FRAGWORD += Datalen / 8; + + // Move Data Down the buffer + + Len -= Datalen; + memmove(ptr1, ptr1 + (Datalen), Len); + } + + // Reset Header in case we've messed with it + + IPptr->IPLENGTH = htons(Len); + + // if this started out as a fragment before we split it more, + // we need to leave the MF bit set + + if ((OrigFragWord & 0x2000) == 0) + FRAGWORD &= 0x5fff; // Clear More Fragments bit + + IPptr->FRAGWORD = htons(FRAGWORD); + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + Len+=16; + + if (Mode == 'D') // Datagram + { + Send_AX_Datagram((PMESSAGE)Msgptr, Len, Port, HWADDR); + return; + } + + Send_AX_Connected((PMESSAGE)Msgptr, Len, Port, HWADDR); +} + +PROUTEENTRY AllocRouteEntry() +{ + PROUTEENTRY Routeptr; + + if (NumberofRoutes == 0) + + RouteRecords = malloc(sizeof(void *)); + else + RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *)); + + Routeptr = zalloc(sizeof(ROUTEENTRY)); + + if (Routeptr == NULL) return NULL; + + RouteRecords[NumberofRoutes++] = Routeptr; + + return Routeptr; +} + + +PARPDATA AllocARPEntry() +{ + ARPDATA * ARPptr; + + if (NumberofARPEntries == 0) + + ARPRecords = malloc(sizeof(void *)); + else + ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)* sizeof(void *)); + + ARPptr = malloc(sizeof(ARPDATA)); + + if (ARPptr == NULL) return NULL; + + memset(ARPptr, 0, sizeof(ARPDATA)); + + ARPRecords[NumberofARPEntries++] = ARPptr; + + return ARPptr; +} + + VOID SendARPMsg(PARPDATA Arp, BOOL ToTAP) + { + // Send ARP. Initially used only to find default gateway + + Arp->ARPTIMER = 5; // Retry periodically + + if (Arp->ARPINTERFACE == 255) + { + ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + + ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(ETHARPREQMSG.TARGETHWADDR, 0, 6); + + ETHARPREQMSG.SENDIPADDR = OurIPAddr; + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + + Send_ETH(ÐARPREQMSG, 42, ToTAP); + + return; + } + else + { + AXARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(AXARPREQMSG.TARGETHWADDR, 0, 7); + AXARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + AXARPREQMSG.SENDIPADDR = OurIPAddr; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, QST); + + return; + + } + } + +PROUTEENTRY FindRoute(uint32_t IPADDR) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if ((IPADDR & Route->SUBNET) == Route->NETWORK) + return Route; + } + return NULL; +} + + + +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if (Route->NETWORK == IPADDR && Route->SUBNET == Mask) + { + *Found = TRUE; + return Route; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Route = AllocRouteEntry(); + return Route; + } + else + return NULL; +} + +PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found) +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (Arp->IPADDR == IPADDR) + { + *Found = TRUE; + return Arp; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Arp = AllocARPEntry(); + return Arp; + } + else + return NULL; +} +VOID RemoveARP(PARPDATA Arp); + +VOID RemoveRoute(PROUTEENTRY Route) +{ + int i; + + for (i=0; i < NumberofRoutes; i++) + { + if (Route == RouteRecords[i]) + { + while (i < NumberofRoutes) + { + RouteRecords[i] = RouteRecords[i+1]; + i++; + } + + if (Route->ARP) + { + PARPDATA Arp = Route->ARP; + Route->ARP->ARPROUTE = NULL; // Avoid recursion + if (Arp->LOCKED == 0) + RemoveARP(Arp); + } + + free(Route); + NumberofRoutes--; + return; + } + } +} + + +VOID RemoveARP(PARPDATA Arp) +{ + int i; + + while (Arp->ARP_Q) + free(Q_REM_NP((void *)&Arp->ARP_Q)); + + for (i=0; i < NumberofARPEntries; i++) + { + if (Arp == ARPRecords[i]) + { + while (i < NumberofARPEntries) + { + ARPRecords[i] = ARPRecords[i+1]; + i++; + } + + // Remove linked route + + if (Arp->ARPROUTE) + { + PROUTEENTRY Route = Arp->ARPROUTE; + Arp->ARPROUTE->ARP = NULL; // Avoid recursion + + if (Route->LOCKED == 0) + RemoveRoute(Route); + } + + free(Arp); + NumberofARPEntries--; + return; + } + } +} + + +Dll int APIENTRY GetIPInfo(VOID * ARPRecs, VOID * IPStatsParam, int index) +{ + IPStats.ARPEntries = NumberofARPEntries; + + ARPFlag = index; +#ifndef LINBPQ +#ifndef _WIN64 + _asm { + + mov esi, ARPRecs + mov DWORD PTR[ESI], offset Arp + + mov esi, IPStatsParam + mov DWORD PTR[ESI], offset IPStats + } +#endif +#endif + return ARPFlag; +} + + +static BOOL ReadConfigFile() +{ + +// IPAddr 192.168.0.129 +// IPBroadcast 192.168.0.255 +// IPGateway 192.168.0.1 +// IPPorts 1,4 + +// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000 + + char * Config; + char * ptr1, * ptr2; + PROUTEENTRY Route; + BOOL Found; + char buf[256],errbuf[256]; + + Config = PortConfig[33]; // Config fnom bpq32.cfg + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strlop(buf, ';'); + strlop(buf, '#'); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf)) + { + WritetoConsoleLocal("IP Gateway bad config record "); + strcat(errbuf, "\n"); + WritetoConsoleLocal(errbuf); + } + } + + // Add an Interface Route to our LAN + + Route = LookupRoute(OurIPAddr & OurNetMask, OurNetMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = OurIPAddr & OurNetMask; + Route->SUBNET = OurNetMask; + Route->GATEWAY = 0; + Route->LOCKED = 1; + Route->TYPE = 'E'; + } + } + } + return TRUE; +} + +static int ProcessLine(char * buf) +{ + char * ptr, * p_value, * p_origport, * p_host, * p_port; + int port, mappedport, ipad, mappedipad; + BOOL NATTAP = FALSE; + int i; + + ptr = strtok(buf, " \t\n\r"); + p_value = strtok(NULL, " \t\n\r"); + + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"ADAPTER") == 0) + { +//#ifndef WIN32 +// WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n"); +// return TRUE; +//#endif + strcpy(Adapter,p_value); + return (TRUE); + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + Promiscuous = atoi(p_value); + return (TRUE); + } + + if(_stricmp(ptr,"UDPTunnel") == 0) + { + WantUDPTunnel = atoi(p_value); + return (TRUE); + } + + if (_stricmp(ptr,"44Encap") == 0) // Enable Net44 IPIP Tunnel + { + WantEncap = TRUE; + + if (p_value == NULL) + return TRUE; + + EncapAddr = inet_addr(p_value); + + strcpy(EncapAddrText, p_value); + + if (EncapAddr != INADDR_NONE) + { + int a,b,c,d,e,f,num; + + // See if MAC Specified + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return TRUE; + + num=sscanf(p_value,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num == 6) + { + RouterMac[0]=a; + RouterMac[1]=b; + RouterMac[2]=c; + RouterMac[3]=d; + RouterMac[4]=e; + RouterMac[5]=f; + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return TRUE; + } + // see if source addr specified + + if (_stricmp(p_value, "RIPSOURCE") == 0) + { + // Set Source addr of RIP44 packets + + p_value = strtok(NULL, " =\t\n\r"); + UCSD44 = inet_addr(p_value); + return TRUE; + } + return FALSE; + } + + if (_stricmp(p_value, "UDP") == 0) + { + UDPEncap = TRUE; + + // look for options PORT and/or IPv6 + + p_value = strtok(NULL, " \t\n\r"); + + while (p_value) + { + if (_stricmp(p_value, "IPv6") == 0) + IPv6 = TRUE; + else + if (_memicmp(p_value, "PORT=", 5) == 0) + UDPPort = atoi(&p_value[5]); + + p_value = strtok(NULL, " \t\n\r"); + } + } + + return (TRUE); + } + + if (_stricmp(ptr,"IPAddr") == 0) + { + // accept /xx as a netmask + + char * p_mask = strlop(p_value, '/'); + + if (p_mask) + { + uint32_t IPMask; + int Bits = atoi(p_mask); + + if (Bits > 32) + Bits = 32; + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + OurNetMask = htonl(IPMask); // Needs to be Network order + } + + OurIPAddr = inet_addr(p_value); + + if (OurIPAddr == INADDR_NONE) return (FALSE); + + strcpy(IPAddrText, p_value); + return (TRUE); + } + +// if (_stricmp(ptr,"HostIPAddr") == 0) +// { +// HostIPAddr = inet_addr(p_value); + +// if (HostIPAddr == INADDR_NONE) return (FALSE); + +// strcpy(HostIPAddrText, p_value); + +// return (TRUE); +// } + + if (_stricmp(ptr,"IPNetMask") == 0) + { + OurNetMask = inet_addr(p_value); + + if (strcmp(p_value, "255.255.255.255") == 0) + return TRUE; + + if (OurNetMask == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + if (_stricmp(ptr,"IPPorts") == 0) + { + p_port = strtok(p_value, " ,\t\n\r"); + + while (p_port != NULL) + { + i=atoi(p_port); + if (i == 0) return FALSE; + if (i > NUMBEROFPORTS) return FALSE; + + IPPortMask |= 1 << (i-1); + p_port = strtok(NULL, " ,\t\n\r"); + } + return (TRUE); + } + + if (_stricmp(ptr,"NoDefaultRoute") == 0) + { + NoDefaultRoute = TRUE; + return TRUE; + } + + // ARP 44.131.4.18 GM8BPQ-7 1 + if (_stricmp(ptr,"ARP") == 0) + { + p_value[strlen(p_value)] = ' '; // put back together + return ProcessARPLine(p_value, TRUE); + } + + if (_stricmp(ptr,"ROUTE") == 0) + { + p_value[strlen(p_value)] = ' '; // put back together + ProcessROUTELine(p_value, TRUE); + return TRUE; + } + + if (_stricmp(ptr,"NAT") == 0) + { + PROUTEENTRY Route; + BOOL Found; + + if (OurIPAddr == 0) + { + WritetoConsoleLocal("NAT lines should follow IPAddr\n"); + return FALSE; + } + + if (!p_value) return FALSE; + ipad = inet_addr(p_value); + + if (ipad == INADDR_NONE) + return FALSE; + + p_host = strtok(NULL, " ,\t\n\r"); + if (!p_host) return FALSE; + + mappedipad = inet_addr(p_host); + if (mappedipad == INADDR_NONE) + return FALSE; + + p_origport = strtok(NULL, " ,\t\n\r"); + + if (p_origport && strcmp(p_origport, "TAP") == 0) + NATTAP = TRUE; + + // +// // Default is all ports +// +// if (p_origport) +// { +// p_port = strtok(NULL, " ,\t\n\r"); +// if (!p_port) return FALSE; +// +// port = atoi(p_origport); +// mappedport=atoi(p_port); +// } +// else + + port = mappedport = 0; + +#ifndef WIN32 + // on Linux, we send stuff for our host to TAP + + if (ipad == OurIPAddr || NATTAP) + nat_table[nat_table_len].ThisHost = TRUE; +#endif + nat_table[nat_table_len].origipaddr = ipad; + nat_table[nat_table_len].origport = ntohs(port); + nat_table[nat_table_len].mappedipaddr = mappedipad; + nat_table[nat_table_len++].mappedport = ntohs(mappedport); + + // Add a Host Route + + Route = LookupRoute(mappedipad, 0xffffffff, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = mappedipad ; + Route->SUBNET = 0xffffffff; + Route->GATEWAY = 0; + Route->LOCKED = 1; + Route->TYPE = 'E'; + } + } + return TRUE; + } + + if (_stricmp(ptr,"ENABLESNMP") == 0) + { + BPQSNMP = TRUE; + return TRUE; + } + // + // Bad line + // + return FALSE; + +} + +VOID DoARPTimer() +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (!Arp->ARPVALID) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Retry Request + +// SendARPMsg(Arp); + RemoveARP(Arp); + } + continue; + } + + // Time out active entries + + if (Arp->LOCKED == 0) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Remove Entry + + RemoveARP(Arp); + SaveARP(); + } + } + } +} + +VOID DoRouteTimer() +{ + int i; + PROUTEENTRY Route; + time_t NOW = time(NULL); + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (Route->RIPTIMOUT) + Route->RIPTIMOUT--; + + if (Route->TYPE == 'T' && Route->RIPTIMOUT == 0 && Route->LOCKED == FALSE) + { + // Only remove Encap routes if we are still getting RIP44 messages, + // so we can keep going if UCSD stops sending updates, but can time + // out entries that are removed + + if ((NOW - LastRIP44Msg) < 3600) + { + RemoveRoute(Route); + return; // Will remove all eventually + } + } + } +} + +// PCAP Support Code + + +#ifdef WIN32 + +FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d", Proc,err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + +FARPROCI GetAddressI(char * Proc) +{ + FARPROCI ProcAddr; + int err = 0; + char buf[256]; + int n; + + + ProcAddr = (FARPROCI)GetProcAddress(PcapDriver, Proc); + + if (ProcAddr == 0) + { + err = GetLastError(); + + n = sprintf(buf, "Error finding %s - %d", Proc, err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + +#endif + +void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +int OpenPCAP() +{ + u_long param=1; + BOOL bcopt=TRUE; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[256]; + struct bpf_program fcode; + char buf[256]; + int n; + +#ifndef MACBPQ + + /* Open the adapter */ + + adhandle= pcap_open_livex(Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + ); + + if (adhandle == NULL) + return FALSE; + + pcap_setnonblockx(adhandle, 1, errbuf); + + /* Check the link layer. We support only Ethernet for simplicity. */ + + if((int)pcap_datalinkx(adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"\nThis program works only on Ethernet networks.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + netmask=0xffffff; + +// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806"); + + n = sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x", + ourMACAddr[0], ourMACAddr[1], ourMACAddr[2], + ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]); + + //compile the filter + + if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(adhandle, &fcode)<0) + { + n=sprintf(buf,"\nError setting the filter.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } +#endif + return TRUE; +} + + +VOID ReadARP() +{ + FILE *file; + char buf[256],errbuf[256]; + + if ((file = fopen(ARPFN,"r")) == NULL) return; + + while(fgets(buf, 255, file) != NULL) + { + strcpy(errbuf,buf); // save in case of error + + if (!ProcessARPLine(buf, FALSE)) + { + WritetoConsoleLocal("IP Gateway bad ARP record "); + WritetoConsoleLocal(errbuf); + } + + } + + fclose(file); + + return; +} + +BOOL ProcessARPLine(char * buf, BOOL Locked) +{ + char * p_ip, * p_mac, * p_port, * p_type; + int Port; + char Mac[7]; + char AXCall[64]; + BOOL Stay, Spy; + uint32_t IPAddr; + int a,b,c,d,e,f,num; + struct PORTCONTROL * PORT; + + PARPDATA Arp; + BOOL Found; + + _strupr(buf); // calls should be upper case + +// 192.168.0.131 GM8BPQ-13 1 D + + p_ip = strtok(buf, " \t\n\r"); + p_mac = strtok(NULL, " \t\n\r"); + p_port = strtok(NULL, " \t\n\r"); + p_type = strtok(NULL, " \t\n\r"); + + if(p_ip == NULL) return (TRUE); + + if(*p_ip =='#') return (TRUE); // comment + + if(*p_ip ==';') return (TRUE); // comment + + if (p_mac == NULL) return FALSE; + + if (p_port == NULL) return FALSE; + + if (p_type == NULL) return FALSE; + + IPAddr = inet_addr(p_ip); + + if (IPAddr == INADDR_NONE) return FALSE; + + _strupr(p_type); + + // Don't restore Eth addresses from the save file + + if (*p_type == 'E' && Locked == FALSE) + return TRUE; + + if (!((*p_type == 'D') || (*p_type == 'E') || (*p_type =='V'))) return FALSE; + + Port=atoi(p_port); + + if (p_mac == NULL) return (FALSE); + + if (*p_type == 'E') + { + num=sscanf(p_mac,"%x:%x:%x:%x:%x:%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + Mac[0]=a; + Mac[1]=b; + Mac[2]=c; + Mac[3]=d; + Mac[4]=e; + Mac[5]=f; + + if (Port != 255) return FALSE; + } + else + { + if (DecodeCallString(p_mac, &Stay, &Spy, &AXCall[0]) == 0) + return FALSE; + + if (Port == 0 && *p_type !='V') // Port 0 for NETROM + return FALSE; + + if (Port) + { + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + return FALSE; + } + } + + Arp = LookupARP(IPAddr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPAddr; + + if (*p_type == 'E') + { + memcpy(Arp->HWADDR, Mac, 6); + } + else + { + memcpy(Arp->HWADDR, AXCall, 64); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = *p_type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = (Arp->ARPTYPE == 'E')? 300 : ARPTIMEOUT; + Arp->LOCKED = Locked; + + // Also add to Routes + + AddToRoutes(Arp, IPAddr, *p_type); + Arp->ARPROUTE->LOCKED = Locked; + + } + } + + return TRUE; +} + +VOID AddToRoutes(PARPDATA Arp, UINT IPAddr, char Type) +{ + PROUTEENTRY Route; + BOOL Found; + UINT IPMask = 0xffffffff; // All ARP rerived routes are Host Routes + + Route = LookupRoute(Arp->IPADDR, IPMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = IPAddr; + Route->SUBNET = IPMask; + Route->GATEWAY = IPAddr; // Host Route + Route->TYPE = Type; + } + } + + Arp->ARPROUTE = Route; + Route->ARP = Arp; // Crosslink Arp<>Routee + + // Sort into reverse mask order + + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); +} + +// ROUTE 44.131.4.18/32 D GM8BPQ-7 1// Datagram?netrom/VC via Call !!!! No - this sohuld be an ARP entry + +// ROUTE 44.131.4.18/32 T n.n.n.n // Vis Tunnel Endpoint n.n.n.n +// ROUTE 44.131.4.18/32 E n.n.n.n // Via IP address over Ethernet + + +int CountDots(char * Addr) +{ + int Dots = 0; + + while(*Addr) + if (*(Addr++) == '.') + Dots++; + + return Dots; +} + + +BOOL ProcessROUTELine(char * buf, BOOL Locked) +{ + char * p_ip, * p_type, * p_mask, * p_gateway, * p_port; + uint32_t IPAddr, IPMask, IPGateway; + char Type = ' '; + int n, Port; + PROUTEENTRY Route; + BOOL Found; + +// ROUTE 44.131.4.18/31 44.131.4.1 // Normal Route +// ROUTE 44.131.4.18/31 1.2.3.4 T // Tunnel via 1.2.3.4 + + + p_ip = strtok(buf, " \t\n\r"); + p_gateway = strtok(NULL, " \t\n\r"); + p_type = strtok(NULL, " \t\n\r"); + + if (_stricmp(p_ip, "addprivate") == 0) + { + // From Encap.txt file + + p_ip = p_gateway; + + p_gateway = strtok(NULL, " \t\n\r"); + Type = 'T'; + p_type = NULL; + } + + if(p_ip == NULL) return (TRUE); + + if(*p_ip =='#') return (TRUE); // comment + + if(*p_ip ==';') return (TRUE); // comment + + if (p_gateway == NULL) return FALSE; + + if (p_type) + { + if (_stricmp(p_type, "UDPT") == 0) // Tunnelling over UDP + { + p_port = strtok(NULL, " \t\n\r"); + Type = 'U'; + + if (p_port) + Port = atoi(p_port); + } + else + { + Type = *p_type; + if (Type == 't') Type = 'T'; + } + } + + p_mask = strchr(p_ip, '/'); + + if (p_mask) + { + int Bits = atoi(p_mask + 1); + + if (Bits > 32) + Bits = 32; + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + *p_mask = 0; + } + else + IPMask = 32; // No mask means Host route + + IPMask = htonl(IPMask); // Needs to be Network order + + IPGateway = inet_addr(p_gateway); + + if (IPGateway == INADDR_NONE) return FALSE; + + // The encap.txt format omits trailing zeros. + + n = CountDots(p_ip); + + if (n == 2) + strcat(p_ip, ".0"); + else if (n == 1) + strcat(p_ip, ".0.0"); + else if (n == 0) + strcat(p_ip, ".0.0.0"); + + IPAddr = inet_addr(p_ip); + + if (IPAddr == INADDR_NONE) return FALSE; + + + Route = LookupRoute(IPAddr, IPMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = IPAddr; + Route->SUBNET = IPMask; + + if (Type == 'U') + { + Route->UDPADDR.sin_port = htons(Port); + Route->UDPADDR.sin_addr.s_addr = inet_addr(p_gateway); + Route->UDPADDR.sin_family = AF_INET; + } + else if (Type == 'T') + Route->Encap = IPGateway; + else + Route->GATEWAY = IPGateway; + + Route->TYPE = Type; + Route->LOCKED = Locked; + + // Link to ARP + + Route->ARP = LookupARP(Route->GATEWAY, FALSE, &Found); + } + } + return TRUE; +} + + +VOID SaveARP () +{ + PARPDATA Arp; + int i; + FILE * file; + + if ((file = fopen(ARPFN, "w")) == NULL) + return; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + if (Arp->ARPVALID && !Arp->LOCKED) + WriteARPLine(Arp, file); + } + + fclose(file); + + return ; +} + +VOID WriteARPLine(PARPDATA ARPRecord, FILE * file) +{ + int SSID, Len, j; + char Mac[20]; + char Call[7]; + char IP[20]; + char Line[100]; + unsigned char work[4]; + + memcpy(work, &ARPRecord->IPADDR, 4); + + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if(ARPRecord->ARPINTERFACE == 255) // Ethernet + { + sprintf(Mac," %02x:%02x:%02x:%02x:%02x:%02x", + ARPRecord->HWADDR[0], + ARPRecord->HWADDR[1], + ARPRecord->HWADDR[2], + ARPRecord->HWADDR[3], + ARPRecord->HWADDR[4], + ARPRecord->HWADDR[5]); + } + else + { + for (j=0; j< 6; j++) + { + Call[j] = ARPRecord->HWADDR[j]/2; + if (Call[j] == 32) Call[j] = 0; + } + Call[6] = 0; + SSID = (ARPRecord->HWADDR[6] & 31)/2; + + sprintf(Mac,"%s-%d", Call, SSID); + } + + Len = sprintf(Line,"%s %s %d %c\n", + IP, Mac, ARPRecord->ARPINTERFACE, ARPRecord->ARPTYPE); + + fputs(Line, file); + + return; +} + +VOID ReadIPRoutes() +{ + PROUTEENTRY Route; + FILE * file; + char * Net; + char * Nexthop; + char * Encap; + char * Context; + char * Type; + char * p_mask; + + char Line[256]; + + uint32_t IPAddr, IPMask = 0xffffffff, IPGateway; + + BOOL Found; + + if ((file = fopen(IPRFN, "r")) == NULL) + return; + +// 44.0.0.1/32 0.0.0.0 T 1 8 encap 169.228.66.251 + + while(fgets(Line, 255, file) != NULL) + { + Net = strtok_s(Line, " \n", &Context); + if (Net == 0) continue; + + if (strcmp(Net, "ENCAPMAC") == 0) + { + int a,b,c,d,e,f,num; + + num=sscanf(Context,"%x:%x:%x:%x:%x:%x",&a,&b,&c,&d,&e,&f); + + if (num == 6) + { + RouterMac[0]=a; + RouterMac[1]=b; + RouterMac[2]=c; + RouterMac[3]=d; + RouterMac[4]=e; + RouterMac[5]=f; + } + continue;; + } + + Nexthop = strtok_s(NULL, " \n", &Context); + if (Nexthop == 0) continue; + + Type = strtok_s(NULL, " \n", &Context); + if (Type == 0) continue; + + p_mask = strlop(Net, '/'); + + if (p_mask) + { + int Bits = atoi(p_mask); + + if (Bits > 32) + Bits = 32; + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + IPMask = htonl(IPMask); // Needs to be Network order + } + + IPAddr = inet_addr(Net); + if (IPAddr == INADDR_NONE) continue; + + IPGateway = inet_addr(Nexthop); + if (IPGateway == INADDR_NONE) continue; + + if (Type[0] == 'T') + { + // Skip Metric, Time and "encap", get encap addr + + Encap = strtok_s(NULL, " \n", &Context); + Encap = strtok_s(NULL, " \n", &Context); + Encap = strtok_s(NULL, " \n", &Context); + Encap = strtok_s(NULL, " \n", &Context); + + if (Encap == 0) continue; + + IPGateway = inet_addr(Encap); + if (IPGateway == INADDR_NONE) continue; + + } + + Route = LookupRoute(IPAddr, IPMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = IPAddr; + Route->SUBNET = IPMask; + + if (Type[0] == 'T') + Route->Encap = IPGateway; + else + Route->GATEWAY = IPGateway; + + Route->TYPE = Type[0]; + Route->RIPTIMOUT = 900; + + // Link to ARP + + if (Type[0] != 'T') + Route->ARP = LookupARP(Route->GATEWAY, FALSE, &Found); + } + } + } + + fclose(file); + return; +} + +VOID SaveIPRoutes () +{ + PROUTEENTRY Route; + int i; + FILE * file; + char Line[128]; + + if ((file = fopen(IPRFN, "w")) == NULL) + return; + + // Save Gateway MAC + + sprintf(Line,"ENCAPMAC %02x:%02x:%02x:%02x:%02x:%02x\n", + RouterMac[0], + RouterMac[1], + RouterMac[2], + RouterMac[3], + RouterMac[4], + RouterMac[5]); + + fputs(Line, file); + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (!Route->LOCKED) + WriteIPRLine(Route, file); + } + + fclose(file); + + return ; +} + +VOID WriteIPRLine(PROUTEENTRY RouteRecord, FILE * file) +{ + int Len; + char Net[20]; + char Nexthop[20]; + char Encap[20]; + + char Line[100]; + unsigned char work[4]; + + memcpy(work, &RouteRecord->NETWORK, 4); + sprintf(Net, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->GATEWAY, 4); + sprintf(Nexthop, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->Encap, 4); + sprintf(Encap, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if (RouteRecord->TYPE == 'T') + Len = sprintf(Line, "%s/%d %s %c %d %d encap %s\n", + Net, CountBits(RouteRecord->SUBNET), + Nexthop, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT, Encap); + else + Len = sprintf(Line, "%s/%d %s %c %d %d\n", + Net, CountBits(RouteRecord->SUBNET), + Nexthop, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT); + + fputs(Line, file); + + return; +} + + + + + +int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 6; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len); + + // No need to do IP checksum as RouteIPMessage doesit + +// CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + RouteIPMsg(IPptr); + return 0; +} + +int CheckSumAndSendUDP(PIPMSG IPptr, PUDPMSG UDPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 17; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + UDPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + + UDPmsg->CHECKSUM = Generate_CHECKSUM(UDPmsg, Len); + + // No need to do IP checksum as ROuteIPMessage doesit + + // CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + RouteIPMsg(IPptr); + return 0; +} + +VOID RecalcTCPChecksum(PIPMSG IPptr) +{ + PTCPMSG TCPptr = (PTCPMSG)&IPptr->Data; + PHEADER PH = {0}; + USHORT Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPptr->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCPptr->CHECKSUM = Generate_CHECKSUM(TCPptr, Len); +} + +VOID RecalcUDPChecksum(PIPMSG IPptr) +{ + PUDPMSG UDPmsg = (PUDPMSG)&IPptr->Data; + PHEADER PH = {0}; + USHORT Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + PH.IPPROTOCOL = 17; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + UDPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + UDPmsg->CHECKSUM = Generate_CHECKSUM(UDPmsg, Len); +} + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD +#include +#include + +/* buffer for reading from tun/tap interface, must be >= 1500 */ + + +#define BUFSIZE 2000 + +/************************************************************************** + * tun_alloc: allocates or reconnects to a tun/tap device. The caller * + * must reserve enough space in *dev. * + **************************************************************************/ +int tun_alloc(char *dev, int flags) { + + struct ifreq ifr; + int fd, err; + char *clonedev = "/dev/net/tun"; + + if( (fd = open(clonedev , O_RDWR)) < 0 ) { + perror("Opening /dev/net/tun"); + return fd; + } + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = flags; + + if (*dev) { + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + } + + if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) { + perror("ioctl(TUNSETIFF)"); + close(fd); + return err; + } + + strcpy(dev, ifr.ifr_name); + return fd; +} + +#include +#include + + +void OpenTAP() +{ + int flags = IFF_TAP; + char if_name[IFNAMSIZ] = "LinBPQTAP"; + struct arpreq arpreq; + int s; + struct ifreq ifr; + int sockfd; + struct rtentry rm; + int err; + int n; + + uint nread, nwrite, plength; + char buffer[BUFSIZE]; + + int optval = 1; + + struct nat_table_entry * NAT = NULL; + int index; + + /* initialize tun/tap interface */ + + if ((tap_fd = tun_alloc(if_name, flags | IFF_NO_PI)) < 0 ) + { + printf("Error connecting to tun/tap interface %s!\n", if_name); + tap_fd = 0; + return; + } + + printf("Successfully connected to TAP interface %s\n", if_name); + + ioctl(tap_fd, FIONBIO, &optval); + + // Bring it up + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd < 0) + { + perror ("Socket"); + return; + } + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + ifr.ifr_flags |= IFF_UP; + + if ((err = ioctl(sockfd, SIOCSIFFLAGS, &ifr)) < 0) + { + perror("SIOCSIFFLAGS"); + printf("SIOCSIFFLAGS failed , ret->%d\n",err); + return; + } + + printf("TAP brought up\n"); + + // Set MTU to 256 + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + ifr.ifr_addr.sa_family = AF_INET; + ifr.ifr_mtu = 256; + + if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0) + perror("Set MTU"); + else + printf("TAP MTU set to 256\n"); + + if (NoDefaultRoute == FALSE) + { + // Add a Route for 44/9 and 44.128/10 via TAP + + memset(&rm, 0, sizeof(rm)); + + (( struct sockaddr_in*)&rm.rt_dst)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_dst)->sin_addr.s_addr = inet_addr("44.0.0.0"); + (( struct sockaddr_in*)&rm.rt_dst)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_genmask)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_genmask)->sin_addr.s_addr = inet_addr("255.128.0.0"); + (( struct sockaddr_in*)&rm.rt_genmask)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_gateway)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_addr.s_addr = 0; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_port = 0; + + rm.rt_dev = if_name; + + rm.rt_flags = RTF_UP; // | RTF_GATEWAY; + + if ((err = ioctl(sockfd, SIOCADDRT, &rm)) < 0) + { + perror("SIOCADDRT"); + printf("SIOCADDRT failed , ret->%d\n",err); + return; + } + printf("Route to 44/9 added via LinBPQTAP\n"); + + memset(&rm, 0, sizeof(rm)); + + (( struct sockaddr_in*)&rm.rt_dst)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_dst)->sin_addr.s_addr = inet_addr("44.128.0.0"); + (( struct sockaddr_in*)&rm.rt_dst)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_genmask)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_genmask)->sin_addr.s_addr = inet_addr("255.192.0.0"); + (( struct sockaddr_in*)&rm.rt_genmask)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_gateway)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_addr.s_addr = 0; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_port = 0; + + rm.rt_dev = if_name; + + rm.rt_flags = RTF_UP; // | RTF_GATEWAY; + + if ((err = ioctl(sockfd, SIOCADDRT, &rm)) < 0) + { + perror("SIOCADDRT"); + printf("SIOCADDRT failed , ret->%d\n",err); + return; + } + printf("Route to 44.128/10 added via LinBPQTAP\n"); + + } + + // Set up ARP entries for any virtual hosts (eg jnos) + + bzero((caddr_t)&arpreq, sizeof(arpreq)); + + for (index=0; index < nat_table_len; index++) + { + struct sockaddr_in *psin; + psin = (struct sockaddr_in *)&arpreq.arp_pa; + NAT = &nat_table[index]; + + if (NAT->ThisHost && OurIPAddr != NAT->origipaddr) + { + printf("Adding ARP for %s\n", FormatIP(NAT->mappedipaddr)); + + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = NAT->mappedipaddr; + + arpreq.arp_flags = ATF_PERM | ATF_COM | ATF_PUBL; + strcpy(arpreq.arp_dev, "LinBPQTAP"); + + if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) + perror("ARP IOCTL"); + } + } + + // Create LinBPQ ARP entry for real IP Address + + // Get Address + + struct ifreq xbuffer; + + memset(&xbuffer, 0x00, sizeof(xbuffer)); + + strcpy(xbuffer.ifr_name, "LinBPQTAP"); + + ioctl(sockfd, SIOCGIFHWADDR, &xbuffer); + + PARPDATA Arp; + PROUTEENTRY Route; + BOOL Found; + + Arp = LookupARP(HostNATAddr, TRUE, &Found); + + if (Arp != NULL) + { + Arp->IPADDR = HostNATAddr; + memcpy(Arp->HWADDR, xbuffer.ifr_hwaddr.sa_data, 6); + + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = 0; + Arp->LOCKED = TRUE; + + // Also add to Routes + + AddToRoutes(Arp, HostNATAddr, 'E'); + Arp->ARPROUTE->LOCKED = TRUE; + } + + close(sockfd); +} +#endif +#endif +#endif + +extern struct DATAMESSAGE * REPLYBUFFER; +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); + +VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // Send ICMP Echo Request + + uint32_t PingAddr; + UCHAR Msg[120] = ""; + PIPMSG IPptr = (PIPMSG)&Msg[40]; // Space for frame header (not used) + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + time_t NOW = time(NULL); + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + PingAddr = inet_addr(CmdTail); + + if (PingAddr == INADDR_NONE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Address\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // We keep the message pretty short in case running over RF + // Send "*BPQPINGID*, then Timestamp (Secs), then padding to 20 bytes + // So we can use same address for host we examine ping responses for + // the pattern, and intercept ours + + IPptr->VERLEN = 0x45; + IPptr->IPDEST.addr = PingAddr; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(48); // IP Header ICMP Header 20 Data + + ICMPptr->ICMPTYPE = 8; + ICMPptr->ICMPID = Session->CIRCUITINDEX; + strcpy(ICMPptr->ICMPData, "*BPQPINGID*"); // 12 including null + memcpy(&ICMPptr->ICMPData[12], &NOW, 4); + + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 28); + + if (RouteIPMsg(IPptr)) + Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "No Route to Host\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; +} + +VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY IP Gateway ARP status or Clear + + int i; + PARPDATA ARPRecord, Arp; + int SSID, j, n; + char Mac[128]; + char Call[7]; + char IP[20]; + unsigned char work[4]; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "CLEAR ", 6) == 0) + { + int n = NumberofARPEntries; + int rec = 0; + + for (i=0; i < n; i++) + { + Arp = ARPRecords[rec]; + if (Arp->LOCKED) + rec++; + else + RemoveARP(Arp); + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + SaveARP(); + + return; + } + + for (i=0; i < NumberofARPEntries; i++) + { + ARPRecord = ARPRecords[i]; + +// if (ARPRecord->ARPVALID) + { + memcpy(work, &ARPRecord->IPADDR, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if(ARPRecord->ARPINTERFACE == 255) // Ethernet + { + sprintf(Mac," %02x:%02x:%02x:%02x:%02x:%02x", + ARPRecord->HWADDR[0], + ARPRecord->HWADDR[1], + ARPRecord->HWADDR[2], + ARPRecord->HWADDR[3], + ARPRecord->HWADDR[4], + ARPRecord->HWADDR[5]); + } + else + { + UCHAR * AXCall = &ARPRecord->HWADDR[0]; + n = 0; + + while (AXCall[0]) + { + + for (j=0; j< 6; j++) + { + Call[j] = AXCall[j]/2; + if (Call[j] == 32) Call[j] = 0; + } + + Call[j] = 0; + SSID = (AXCall[6] & 31)/2; + + if (SSID) + n += sprintf(&Mac[n], " %s-%d", Call, SSID); + else + n += sprintf(&Mac[n], " %s", Call); + + AXCall += 7; + } + } + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s %d %c %d %s\r", + IP, Mac, ARPRecord->ARPINTERFACE, ARPRecord->ARPTYPE, + (int)ARPRecord->ARPTIMER, ARPRecord->LOCKED?"Locked":""); + } + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY IP Gateway ARP status or Clear + + struct nat_table_entry * NAT = NULL; + int index; + char From[20]; + char To[20]; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + strcpy(From, FormatIP(NAT->origipaddr)); + strcpy(To, FormatIP(NAT->mappedipaddr)); + + +#ifdef LINBPQ + Bufferptr = Cmdprintf(Session, Bufferptr, "%s to %s %s\r", From, To, + NAT->ThisHost?"via TAP":""); +#else + Bufferptr = Cmdprintf(Session, Bufferptr, "%s to %s\r", From, To); +#endif + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int CountBits(uint64_t in) +{ + int n = 0; + while (in) + { + if (in & 1) n ++; + in >>=1; + } + return n; +} + +VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY IP Gateway ARP status or Clear + + int i; + PROUTEENTRY RouteRecord; + char Net[20]; + char Nexthop[20]; + char Encap[20]; + char *Context; + char Reply[128]; + char UCReply[128]; + + unsigned char work[4]; + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "\rIP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "%d Entries\r", NumberofRoutes); + + if (NumberofRoutes) + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareRoutes); + + for (i=0; i < NumberofRoutes; i++) + { + RouteRecord = RouteRecords[i]; + +// if (RouteRecord->ARPVALID) + { + memcpy(work, &RouteRecord->NETWORK, 4); + sprintf(Net, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->GATEWAY, 4); + sprintf(Nexthop, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->Encap, 4); + sprintf(Encap, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if (RouteRecord->TYPE == 'T') + sprintf(Reply, "%s/%d %d %c %d %d encap %s\r", + Net, CountBits(RouteRecord->SUBNET), + RouteRecord->FRAMECOUNT, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT, Encap); + else + sprintf(Reply, "%s/%d %d %s %c %d %d %s\r", + Net, CountBits(RouteRecord->SUBNET), + RouteRecord->FRAMECOUNT, Nexthop, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT, + RouteRecord->LOCKED?"Locked":""); + } + + // Treat any parameter as a "Find Filter" + + CmdTail = strtok_s(CmdTail, " ", &Context); + strcpy(UCReply, Reply); + _strupr(UCReply); + + if (CmdTail && CmdTail[0] && strstr(UCReply, CmdTail) == 0) + continue; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Reply); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + if (NumberofRoutes) + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); // Back to Maks order + +} + +// SNMP Support Code. Pretty limited - basically just for MRTG + +/* +Primitive ASN.1 Types Identifier in hex +INTEGER 02 +BIT STRING 03 +OCTET STRING 04 +NULL 05 +OBJECT IDENTIFIER 06 + +Constructed ASN.1 type Identifier in hex +SEQUENCE 30 + +Primitive SNMP application types Identifier in hex +IpAddress 40 +Opaque 44 +NsapAddress 45 +Counter64 (available only in SNMPv2) 46 +Uinteger32 (available only in SNMPv2) 47 + +Context-specific types within an SNMP Message Identifier in hex +GetRequest-PDU A0 +GetNextRequestPUD A1 +GetResponse-PDU (Response-PDU in SNMPv 2) A2 +SetRequest-PDU A3 +Trap-PDU (obsolete in SNMPv 2) A4 +GetBulkRequest-PDU (added in SNMPv 2) A5 +InformRequest-PDU (added in SNMPv 2) A6 +SNMPv2-Trap-PDU (added in SNMPv 2) A7 + +*/ + +#define Counter32 0x41 +#define Gauge32 0x42 +#define TimeTicks 0x43 + + + +UCHAR ifInOctets[] = {'+',6,1,2,1,2,2,1,10}; +UCHAR ifOutOctets[] = {'+',6,1,2,1,2,2,1,16}; + +int ifInOctetsLen = 9; // Not Inc Port +int ifOutOctetsLen = 9; // Not Inc Port + +UCHAR sysUpTime[] = {'+', 6,1,2,1,1,3,0}; +int sysUpTimeLen = 8; + +UCHAR sysName[] = {'+', 6,1,2,1,1,5,0}; +int sysNameLen = 8; + +extern time_t TimeLoaded; + +int InOctets[32] = {0}; +int OutOctets[32] = {0}; + +// ASN PDUs have to be constructed backwards, as each header included a length + +// This code assumes we have enough space in front of the buffer. + +int ASNGetInt(UCHAR * Msg, int Len) +{ + int Val = 0; + + while(Len) + { + Val = (Val << 8) + *(Msg++); + Len --; + } + + return Val; +} + + +int ASNPutInt(UCHAR * Buffer, int Offset, unsigned int Val, int Type) +{ + int Len = 0; + + // Encode in minimum space. But servers seem to sign-extend top byte, so if top bit set add another zero; + + // I think zero is sent as a zero length field + + if (Val == 0) + { + // return zero as 01 00, not zero length string + + Buffer[--Offset] = 0; // Val + Buffer[--Offset] = 1; // Len + Buffer[--Offset] = Type; + return 3; + } + + while(Val) + { + Buffer[--Offset] = Val & 255; // Value + Val = Val >> 8; + Len++; + } + + if (Len < 4 && (Buffer[Offset] & 0x80)) // Negative + { + Buffer[--Offset] = 0; + Len ++; + } + + Buffer[--Offset] = Len; // Len + Buffer[--Offset] = Type; + + return Len + 2; +} + + +int AddHeader(UCHAR * Buffer, int Offset, UCHAR Type, int Length) +{ + Buffer[Offset - 2] = Type; + Buffer[Offset - 1] = Length; + + return 2; +} + +int BuildReply(UCHAR * Buffer, int Offset, UCHAR * OID, int OIDLen, UCHAR * Value, int ReqID) +{ + int IDLen; + int ValLen = Value[1] + 2; + + // Value is pre-encoded = type, len, data + + // Contruct the Varbindings. Sequence OID Value + + Offset -= ValLen; + memcpy(&Buffer[Offset], Value, ValLen); + Offset -= OIDLen; + memcpy(&Buffer[Offset], OID, OIDLen); + Buffer[--Offset] = OIDLen; + Buffer[--Offset] = 6; // OID Type + + Buffer[--Offset] = OIDLen + ValLen + 2; + Buffer[--Offset] = 48; // Sequence + + Buffer[--Offset] = OIDLen + ValLen + 4; + Buffer[--Offset] = 48; // Sequence + + // Add the error fields (two zero ints + + Buffer[--Offset] = 0; // Value + Buffer[--Offset] = 1; // Len + Buffer[--Offset] = 2; // Int + + Buffer[--Offset] = 0; // Value + Buffer[--Offset] = 1; // Len + Buffer[--Offset] = 2; // Int + + // ID + + IDLen = ASNPutInt(Buffer, Offset, ReqID, 2); + Offset -= IDLen; + + // PDU Type + + Buffer[--Offset] = OIDLen + ValLen + 12 + IDLen; + Buffer[--Offset] = 0xA2; // Len + + return OIDLen + ValLen + 14 + IDLen; +} + + + +// snmpget -v1 -c jnos [ve4klm.ampr.org | www.langelaar.net] 1.3.6.1.2.1.2.2.1.16.5 + + +VOID ProcessSNMPMessage(PIPMSG IPptr) +{ + int Len; + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + char Community[256]; + UCHAR OID[256]; + int OIDLen; + UCHAR * Msg; + int Type; + int Length, ComLen; + int IntVal; + int ReqID; + int RequestType; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(UDPptr, Len); + + // 4 bytes version + // Null Terminated Community + + Msg = (char *) UDPptr; + + Msg += 8; // Over UDP Header + Len -= 8; + + // ASN 1 Encoding - Type, Len, Data + + while (Len > 0) + { + Type = *(Msg++); + Length = *(Msg++); + + // First should be a Sequence + + if (Type != 0x30) + return; + + Len -= 2; + + Type = *(Msg++); + Length = *(Msg++); + IntVal = *(Msg++); + + // Should be Integer - SNMP Version - We support V1, identified by zero + + if (Type != 2 || Length != 1 || IntVal != 0) + return; + + Len -= 3; + + Type = *(Msg++); + ComLen = *(Msg++); + + // Should Be String (community) + + if (Type != 4) + return; + + memcpy(Community, Msg, ComLen); + Community[ComLen] = 0; + + Len -=2; // Header + Len -= ComLen; + + Msg += (ComLen); + + // A Complex Data Types - GetRequest PDU etc + + RequestType = *(Msg); + *(Msg++) = 0xA2; + Length = *(Msg++); + + Len -= 2; + + // A 2 byte value requestid + + // Next is integer requestid + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 2) + return; + + ReqID = ASNGetInt(Msg, Length); + + Len -= (2 + Length); + Msg += Length; + + // Two more Integers - error status, error index + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 2) + return; + + ASNGetInt(Msg, Length); + + Len -= (2 + Length); + Msg += Length; + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 2) + return; + + ASNGetInt(Msg, Length); + + Len -= (2 + Length); + Msg += Length; + + // Two Variable-bindings structs - another Sequence + + Type = *(Msg++); + Length = *(Msg++); + + Len -= 2; + + if (Type != 0x30) + return; + + Type = *(Msg++); + Length = *(Msg++); + + Len -= 2; + + if (Type != 0x30) + return; + + // Next is OID + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 6) // Object ID + return; + + memcpy(OID, Msg, Length); + OID[Length] = 0; + + OIDLen = Length; + + Len -=2; // Header + Len -= Length; + + Msg += Length; + + // Should Just have a null value left + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 5 || Length != 0) + return; + + Len -=2; // Header + + // Should be nothing left + } + + if (RequestType = 160) + { + UCHAR Reply[256]; + int Offset = 255; + int PDULen, SendLen; + char Value[256]; + int ValLen; + + // Only Support Get + + if (memcmp(OID, sysName, sysNameLen) == 0) + { + ValLen = (int)strlen(MYNODECALL);; + Value[0] = 4; // String + Value[1] = ValLen; + memcpy(&Value[2], MYNODECALL, ValLen); + + PDULen = BuildReply(Reply, Offset, sysName, sysNameLen, Value, ReqID); + } + else if (memcmp(OID, sysUpTime, sysUpTimeLen) == 0) + { + int ValOffset = 10; + ValLen = ASNPutInt(Value, ValOffset, (int)((time(NULL) - TimeLoaded) * 100), TimeTicks); + ValOffset -= ValLen; + + PDULen = BuildReply(Reply, Offset, sysUpTime, sysUpTimeLen, &Value[ValOffset], ReqID); + } + else if (memcmp(OID, ifOutOctets, ifOutOctetsLen) == 0) + { + int Port = OID[9]; + int ValOffset = 10; + ValLen = ASNPutInt(Value, ValOffset, OutOctets[Port], Counter32); + ValOffset -= ValLen; + PDULen = BuildReply(Reply, Offset, OID, OIDLen, &Value[ValOffset], ReqID); + + } + else if (memcmp(OID, ifInOctets, ifInOctetsLen) == 0) + { + int Port = OID[9]; + int ValOffset = 10; + ValLen = ASNPutInt(Value, ValOffset, InOctets[Port], Counter32); + ValOffset -= ValLen; + PDULen = BuildReply(Reply, Offset, OID, OIDLen, &Value[ValOffset], ReqID); + + } + else + return; + + Offset -= PDULen; + Offset -= ComLen; + + memcpy(&Reply[Offset], Community, ComLen); + Reply[--Offset] = ComLen; + Reply[--Offset] = 4; + + // Version + + Reply[--Offset] = 0; + Reply[--Offset] = 1; + Reply[--Offset] = 2; + + Reply[--Offset] = PDULen + ComLen + 5; + Reply[--Offset] = 48; + + SendLen = PDULen + ComLen + 7; + + memcpy(UDPptr->UDPData, &Reply[Offset], SendLen); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + + IPptr->IPSOURCE.addr = OurIPAddr; + + UDPptr->DESTPORT = UDPptr->SOURCEPORT; + UDPptr->SOURCEPORT = htons(161); + SendLen += 8; // UDP Header + UDPptr->LENGTH = htons(SendLen); + IPptr->IPLENGTH = htons(SendLen + 20); + + CheckSumAndSendUDP(IPptr, UDPptr, SendLen); + } + + // Ingnore others +} + diff --git a/KAMPactor.c b/KAMPactor.c new file mode 100644 index 0000000..0081075 --- /dev/null +++ b/KAMPactor.c @@ -0,0 +1,2083 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface 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; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +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; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +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 + + 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; +} + +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->Hardware = H_KAM; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + PortEntry->MAXHOSTMODESESSIONS = 11; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; // 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; + + 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(-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(-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->CIRCUITINDEX, &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, "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; + } + + // 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(-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; + + 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) +{ + 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/KISSHF.c b/KISSHF.c new file mode 100644 index 0000000..44b9071 --- /dev/null +++ b/KISSHF.c @@ -0,0 +1,1543 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use a KISS over TCP TNC for HF style use (ATTACH and single channel operation) + + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "CHeaders.h" + + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL SerialWriteCommBlock(struct TNCINFO * TNC); +void SerialCheckRX(struct TNCINFO * TNC); +int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int KISSHFGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC); +static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int ConnecttoKISS(int port); +TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr); +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +VOID RESET2(struct _LINKTABLE * LINK); +VOID L2SENDXID(struct _LINKTABLE * LINK); +VOID SENDSABM(struct _LINKTABLE * LINK); + +static char ClassName[]="KISSSTATUS"; +static char WindowTitle[] = "KISSHF"; +static int RigControlRow = 165; + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->DefaultMode = TNC->WL2KMode = 0; // Packet 1200 + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while (TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0) + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[16]) * 60; + else + strcat(TNC->InitScript, buf); + } + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int KISSHFGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXBPQPORTS 32 + +static time_t ltime; + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0 && TNC->CONNECTING == 0) + { + // Try to reopen every 30 secs + + if (fn > 3 && fn < 7) + goto ok; + + TNC->ReopenTimer++; + + if (TNC->ReopenTimer < 150) + return 0; + + TNC->ReopenTimer = 0; + + ConnecttoKISS(TNC->Port); + + return 0; + + SendInitScript(TNC); + + } +ok: + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SerialCheckRX(TNC); + return 0; + + case 1: // poll + + STREAM = &TNC->Streams[0]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + SerialSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + // See if any frames for this port + + STREAM = &TNC->Streams[0]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->BytesTXed += txlen; + + bytes=SerialSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; + return -1; + } + } + + return (0); + + case 2: // send + + Stream = 0; + + if (!TNC->CONNECTED) + return 0; // Don't try if not connected + + STREAM = &TNC->Streams[0]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + // We may get KISS packets (UI or session related) or text commands such as RADIO, CONNECT + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN); + + TXMsg = &buff->L2DATA[0]; + + if (buff->PID != 240) // ax.25 address + { + txlen = KissEncode(&buff->PID, txbuff, txlen); + txlen = send(TNC->TCPSock, txbuff, txlen, 0); + return 1; + } + + TXMsg[txlen - 1] = 0; + strcpy(txbuff, TXMsg); + + if (STREAM->Attached == 0) + return 0; + + if (STREAM->Connected) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = txlen; + memcpy((UCHAR *)&buffptr->Data[0], &buff->L2DATA[0], txlen); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + // connected data + + return 1; + } + + // Process as Text Command + + if (_memicmp(txbuff, "D\r", 2) == 0 || _memicmp(txbuff, "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(txbuff, "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &txbuff[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + + if (_memicmp(txbuff, "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + // Connect Command. Pass to L2 code to start session + + char * ptr = strchr(&buff->L2DATA[2], 13); + TRANSPORTENTRY * NewSess = L4TABLE; + struct _LINKTABLE * LINK; + TRANSPORTENTRY * Session = TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK; + struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; + + UCHAR axcalls[64]; + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + int Stay = 0, Spy = 0, CQFLAG = 0, n; + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (DecodeCallString(&buff->L2DATA[2], &Stay, &Spy, &axcalls[0]) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} Invalid Call\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + // Code copied from cmdc00 + + // Get Session Entry for Downlink + + NewSess = SetupNewSession(Session, NULL); + + if (NewSess == NULL) + return 0; + + NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK; + + // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION + + memcpy(Session->L4USER, NewSess->L4USER, 7); + memcpy(ourcall, NewSess->L4USER, 7); + + // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO) + + if ((Session->L4CIRCUITTYPE & BPQHOST))// host + goto noFlip3; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip3; + + if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession + goto noFlip3; + + ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + ourcall[6] ^= 0x1e; // Flip SSID + +noFlip3: + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(axcalls, ourcall, TNC->Port, &LINK); + + if (LINK == NULL) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} Sorry - System Tables Full\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + memcpy(LINK->LINKCALL, axcalls, 7); + memcpy(LINK->OURCALL, ourcall, 7); + + LINK->LINKPORT = PORT; + + LINK->L2TIME = PORT->PORTT1; + + // Copy Digis + + n = 7; + ptr = &LINK->DIGIS[0]; + + while (axcalls[n]) + { + memcpy(ptr, &axcalls[n], 7); + n += 7; + ptr += 7; + + LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI + } + + LINK->LINKTYPE = 2; // DOWNLINK + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + +// if (CMD->String[0] == 'N' && SUPPORT2point2) +// LINK->L2STATE = 1; // New (2.2) send XID +// else + LINK->L2STATE = 2; // Send SABM + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN; + + STREAM->Connecting = TRUE; + + if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM + { + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + } + return 0; + } + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + return ((TNC->CONNECTED) << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on KISSHF"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + SerialSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + SerialSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID KISSHFReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[64]; + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, TXMsg); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseOtherPorts(TNC); +} + +VOID KISSHFSuspendPort(struct TNCINFO * TNC) +{ +} + +VOID KISSHFReleasePort(struct TNCINFO * TNC) +{ +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

KISSHF Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + + +VOID * KISSHFExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + + port = PORT->PORTNUMBER; + + if (TNCInfo[port]) // If restarting, free old config + free(TNCInfo[port]); + + TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (PortConfig[port]) // May not have config + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_KISSHF; + TNC->ARDOPBuffer = malloc(8192); + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->PORTCONTROL.USERS = 1; // Max 1 Session + + TNC->PacketChannels = 0; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = KISSHFSuspendPort; + TNC->ReleasePortProc = KISSHFReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KISSStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KISSStopPort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 700, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + Consoleprintf("KISSHF Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoKISS(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + SerialSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + SerialSendCommand(TNC, "DISCONNECT\r"); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + KISSHFReleaseTNC(TNC); + } +} + + +VOID KISSThread(void * portptr); + +int ConnecttoKISS(int port) +{ + _beginthread(KISSThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID KISSThread(void * portptr) +{ + // Opens socket and looks for data on control and data sockets. + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1; + char * ptr2; + UINT * buffptr; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + } +#endif + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPSock); +// closesocket(TNC->TCPDataSock); + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for KISSHF socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for KISSHF socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // VARA needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + + while (ptr1 && ptr1[0]) + { + unsigned char c; + + ptr2 = strchr(ptr1, 13); + + if (ptr2) + { + c = *(ptr2 + 1); // Save next char + *(ptr2 + 1) = 0; // Terminate string + } +// VARASendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(1 + ptr2++) = c; // Put char back + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to KISS TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to KISS TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title +// EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("KISSHF Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + KISSHFProcessReceivedPacket(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "KISSHF Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->TCPSock = 0; + return; + } + } + } + sprintf(Msg, "KISSHF Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + for (i=0;iInputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + InputLen = recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + int err = GetLastError(); + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + + // Extract and decode KISS frames + + ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1); // Ignore leading FEND + + while (ptr) // FEND in buffer + { + ptr++; + + MsgLen = ptr - TNC->ARDOPBuffer; + + if (MsgLen > 360) + { + TNC->InputLen = 0; + return; + } + + TNC->InputLen -= MsgLen; + + if (MsgLen > 1) + { + PMESSAGE Buff = GetBuff(); + + MsgLen = KissDecode(TNC->ARDOPBuffer, Buffer, MsgLen); + + // we dont need the FENDS or control byte + + MsgLen -= 3; + + if (Buff) + { + memcpy(&Buff->DEST, &Buffer[2], MsgLen); + MsgLen += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)Buff, MsgLen); // Needed for arm5 portability + + C_Q_ADD(&TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buff); + } + } + + if (TNC->InputLen == 0) + return; + + memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen); + ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1); // Ignore leading FEND + } + +} + + + +/* + +// we dont need the control byte + +len --; + +if (Buffer) +{ +memcpy(&Buffer->DEST, &Port->RXMSG[1], len); +len += (3 + sizeof(void *)); + +PutLengthinBuffer((PDATAMESSAGE)Buffer, len); // Needed for arm5 portability + +C_Q_ADD(TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buffer); +} + +*/ + +// TNC->InputLen -= MsgLen; +// goto loop; + + + + +void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // SABM on HFKISS port. L2 code will accepr call and connect to appl if necessary, but + // need to attach the port + + char Call[16] = ""; + char OrigCall[16] = ""; + struct TNCINFO * TNC = PORT->TNC; + struct WL2KInfo * WL2K = TNC->WL2K; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + struct TNCINFO * TNC = PORT->TNC; + + // Incoming Connect + + Call[ConvFromAX25(Buffer->DEST, Call)] = 0; + OrigCall[ConvFromAX25(Buffer->ORIGIN, OrigCall)] = 0; + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + ProcessIncommingConnectEx(TNC, Call, 0, FALSE, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", OrigCall, Call, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", OrigCall, Call); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } +} + +void DetachKISSHF(struct PORTCONTROL * PORT) +{ + // L2 Link Closed. Detach. + + struct TNCINFO * TNC = PORT->TNC; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (STREAM->Attached) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; + +} + +void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK) +{ + // UA received when connecting + + struct TNCINFO * TNC = PORT->TNC; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct WL2KInfo * WL2K = TNC->WL2K; + + TRANSPORTENTRY * SESS; + char Call[16] = ""; + char OrigCall[16] = ""; + + + if (STREAM->Connecting) + { + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; + + Call[ConvFromAX25(LINK->LINKCALL, Call)] = 0; + OrigCall[ConvFromAX25(LINK->OURCALL, OrigCall)] = 0; + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + SESS->Mode = TNC->WL2KMode; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", OrigCall, Call, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", OrigCall, Call); + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } +} + + + + + diff --git a/KernelScript1.rc b/KernelScript1.rc new file mode 100644 index 0000000..6a77e46 --- /dev/null +++ b/KernelScript1.rc @@ -0,0 +1,339 @@ +//Microsoft Developer Studio generated resource script. +// +#include "kernelresource.h" + +#define CKernel + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "bpqtermmdi.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +BPQMAINWINDOW DIALOG DISCARDABLE 17, 25, 382, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "BPQ32 Console" +CLASS "BPQMAINWINDOW" +FONT 8, "Fixedsys" +BEGIN + LTEXT "",IDC_BACKGROUND,22,20,337,273 + CONTROL "",IDC_ENIGATE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | + WS_TABSTOP,57,0,8,12 + LTEXT "IGate State - Disconnected",IGATESTATE,69,0,110,12, + SS_CENTERIMAGE + LTEXT "IGATE Stats - Msgs 0 Local Stns 0",IGATESTATS,180,0, + 152,12,SS_CENTERIMAGE + LISTBOX BPQCONSOLE,1,17,359,262,LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | + WS_HSCROLL + LTEXT "GPS Off",IDC_GPS,332,0,40,12,SS_CENTERIMAGE + LTEXT "Enable IGate",IDC_STATIC,5,2,49,10 +END + +CONFIG DIALOGEX 249, 200, 160, 118 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION +EXSTYLE WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE +CAPTION "Configuration" +CLASS "CONFIG" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + EDITTEXT 1001,50,5,43,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + EDITTEXT 1002,50,25,100,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + EDITTEXT 1003,50,65,43,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Call",1,10,5,21,18,0,WS_EX_NOPARENTNOTIFY + LTEXT "Host",2,10,25,30,20,0,WS_EX_NOPARENTNOTIFY + LTEXT "UDP Port",3,10,65,32,15,0,WS_EX_NOPARENTNOTIFY + PUSHBUTTON "Cancel",ID_CANCEL,15,95,35,14,0,WS_EX_NOPARENTNOTIFY + PUSHBUTTON "Apply",ID_SAVE,55,95,35,14,0,WS_EX_NOPARENTNOTIFY + CONTROL "UDP Flag ",1004,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,8,45,50,14,WS_EX_RIGHT + CONTROL "Broadcast Flag ",1005,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,68,45,70,14,WS_EX_RIGHT +END + +IDD_WL2KSYSOP DIALOGEX 0, 0, 277, 355 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "WL2K Sysop Record Update" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Name",3,5,31,28,10,0,WS_EX_NOPARENTNOTIFY + EDITTEXT NAME,60,30,64,12,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Address Line 1",6,5,72,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT ADDR1,60,71,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Address Line 2",7,5,92,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT ADDR2,60,91,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "City",4,5,112,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT CITY,60,111,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "State",8,5,132,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT STATE,60,131,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Country",9,5,152,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT COUNTRY,60,151,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + PUSHBUTTON "Cancel",ID_CANCEL,115,311,47,17,0,WS_EX_NOPARENTNOTIFY + PUSHBUTTON "Apply",ID_SAVE,55,311,47,17,0,WS_EX_NOPARENTNOTIFY + LTEXT "PostCode",10,5,172,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT POSTCODE,60,171,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Email",11,5,192,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT EMAIL,60,191,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Website",12,5,212,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT WEBSITE,60,209,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Phone",13,6,232,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT PHONE,60,231,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Addiitional Data",14,5,252,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT ADDITIONALDATA,60,251,205,14,ES_AUTOHSCROLL | NOT + WS_BORDER,WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Password",15,5,13,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT IDC_Password,60,12,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Locator",16,5,49,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT IDC_Locator,60,48,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE +END + +UIMAINWINDOW DIALOG DISCARDABLE 100, 100, 323, 304 +STYLE WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "BPQ32 UI Utility" +CLASS "UIMAINWINDOW" +FONT 8, "FixedSys" +BEGIN +END + +PORTPAGE DIALOG DISCARDABLE 26, 5, 366, 186 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "FixedSys" +BEGIN + LTEXT "Send Beacon Every",IDC_STATIC,17,25,68,14, + SS_CENTERIMAGE + EDITTEXT IDC_INTERVAL,90,26,20,12,ES_AUTOHSCROLL + LTEXT "Minutes to ",IDC_STATIC,117,25,55,14,SS_CENTERIMAGE + EDITTEXT IDC_UIDEST,175,26,59,12,ES_UPPERCASE | ES_AUTOHSCROLL + CONTROL "Send From File",IDC_FROMFILE,"Button",BS_AUTOCHECKBOX | + BS_VCENTER | WS_TABSTOP,17,59,68,13 + EDITTEXT IDC_FILENAME,89,58,223,12,ES_AUTOHSCROLL + PUSHBUTTON "Find File",IDC_FILE,316,58,40,12 + DEFPUSHBUTTON "Save",IDOK,122,142,40,12 + DEFPUSHBUTTON "Test",ID_TEST,205,142,40,12 + EDITTEXT IDC_MESSAGE,20,84,294,52,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "",IDC_PORTNAME,12,9,139,14,SS_CENTERIMAGE + LTEXT "Path",IDC_STATIC,17,42,57,14,SS_CENTERIMAGE + EDITTEXT IDC_UIDIGIS,90,42,223,12,ES_UPPERCASE | ES_AUTOHSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Terminal Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END +END + +CONS_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Terminal Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END + POPUP "Actions" + BEGIN + MENUITEM "Save Nodes to file BPQNODES.DAT", BPQSAVENODES + MENUITEM "Save Registry Configuration", BPQSAVEREG + MENUITEM "Diagnostic Dump to file BPQDUMP", BPQDUMP + MENUITEM "Re-read Rigcontrol Config", SCANRECONFIG + MENUITEM "Re-read APRS Config", APRSRECONFIG + MENUITEM "Start Minimized", BPQSTARTMIN + MENUITEM "Minimize to Notification Area (System Tray)", BPQMINTOTRAY + MENUITEM "Update WL2K Sysop Record", IDD_WL2KSYSOP + END +END + +TERM_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Terminal Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END + POPUP "Action" + BEGIN + MENUITEM "Connect", BPQCONNECT + MENUITEM "Disconnect", BPQDISCONNECT, GRAYED + END + POPUP "Config" + BEGIN + MENUITEM "Font", ID_SETUP_FONT + MENUITEM "Enable Bells", BPQBELLS + MENUITEM "Strip Linefeeds", BPQStripLF + MENUITEM "Log Output", BPQLogOutput + MENUITEM "Send Disconnected", BPQSendDisconnected + MENUITEM "Chat Terminal Mode", CHATTERM + MENUITEM "Restore Windows on load", ID_WINDOWS_RESTORE + MENUITEM "Beep if input too long", ID_WARNWRAP + MENUITEM "Wrap Input", ID_WRAP + MENUITEM "Flash instead of Beep on Bell", ID_FLASHONBELL + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Output Window", BPQCOPYOUT + MENUITEM "Clear Output Window", BPQCLEAROUT + END + MENUITEM "Help", BPQHELP +END + +MON_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END + POPUP "Monitor" + BEGIN + MENUITEM "Monitor TX", BPQMTX + MENUITEM "Monitor Supervisory", BPQMCOM + MENUITEM "Monitor UI Only", MON_UI_ONLY + MENUITEM "Monitor NODES", BPQMNODES + MENUITEM "Enable Colour", MONCOLOUR + MENUITEM "Log Monitor", BPQLogMonitor + MENUITEM "Trace APRS-IS", MONITORAPRS + MENUITEM "Clear all port flags", StopALLMon + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYMON + MENUITEM "Clear Monitor Window", BPQCLEARMON + END + MENUITEM "Help", BPQHELP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// WAVE +// + +INCOMINGCALL WAVE MOVEABLE PURE "Ring.wav" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "kernelresource.h\0" + """\r\n" + "bpqtermmdi.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""bpqtermmdi.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""..\\CommonSource\\Versions.h""\r\n" + "#include ""..\\CommonSource\\StdVer.inc""\r\n" + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "BPQMAINWINDOW", DIALOG + BEGIN + RIGHTMARGIN, 360 + END + + IDD_WL2KSYSOP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 270 + TOPMARGIN, 7 + BOTTOMMARGIN, 348 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "..\CommonSource\Versions.h" +#include "..\StdVer.inc" + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/L2Code.c b/L2Code.c new file mode 100644 index 0000000..261c909 --- /dev/null +++ b/L2Code.c @@ -0,0 +1,3865 @@ +/* +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 +*/ + +// +// C replacement for L2Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" + +#include "CHeaders.h" +#include "tncinfo.h" + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY + // TO AN I(P) +#define RNRSET 2 // RNR RECEIVED FROM OTHER END +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED +#define RNRSENT 0x10 // WE HAVE SEND RNR +#define POLLSENT 0x20 // POLL BIT OUTSTANDING + +#define ONEMINUTE 60*3 +#define TENSECS 10*3 +#define THREESECS 3*3 + + +VOID L2SENDCOMMAND(); +VOID L2ROUTINE(); +MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD); +VOID SendSupervisCmd(struct _LINKTABLE * LINK); +void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF); +VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD); +VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD); +VOID ACKMSG(struct _LINKTABLE * LINK); +VOID InformPartner(struct _LINKTABLE * LINK, int Reason); +UINT RR_OR_RNR(struct _LINKTABLE * LINK); +VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); +VOID CLEAROUTLINK(struct _LINKTABLE * LINK); +VOID SENDFRMR(struct _LINKTABLE * LINK); +char * SetupNodeHeader(struct DATAMESSAGE * Buffer); +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session); +VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); +VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL); +VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS); +VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID RESET2X(struct _LINKTABLE * LINK); +VOID RESET2(struct _LINKTABLE * LINK); +VOID CONNECTREFUSED(struct _LINKTABLE * LINK); +VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL); +VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG); +VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); +VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK); +VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG); +BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE); +VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG); +VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); +VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); +VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); +int COUNTLINKS(int Port); +VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions); +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID L2SWAPADDRESSES(MESSAGE * Buffer); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +VOID SENDSABM(struct _LINKTABLE * LINK); +VOID L2SENDXID(struct _LINKTABLE * LINK); +VOID __cdecl Debugprintf(const char * format, ...); +VOID Q_IP_MSG(MESSAGE * Buffer); +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT); +VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +BOOL CompareAliases(UCHAR * c1, UCHAR * c2); +VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly); +VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg); +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer); +BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg); +VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); +UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg); +VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +int CountBits(uint32_t in); +void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer); +void DetachKISSHF(struct PORTCONTROL * PORT); +void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK); +void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode); + +extern int REALTIMETICKS; + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + +// FRMR REJECT FLAGS + +#define SDINVC 1 // INVALID COMMAND +#define SDNRER 8 // INVALID N(R) + + + +UCHAR NO_CTEXT = 0; +UCHAR ALIASMSG = 0; +extern UINT APPLMASK; +static UCHAR ISNETROMMSG = 0; +UCHAR MSGFLAG = 0; +extern UCHAR * ALIASPTR; + +UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25 +UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +extern BOOL LogAllConnects; + +APPLCALLS * APPL; + +VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer) +{ + // LEVEL 2 PROCESSING + + MESSAGE * ADJBUFFER; + struct _LINKTABLE * LINK; + UCHAR * ptr; + int n; + UCHAR CTL; + uintptr_t Work; + UCHAR c; + + // Check for invalid length (< 22 7Header + 7Addr + 7Addr + CTL + + if (Buffer->LENGTH < (18 + sizeof(void *))) + { + Debugprintf("BPQ32 Bad L2 Msg Port %d Len %d", PORT->PORTNUMBER, Buffer->LENGTH); + ReleaseBuffer(Buffer); + return; + } + + PORT->L2FRAMES++; + + ALIASMSG = 0; + APPLMASK = 0; + ISNETROMMSG = 0; + + MSGFLAG = 0; // CMD/RESP UNDEFINED + + // Check for Corrupted Callsign in Origin (to keep MH list clean) + + ptr = &Buffer->ORIGIN[0]; + n = 6; + + c = *(ptr) >> 1; + + if (c == ' ') // Blank Call + { + Debugprintf("BPQ32 Blank Call Port &%", PORT->PORTNUMBER); + ReleaseBuffer(Buffer); + return; + } + + while(n--) + { + // Try a bit harder to detect corruption + + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + + // Check Digis if present + + if ((Buffer->ORIGIN[6] & 1) == 0) // Digis + { + ptr = &Buffer->CTL; + n = 6; + + while(n--) + { + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + } + + BPQTRACE(Buffer, TRUE); // TRACE - RX frames to APRS + + if (PORT->PORTMHEARD) + MHPROC(PORT, Buffer); + + /// TAJ added 07/12/2020 for 'all RX traffic as IfinOctects + + InOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN; + + // CHECK THAT ALL DIGIS HAVE BEEN ACTIONED, + // AND ADJUST FOR DIGIPEATERS IF PRESENT + + n = 8; // MAX DIGIS + ptr = &Buffer->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + + if ((*ptr & 0x80) == 0) // Digi'd bit + { + // FRAME HAS NOT BEEN REPEATED THROUGH CURRENT DIGI - + // SEE IF WE ARE MEANT TO DIGI IT + + struct XDIGI * XDigi = PORT->XDIGIS; // Cross port digi setup + + ptr -= 6; // To start of Call + + if (CompareCalls(ptr, MYCALL) || CompareAliases(ptr, MYALIAS) || + CompareCalls(ptr, PORT->PORTALIAS) || CompareCalls(ptr, PORT->PORTALIAS2)) + { + Digipeat(PORT, Buffer, ptr, 0, 0); // Digi it (if enabled) + return; + } + + while (XDigi) + { + if (CompareCalls(ptr, XDigi->Call)) + { + Digipeat(PORT, Buffer, ptr, XDigi->Port, XDigi->UIOnly); // Digi it (if enabled) + return; + } + XDigi = XDigi->Next; + } + + ReleaseBuffer(Buffer); + return; // not complete and not for us + } + n--; + + if (n == 0) + { + ReleaseBuffer(Buffer); + return; // Corrupt - no end of address bit + } + } + + // Reached End of digis, and all actioned, so can process it + + Work = (uintptr_t)&Buffer->ORIGIN[6]; + ptr -= Work; // ptr is now length of digis + + Work = (uintptr_t)Buffer; + ptr += Work; + + ADJBUFFER = (MESSAGE * )ptr; // ADJBUFFER points to CTL, etc. allowing for digis + + // GET CMD/RESP BITS + + if (Buffer->DEST[6] & 0x80) + { + if (Buffer->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + MSGFLAG |= CMDBIT; + } + else + { + if (Buffer->ORIGIN[6] & 0x80) // Only Dest Set + MSGFLAG |= RESP; + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + // SEE IF FOR AN ACTIVE LINK SESSION + + CTL = ADJBUFFER->CTL; + + // IF A UI, THERE IS NO SESSION + + if (FindLink(Buffer->ORIGIN, Buffer->DEST, PORT->PORTNUMBER, &LINK)) + { + L2LINKACTIVE(LINK, PORT, Buffer,ADJBUFFER, CTL, MSGFLAG); + return; + } + + // NOT FOR ACTIVE LINK - SEE IF ADDRESSED TO OUR ADDRESSES + + // FIRST TRY PORT ADDR/ALIAS + + if(PORT->PORTBBSFLAG == 1) + goto PORTCALLISBBS; // PORT CALL/ALIAS ARE FOR BBS + + if (NODE) + goto USING_NODE; + +PORTCALLISBBS: + + // NODE IS NOT ACTIVE, SO PASS CALLS TO PORTCALL/ALIAS TO BBS + + APPLMASK = 1; + + if (CompareCalls(Buffer->DEST, NETROMCALL)) + { + ISNETROMMSG = 1; + goto FORUS; + } + if (PORT->PORTL3FLAG) // L3 Only Port? + goto NOTFORUS; // If L3ONLY, only accept calls to NETROMCALL + + ISNETROMMSG = 0; + +USING_NODE: + + if (CompareCalls(Buffer->DEST, PORT->PORTCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, PORT->PORTALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + + if (NODE == 0) + goto TRYBBS; // NOT USING NODE SYSTEM + + ALIASMSG = 0; + + if (CompareCalls(Buffer->DEST, MYCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, MYALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + +TRYBBS: + + if (BBS == 0) + goto NOWTRY_NODES; // NOT USING BBS CALLS + + // TRY APPLICATION CALLSIGNS/ALIASES + + + APPLMASK = 1; + ALIASPTR = &CMDALIAS[0][0]; + + n = NumberofAppls; + + APPL = APPLCALLTABLE; + + while (n--) + { + if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr + { + // WE MAY NOT BE ALLOWED TO USE THE BBS CALL ON SOME BANDS DUE TO + // THE RATHER ODD UK LICENCING RULES! + // For backward compatibility only apply to appl 1 + + if ((PORT->PERMITTEDAPPLS & APPLMASK) != 0) + { + ALIASMSG = 0; + + if (CompareCalls(Buffer->DEST, APPL->APPLCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, APPL->APPLALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + + if (CompareAliases(Buffer->DEST, APPL->L2ALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + } + } + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + APPL++; + } + + // NOT FOR US - SEE IF 'NODES' OR IP/ARP BROADCAST MESSAGE + +NOWTRY_NODES: + + if (CompareCalls(Buffer->DEST, QSTCALL)) + { + Q_IP_MSG(Buffer); // IP BROADCAST + return; + } + + if (ADJBUFFER->PID != 0xCF) // NETROM MSG? + goto NOTFORUS; // NO + + if (CompareCalls(Buffer->DEST, NODECALL)) + { + if (Buffer->L2DATA[0] == 0xff) // Valid NODES Broadcast + { + PROCESSNODEMESSAGE(Buffer, PORT); + } + } + + ReleaseBuffer(Buffer); + return; + +NOTFORUS: + // + // MAY JUST BE A REPLY TO A 'PRIMED' CQ CALL + // + if ((CTL & ~PFBIT) == SABM) + if (CheckForListeningSession(PORT, Buffer)) + return; // Used buffer to send UA + + ReleaseBuffer(Buffer); + return; + +FORUS: + + // if a UI frame and UIHook Specified, call it + + if (PORT->UIHook && CTL == 3) + PORT->UIHook(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + + L2FORUS(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); +} + + +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + PMHSTRUC MH = PORT->PORTMHEARD; + PMHSTRUC MHBASE = MH; + int i; + int OldCount = 0; + char Freq[16] = ""; + char DIGI = '*'; + double ReportFreq = 0; + + // if port has Rigcontrol associated with it, get frequency + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC && TNC->RIG && TNC->RIG->Valchar[0]) + { + if (TNC->Hardware == H_UZ7HO) + { + // See if we have Center Freq Info + if (TNC->AGWInfo->CenterFreq) + { + ReportFreq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); + } +#ifdef WIN32 + else if (TNC->AGWInfo->hFreq) + { + char Centre[16]; + double ModemFreq; + + SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); + + ModemFreq = atof(Centre); + + ReportFreq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); + } +#endif + else + ReportFreq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 + } + else + + // Not UZ7HO or Linux + + ReportFreq = atof(TNC->RIG->Valchar) + 0.0015; + + _gcvt(ReportFreq, 9, Freq); + } + else + { + if (PORT->RIGPort) + { + struct TNCINFO * TNC = TNCInfo[PORT->RIGPort]; + + if (TNC && TNC->RIG) + { + strcpy(Freq, TNC->RIG->Valchar); + Freq[11] = 0; + } + } + } +// if (Buffer->ORIGIN[6] & 1) + DIGI = 0; // DOn't think we want to do this + + // See if in list + + for (i = 0; i < MHENTRIES; i++) + { + if ((MH->MHCALL[0] == 0) || (CompareCalls(Buffer->ORIGIN, MH->MHCALL) && MH->MHDIGI == DIGI)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + + memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); // Save Digis + MHBASE->MHDIGI = DIGI; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + strcpy(MHBASE->MHFreq, Freq); + MHBASE->MHLocator[0] = 0; + + return; +} + + +int CountFramesQueuedOnSession(TRANSPORTENTRY * Session) +{ + // COUNT NUMBER OF FRAMES QUEUED ON A SESSION + + if (Session == 0) + return 0; + + if (Session->L4CIRCUITTYPE & BPQHOST) + { + return C_Q_COUNT(&Session->L4TX_Q); + } + + if (Session->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION - GET NUMBER UNACKED, AND ADD NUMBER ON TX QUEUE + + int Count = C_Q_COUNT(&Session->L4TX_Q); + UCHAR Unacked = Session->TXSEQNO - Session->L4WS; + + return Count + Unacked; + } + + if (Session->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + struct PORTCONTROL * PORT = Session->L4TARGET.PORT; + EXTPORTDATA * EXT = (EXTPORTDATA *)PORT; + + int ret = EXT->FramesQueued; + + // Check L4 Queue as messages can stay there briefly + + ret += C_Q_COUNT(&Session->L4RX_Q); + + return ret + C_Q_COUNT(&PORT->PORTTX_Q); + } + + // L2 CIRCUIT + + { + int SessCount = C_Q_COUNT(&Session->L4TX_Q); + struct _LINKTABLE * LINK = Session->L4TARGET.LINK; + int L2 = COUNT_AT_L2(LINK); + + return SessCount + L2; + } +} + +int CHECKIFBUSYL2(TRANSPORTENTRY * Session) +{ + // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY + + if (Session->L4CROSSLINK) // CONNECTED? + { + Session = Session->L4CROSSLINK; + + if (CountFramesQueuedOnSession(Session) > 10) + return L4BUSY;; + } + return 0; +} + +VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // MESSAGE ADDRESSED TO OUR CALL OR ALIAS, BUT NOT FOR AN ACTIVE SESSION + + // LINK points to an empty link table entry + + struct ROUTE * ROUTE; + int CTLlessPF = CTL & ~PFBIT; + + PORT->L2FRAMESFORUS++; + + NO_CTEXT = 0; + + // ONLY SABM or UI ALLOWED IF NO SESSION + // Plus XID/TEST/SABME if V2.2 support enabled + + if (CTLlessPF == 3) // UI + { + // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES + + switch(ADJBUFFER->PID) + { + case 0xcf: // Netrom + + if (Buffer->L2DATA[0] == 0xff) // NODES + PROCESSNODEMESSAGE(Buffer, PORT); + + break; + + case 0xcc: // TCP + case 0xcd: // ARP + case 0x08: // NOS FRAGMENTED AX25 TCP/IP + + Q_IP_MSG( Buffer); + return; + } + + ReleaseBuffer(Buffer); + return; + } + + if (PORT->PortUIONLY) // Port is for UI only + { + ReleaseBuffer(Buffer); + return; + } + + if (CTLlessPF == SABME) + { + // Although some say V2.2 requires SABME I don't agree! + + // Reject until we support Mod 128 + + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; + } + + if (CTLlessPF == SREJ) // Used to see if other end supports SREJ on 2.0 + { + // Send FRMR if dont support SREJ + // Send DM if we do + + if (SUPPORT2point2) + L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); + else + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + + return; + } + + if (CTLlessPF == XID) + { + // Send FRMR if we only support V 2.0 + + if (SUPPORT2point2 == FALSE) + { + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; + } + // if Support 2.2 drop through + } + + if (CTLlessPF == TEST) + { + // I can't see amy harm in replying to TEST + + L2SENDRESP(PORT, Buffer, ADJBUFFER, TEST); + return; + } + + +// if (CTLlessPF != SABM && CTLlessPF != SABME) + if (CTLlessPF != SABM && CTLlessPF != XID) + { + if ((MSGFLAG & CMDBIT) && (CTL & PFBIT)) // Command with P? + L2SENDDM(PORT, Buffer, ADJBUFFER); + else + ReleaseBuffer(Buffer); // Ignore if not + + return; + } + + // Exclude and limit tests are done for XID and SABM + + if (NODE == 0 && BBS == 0) // Don't want any calls + { + ReleaseBuffer(Buffer); + return; + } + +#ifdef EXCLUDEBITS + + // CHECK ExcludeList + + if (CheckExcludeList(Buffer->ORIGIN) == 0) + { + ReleaseBuffer(Buffer); + return; + } +#endif + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (PORT->PERMITTEDCALLS) + { + UCHAR * ptr = PORT->PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(Buffer->ORIGIN, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + ReleaseBuffer(Buffer); + return; + } + } + } + + // IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + // From a known node + + NO_CTEXT = 1; + + if (ROUTE->NEIGHBOUR_FLAG == 1 && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0 + { + ReleaseBuffer(Buffer); + return; + } + } + + // CHECK PORT CONNECT LIMITS + + if (PORT->USERS) + { + if (COUNTLINKS(PORT->PORTNUMBER) >= PORT->USERS) + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + } + + // if KISSHF, check if attached. If so, reject. If not, attach. + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct TNCINFO * TNC = PORT->TNC; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + } + + // OK to accept SABM or XID + + if (CTLlessPF == XID) + { + ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + return; + } + + // Not XID, so must be SABM + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM +} + + +VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // I think it is fairly safe to accept XID as soon as we + // can process SREJ, but only accept Mod 8 and 256 Byte frames + + // I think the only way to run 2.2 Mod 8 is to preceed a + // SABM with XID, but others don't seem to agree! + + // Run through XID fields, changing any we don't like, + // then return an XID response + + // Decode and process XID + + UCHAR * ptr = &ADJBUFFER->PID; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + + break; + + case 3: + + if ((value & OPMustHave) != OPMustHave) + goto BadXID; + + if ((value & OPMod8) == 0) + goto BadXID; + + if ((value & OPSREJMult) == 0) + goto BadXID; + + + // Reply Mod 8 SREJMULTI + + value = OPMustHave | OPSREJMult | OPMod8; + ptr -=3; + *ptr++ = value >> 16; + *ptr++ = value >> 8; + *ptr++ = value; + + + break; + + case 6: //RX Size + + break; + + case 8: //RX Window + + break; + } + } + + // Send back as XID response + + LINK->L2STATE = 1; // XID received + LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID + LINK->L2TIME = PORT->PORTT1; + + LINK->LINKPORT = PORT; + + // save calls so we can match up SABM when it comes + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + ReleaseBuffer(Buffer); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + } + } + + ADJBUFFER->CTL = CTL | PFBIT; + +// Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + return; + } +BadXID: + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; +} + + + +int COUNTLINKS(int Port) +{ + //COUNT LINKS ON PORT + + int i = MAXLINKS, n = 0; + struct _LINKTABLE * LINK = LINKS; + + while (i--) + { + if (LINK->LINKPORT && LINK->LINKPORT->PORTNUMBER == Port) + n++; + + LINK++; + } + + return n; +} + + +VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // MESSAGE ON AN ACTIVE LINK + + int CTLlessPF = CTL & ~PFBIT; + + PORT->L2FRAMESFORUS++; + + // ONLY SABM or UI ALLOWED IF NO SESSION + + if (CTLlessPF == 3) // UI + { + // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES + + switch(ADJBUFFER->PID) + { + case 0xcf: // Netrom + + if (Buffer->L2DATA[0] == 0xff) // NODES + PROCESSNODEMESSAGE(Buffer, PORT); + + break; + + case 0xcc: // TCP + case 0xcd: // ARP + case 0x08: // NOS FRAGMENTED AX25 TCP/IP + + Q_IP_MSG( Buffer); + return; + } + + ReleaseBuffer(Buffer); + return; + } + + if (CTLlessPF == DISC) + { + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + return; + } + + + if (LINK->L2STATE == 1) + { + // XID State. Should be XID response if 2.2 ok or DM/FRMR if not + + if (MSGFLAG & RESP) + { + if (CTLlessPF == DM || CTLlessPF == FRMR) + { + // Doesn't support XID - Send SABM + + LINK->L2STATE = 2; + LINK->Ver2point2 = FALSE; + LINK->L2TIMER = 1; // USe retry to send SABM + } + else if (CTLlessPF == XID) + { + // Process response to make sure ok, Send SABM or DISC + + LINK->L2STATE = 2; + LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID + LINK->L2TIMER = 1; // USe retry to send SABM + } + + ReleaseBuffer(Buffer); + return; + } + + // Command on existing session. Could be due to other end missing + // the XID response, so if XID just resend response + + } + + if (CTLlessPF == XID && (MSGFLAG & CMDBIT)) + { + // XID Command on active session. Other end may be restarting. Send Response + + ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + return; + } + + + if (CTLlessPF == SABM) + { + // SABM ON EXISTING SESSION - IF DISCONNECTING, REJECT + + if (LINK->L2STATE == 1) // Sent XID? + { + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM + return; + } + + if (LINK->L2STATE == 4) // DISCONNECTING? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + // THIS IS A SABM ON AN EXISTING SESSION + + // THERE ARE SEVERAL POSSIBILITIES: + + // 1. RECONNECT COMMAND TO TNC + // 2. OTHER END THINKS LINK HAS DIED + // 3. RECOVERY FROM FRMR CONDITION + // 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA + + // FOR 1-3 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4 + // SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A + // BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG + // WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME + // CONDITION 4, AND JUST RESEND THE UA + + + if (LINK->SESSACTIVE == 0) // RESET OF ACTIVE CIRCUIT? + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // No, so repeat UA + return; + } + + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM + return; + + } + + L2_PROCESS(LINK, PORT, Buffer, CTL, MSGFLAG); +} + + +VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG) +{ + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + TRANSPORTENTRY * Session; + int CONERROR; + + if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG); + + if (LINK->L2STATE != 5) // Setup OK? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed + return; + } + + + // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION + + if (APPLMASK == 0) + { + // Not ATTACH TO APPL + + // Send CTEXT if connect to NODE/Port Alias, or NODE/Port Call, and FULL_CTEXT set + // Dont sent to known NODEs, or appl connects + + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + AttachKISSHF(PORT, Buffer); + + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (NO_CTEXT == 1) + return; + + if (FULL_CTEXT == 0 && !ALIASMSG) // Any connect, or call to alias + return; + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (CTEXTLEN) + { + Totallen = CTEXTLEN; + ptr = CTEXTMSG; + } + else + return; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + return; + } + + + // Connnect to APPL + + if (LINK->LINKTYPE != 1) + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // RESET OF DOWN/CROSSLINK + return; + } + + if (LINK->CIRCUITPOINTER) + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET + return; + } + + // IF RUNNING ONLY BBS (NODE=0), THIS MAY BE EITHER A USER OR NODE + // TRYING TO SET UP A L4 CIRCUIT - WE DONT WANT TO ATTACH A NODE TO + // THE BBS! + + if (NODE == 0) + { + // NOW THINGS GET DIFICULT - WE MUST EITHER WAIT TO SEE IF A PID CF MSG + // ARRIVES, OR ASSUME ALL NODES ARE IN NEIGHBOURS - I'LL TRY THE LATTER + // AND SEE HOW IT GOES. tHIS MEANS THAT YOU MUST DEFINE ALL ROUTES + // IN CONFIG FILE + + struct ROUTE * ROUTE; + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + // It's a node + + L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET + return; + } + } + + + Session = SetupSessionForL2(LINK); // CREATE INCOMING L4 SESSION + + if (Session == NULL) + { + CLEAROUTLINK(LINK); + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + // NOW TRY A BBS CONNECT + // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS + + if (ALIASPTR[0] > ' ') + { + struct DATAMESSAGE * Msg; + + // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + AttachKISSHF(PORT, Buffer); + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + Msg = GetBuff(); + + if (Msg) + { + + Msg->PID = 0xf0; + + memcpy(Msg->L2DATA, APPL->APPLCMD, 12); + Msg->L2DATA[12] = 13; + + Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR + + C_Q_ADD(&LINK->RX_Q, Msg); + } + + return; + } + + if (cATTACHTOBBS(Session, APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0) + { + // NO BBS AVAILABLE + + CLEARSESSIONENTRY(Session); + CLEAROUTLINK(LINK); + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + AttachKISSHF(PORT, Buffer); + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + L2SENDUA(PORT, Buffer, ADJBUFFER); +} + +VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG) +{ + // COPY ADDRESS INFO TO LINK TABLE + + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + LINK->L2TIME = PORT->PORTT1; // Set tomeoiut for no digis + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + + LINK->L2TIME += PORT->PORTT1; // Adjust timeout for digis + } + } + + // THIS MAY BE RESETTING A LINK - BEWARE OF CONVERTING A CROSSLINK TO + // AN UPLINK AND CONFUSING EVERYTHING + + LINK->LINKPORT = PORT; + + if (LINK->LINKTYPE == 0) + { + if (ISNETROMMSG && NODE == 0) // Only allow crosslink if node = 0 + LINK->LINKTYPE = 3; // Crosslink + else + LINK->LINKTYPE = 1; // Uplink + } + LINK->L2TIMER = 0; // CANCEL TIMER + + LINK->L2SLOTIM = T3; // SET FRAME SENT RECENTLY + + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + LINK->L2STATE = 5; + + // IF VERSION 1 MSG, SET FLAG + + if (MSGFLAG & VER1) + LINK->VER1FLAG |= 1; + +} + +VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) +{ + L2SENDRESP(PORT, Buffer, ADJBUFFER, UA); +} + +VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) +{ + L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); +} + +VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) +{ + // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY + + // SET APPROPRIATE P/F BIT + + ADJBUFFER->CTL = CTL | PFBIT; + + Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + + return; +} + + +VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) +{ + // Send FRMR Invalid Control field + + // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY + + // SET APPROPRIATE P/F BIT + + UCHAR * ptr; + + ADJBUFFER->CTL = FRMR | PFBIT; + + ptr = &ADJBUFFER->PID; + + *(ptr++) = CTL; // MOVE REJECT C-BYTE + *(ptr++) = 0; + *(ptr++) = SDINVC; // MOVE REJECT FLAGS + + Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 18; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + + return; +} + +VOID L2SWAPADDRESSES(MESSAGE * Buffer) +{ + // EXCHANGE ORIGIN AND DEST, AND REVERSE DIGIS (IF PRESENT) + + char TEMPFIELD[7]; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + + memcpy(TEMPFIELD, Buffer->ORIGIN, 7); + memcpy(Buffer->ORIGIN, Buffer->DEST, 7); + memcpy(Buffer->DEST, TEMPFIELD, 7); + + Buffer->ORIGIN[6] &= 0x1e; // Mask SSID + Buffer->ORIGIN[6] |= 0xe0; // Reserved and Response + + Buffer->DEST[6] &= 0x1e; // Mask SSID + Buffer->DEST[6] |= 0x60; // Reserved + + if ((TEMPFIELD[6] & 1) == 0) + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - copy back + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &Buffer->CTL; + + while (*ptr1) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // End of addresses + } + else + { + Buffer->ORIGIN[6] |= 1; // End of address + } +} + +BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries) +{ + // ROUTE POINTS TO A NEIGHBOUR - FIND AN L2 SESSION FROM US TO IT, OR INITIATE A NEW ONE + + struct _LINKTABLE * LINK; + struct PORTCONTROL * PORT; + int FRACK; + + if (FindLink(ROUTE->NEIGHBOUR_CALL, NETROMCALL, ROUTE->NEIGHBOUR_PORT, &LINK)) + { + // SESSION ALREADY EXISTS + + LINK->LINKTYPE = 3; // MAKE SURE IT KNOWS ITS A CROSSLINK + ROUTE->NEIGHBOUR_LINK = LINK; + LINK->NEIGHBOUR = ROUTE; + + return TRUE; + } + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + if (LINK == NULL) + return FALSE; // No free links + + + ROUTE->NEIGHBOUR_LINK = LINK; + LINK->NEIGHBOUR = ROUTE; + + LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT); + + if (PORT == NULL) + return FALSE; // maybe port has been deleted + + // IF ROUTE HAS A FRACK, SET IT + + if (ROUTE->NBOUR_FRACK) + FRACK = ROUTE->NBOUR_FRACK; + else + FRACK = PORT->PORTT1; + + LINK->L2TIME = FRACK; // SET TIMER VALUE + + // IF ROUTE HAS A WINDOW, SET IT + + if (ROUTE->NBOUR_MAXFRAME) + LINK->LINKWINDOW = ROUTE->NBOUR_MAXFRAME; + else + LINK->LINKWINDOW = PORT->PORTWINDOW; + +// if (SUPPORT2point2) +// LINK->L2STATE = 1; // Send XID +// else + LINK->L2STATE = 2; + + memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7); + memcpy(LINK->OURCALL, NETROMCALL, 7); + + if (ROUTE->NEIGHBOUR_DIGI1[0]) + { + memcpy(LINK->DIGIS, ROUTE->NEIGHBOUR_DIGI1, 7); + LINK->L2TIME += FRACK; + } + + if (ROUTE->NEIGHBOUR_DIGI2[0]) + { + memcpy(&LINK->DIGIS[7], ROUTE->NEIGHBOUR_DIGI1, 7); + LINK->L2TIME += FRACK; + } + + LINK->LINKTYPE = 3; // CROSSLINK + + if (Retries) + LINK->L2RETRIES = PORT->PORTN2 - Retries; + + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + + return TRUE; +} + + + +BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries) +{ + // Allows caller to specify number of times SABM should be sent + + return InternalL2SETUPCROSSLINK(ROUTE, Retries); +} + +BOOL L2SETUPCROSSLINK(PROUTE ROUTE) +{ + return InternalL2SETUPCROSSLINK(ROUTE, 0); +} + +VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) +{ + // PROCESS LEVEL 2 PROTOCOL STUFF + + // SEE IF COMMAND OR RESPONSE + + if ((MSGFLAG & CMDBIT) == 0) + { + + // RESPONSE OR VERSION 1 + + // IF RETRYING, MUST ONLY ACCEPT RESPONSES WITH F SET (UNLESS RUNNING V1) + + if ((CTL & PFBIT) || LINK->VER1FLAG == 1) + { + // F SET or V1 - CAN CANCEL TIMER + + LINK->L2TIMER = 0; // CANCEL LINK TIMER + } + } + + if (LINK->L2STATE == 3) + { + + // FRMR STATE - IF C(P) SEND FRMR, ELSE IGNORE + + if (CTL & PFBIT) + { + if (CTL == (FRMR | PFBIT)) // if both ends in FRMR state, reset link + { + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + L2SENDCOMMAND(LINK, SABM | PFBIT); + } + } + + if (MSGFLAG & CMDBIT) + { + // SEND FRMR AGAIN + + SENDFRMR(LINK); + } + + ReleaseBuffer(Buffer); + return; + } + + if (LINK->L2STATE >= 5) + { + // LINK IN STATE 5 OR ABOVE - LINK RUNNING + + if ((CTL & 1) == 0) // I frame + { + SDIFRM(LINK, PORT, Buffer, CTL, MSGFLAG); // consumes buffer + return; + } + + if ((CTL & 2)) // U frame + { + SDUFRM(LINK, PORT, Buffer, CTL); //consumes buffer + return; + } + + // ELSE SUPERVISORY, MASK OFF N(R) AND P-BIT + + switch (CTL & 0x0f) + { + // is there any harm in accepoting SREJ even if we don't + // otherwise support 2.2? + + case REJ: + case SREJ: + + PORT->L2REJCOUNT++; + + case RR: + case RNR: + + SFRAME(LINK, PORT, CTL, MSGFLAG); + break; + + default: + + // UNRECOGNISABLE COMMAND + + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE + LINK->SDREJF |= SDINVC; // SET INVALID COMMAND REJECT + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + } + + ReleaseBuffer(Buffer); + return; + } + + // NORMAL DISCONNECT MODE + + // COULD BE UA, DM - SABM AND DISC HANDLED ABOVE + + switch (CTL & ~PFBIT) + { + case UA: + + // UA RECEIVED + + if (LINK->L2STATE == 2) + { + // RESPONSE TO SABM - SET LINK UP + + RESET2X(LINK); // LEAVE QUEUED STUFF + + LINK->L2STATE = 5; + LINK->L2TIMER = 0; // CANCEL TIMER + LINK->L2RETRIES = 0; + LINK->L2SLOTIM, T3; // SET FRAME SENT RECENTLY + + // IF VERSION 1 MSG, SET FLAG + + if (MSGFLAG & VER1) + LINK->VER1FLAG |= 1; + + // TELL PARTNER CONNECTION IS ESTABLISHED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + KISSHFConnected(PORT, LINK); + + SENDCONNECTREPLY(LINK); + ReleaseBuffer(Buffer); + return; + } + + if (LINK->L2STATE == 4) // DISCONNECTING? + { + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + } + + // UA, BUT NOT IN STATE 2 OR 4 - IGNORE + + ReleaseBuffer(Buffer); + return; + + case DM: + + // DM RESPONSE - IF TO SABM, SEND BUSY MSG + + if (LINK->L2STATE == 2) + { + CONNECTREFUSED(LINK); // SEND MESSAGE IF DOWNLINK + return; + } + + // DM ESP TO DISC RECEIVED - OTHER END HAS LOST SESSION + + // CLEAR OUT TABLE ENTRY - IF INTERNAL TNC, SHOULD SEND *** DISCONNECTED + + InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + ReleaseBuffer(Buffer); + return; + + case FRMR: + + // FRAME REJECT RECEIVED - LOG IT AND RESET LINK + + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + PORT->L2FRMRRX++; + + L2SENDCOMMAND(LINK, SABM | PFBIT); + return; + + default: + + // ANY OTHER - IGNORE + + ReleaseBuffer(Buffer); + } +} + +VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL) +{ + // PROCESS AN UNSEQUENCED COMMAND (IN LINK UP STATES) + + switch (CTL & ~PFBIT) + { + case UA: + + // DISCARD - PROBABLY REPEAT OF ACK OF SABM + + break; + + case FRMR: + + // FRAME REJECT RECEIVED - LOG IT AND RESET LINK + + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + PORT->L2FRMRRX++; + + L2SENDCOMMAND(LINK, SABM | PFBIT); + break; + + case DM: + + // DM RESPONSE - SESSION MUST HAVE GONE + + // SEE IF CROSSLINK ACTIVE + + InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + break; + + default: + + // UNDEFINED COMMAND + + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE + LINK->SDREJF |= SDINVC; + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + + } + + ReleaseBuffer(Buffer); +} + + +VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG) +{ + // CHECK COUNTS, AND IF RNR INDICATE _BUFFER SHORTAGE AT OTHER END + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + return; + } + + SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + return; + } + + if ((CTL & 0xf) == SREJ) + { + // Probably safer to handle SREJ completely separately + + // Can we get SREJ Command with P??(Yes) + + // Can we just resend missing frame ?? (Think so!) + + // We support MultiSREJ (can gave additional missing frame + // numbers in the Info field + + // I don't see the point of Multi unless we wait fot an F bit, + // bur maybe not safe to assume others do the same + + // So if I get SREJ(F) I can send missing frame(s) + + if (MSGFLAG & RESP) + { + // SREJ Response + + if (CTL & PFBIT) + { + // SREJ(F). Send Frames() + + UCHAR NS = (CTL >> 5) & 7; // Frame to resend + + struct PORTCONTROL * PORT; + UCHAR * ptr1, * ptr2; + UCHAR CTL; + int count; + MESSAGE * Msg; + MESSAGE * Buffer; + + Msg = LINK->FRAMES[NS]; // is frame available? + + if (Msg == NULL) + return; // Wot!! + + // send the frame + + // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr2 NOW POINTS TO COMMAND BYTE + + // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER + + CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS + CTL |= NS << 1; // BITS 1-3 OF CONTROL BYTE + + // SET P BIT IF NO MORE TO SEND (only more if Multi SREJ) + + if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 + { + CTL |= PFBIT; + LINK->L2FLAGS |= POLLSENT; + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) + + Buffer->Linkptr = LINK; + } + + *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) + + count = Msg->LENGTH - MSGHDDRLEN; + + if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY + { + ptr1 = (UCHAR *)Msg; + ptr1 += MSGHDDRLEN; + memcpy(ptr2, ptr1, count); + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } + } + } + + return; + } + + // VALID RR/RNR RECEIVED + + LINK->L2FLAGS &= ~RNRSET; //CLEAR RNR + + if ((CTL & 0xf) == RNR) + LINK->L2FLAGS |= RNRSET; //Set RNR + + if (MSGFLAG & CMDBIT) + { + // ALWAYS REPLY TO RR/RNR/REJ COMMAND (even if no P bit ??) + + // FIRST PROCESS RESEQ QUEUE + + //; CALL PROCESS_RESEQ + + // IGNORE IF AN 'F' HAS BEEN SENT RECENTLY + + if (LINK->LAST_F_TIME + 15 > REALTIMETICKS) + return; // DISCARD + + CTL = RR_OR_RNR(LINK); + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PFBIT; + + L2SENDRESPONSE(LINK, CTL); + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + + LINK->L2ACKREQ = 0; // CANCEL DELAYED ACKL2 + + // SAVE TIME IF 'F' SENT' + + LINK->LAST_F_TIME = REALTIMETICKS; + + return; + } + + // Response + + if ((CTL & PFBIT) == 0 && LINK->VER1FLAG == 0) + { + // RESPONSE WITHOUT P/F DONT RESET N(S) (UNLESS V1) + + return; + + } + + // RESPONSE WITH P/F - MUST BE REPLY TO POLL FOLLOWING TIMEOUT OR I(P) + + // THERE IS A PROBLEM WITH REPEATED RR(F), SAY CAUSED BY DELAY AT L1 + + // AS FAR AS I CAN SEE, WE SHOULD ONLY RESET N(S) IF AN RR(F) FOLLOWS + // AN RR(P) AFTER A TIMEOUT - AN RR(F) FOLLOWING AN I(P) CANT POSSIBLY + // INDICATE A LOST FRAME. ON THE OTHER HAND, A REJ(F) MUST INDICATE + // A LOST FRAME. So dont reset NS if not retrying, unless REJ + + + // someone (probably WLE KISS Driver) is sending REJ followed by RR(F) + // after lost frame and i(p) + +/* +1:Fm W4DHW-10 To W4DHW [17:08:03R] [+++] +úJƒÑZKÀ)x@DÖBÉrNôÝ4XÔ;i‹#CäM³,ïнҼüÕrÞùOË N¿XæâïÀÄ5Ð(È|©¸ì#íÿÈUþïÒcYÞÍl—çûž)Àú璘oÑȼö>©Ï9¨*ÎG²£ëðû(6À5C‹!áL±Ÿîßì÷³ÙQð»pƒËIH”Š;ØÚi¯Ò>â9p¶B¬õ<ÌcŠEPž«<ŸÊ{0aŽ(’­YÕ–´M¢†—N£+<ÇIÐ[–áÛPw–[^]6ƒ2\ù¿9äÆov{‹¥Å¸mm [17:08:03T] +1:Fm W4DHW To W4DHW-10 [17:08:03T] +1:Fm W4DHW To W4DHW-10 [17:08:03T] + + is there a problem with restting on RR(F) following I(P)? + + I think the problem is restting NS twice if you get delayed responses to + I or RR (P). So lets try only resetting NS once for each P sent + +*/ +// if ((CTL & 0xf) == REJ || LINK->L2RETRIES) + if ((LINK->L2FLAGS & POLLSENT)) + { + RESETNS(LINK, (CTL >> 5) & 7); // RESET N(S) AND COUNT RETRIED FRAMES + + LINK->L2RETRIES = 0; + LINK->L2TIMER = 0; // WILL RESTART TIMER WHEN RETRY SENT + } + + LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) or RR(P) SET + + if ((CTL & 0xf) == RNR) + { + // Dont Clear timer on receipt of RNR(F), spec says should poll for clearing of busy, + // and loss of subsequent RR will cause hang. Perhaps should set slightly longer time?? + // Timer may have been cleared earlier, so restart it + + LINK->L2TIMER = LINK->L2TIME; + } +} + +//*** PROCESS AN INFORMATION FRAME + +VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) +{ + int NS; + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + ReleaseBuffer(Buffer); + return; + } + + SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + ReleaseBuffer(Buffer); + return; + } + + LINK->SESSACTIVE = 1; // SESSION IS DEFINITELY SET UP + + NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + + // IPOLL (sending an I(P) frame following timeout instead of RR(P)) + // is a problem. We need to send REJ(F), but shouldn't add to collector. + // We also need to handle repeated I(P), so shouldn't set REJSENT in + // this state. + + if ((((NS + 1) & 7) == LINK->LINKNR) && (CTL & PFBIT)) + { + // Previous Frame and P set - Assume IPOLL + + PORT->L2OUTOFSEQ++; + LINK->L2STATE = 6; + + LINK->L2ACKREQ = 0; // CANCEL RR NEEDED + + // We need to protect against sending multiple REJ(F) if channel + // delays mean we get two I(P) close together (how close is close ??) + // SM has default IPOLL limit of 30 bytes or about a second at 300 + // ACKMODE should avoid this anyway, and resptime of under 3 secs + // is unlikely so say 2.5 secs ?? + + if (LINK->LAST_F_TIME + 25 > REALTIMETICKS) + { + ReleaseBuffer(Buffer); + return; + } + + SEND_RR_RESP(LINK, PFBIT); + LINK->LAST_F_TIME = REALTIMETICKS; + + ReleaseBuffer(Buffer); + return; + } + +CheckNSLoop: + + if (NS != LINK->LINKNR) // EQUAL TO OUR N(R)? + { + // There is a frame missing. + // if we have just sent a REJ we have at least one out + // of sequence frame in RXFRAMES + + // so if we have frame LINK->LINKNR we can process it + // and remove it from RXFRAMES. If we are then back + // in sequence we just carry on. + + if (LINK->RXFRAMES[LINK->LINKNR]) + { + // We have the first missing frame. Process it. + + MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); + + Debugprintf("L2 process saved Frame %d", LINK->LINKNR); + PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer + + // NR has been updated. + + goto CheckNSLoop; // See if OK or we have another saved frame + } + + // BAD FRAME, SEND REJ (AFTER RESPTIME - OR WE MAY SEND LOTS!) + + // ALSO SAVE THE FRAME - NEXT TIME WE MAY GET A DIFFERENT SUBSET + // AND SOON WE WILL HANDLE SREJ + + PORT->L2OUTOFSEQ++; + + LINK->L2STATE = 6; + + // IF RUNNING VER1, AND OTHER END MISSES THIS REJ, LINK WILL FAIL + // SO TIME OUT REJ SENT STATE (MUST KEEP IT FOR A WHILE TO AVOID + // 'MULTIPLE REJ' PROBLEM) + + if (LINK->VER1FLAG == 1) + LINK->REJTIMER = TENSECS; + + // SET ACK REQUIRED TIMER - REJ WILL BE SENT WHEN IT EXPIRES + + // if configured RESPTIME is longer than 3 secs use it (may be longer on HF) + + if (PORT->PORTT2 > THREESECS) + LINK->L2ACKREQ = PORT->PORTT2; + else + LINK->L2ACKREQ = THREESECS; // EXTRA LONG RESPTIME, AS SENDING TOO MANY REJ'S IS SERIOUS + + if (LINK->RXFRAMES[NS]) + { + // Already have a copy, so discard old and keep this + + Debugprintf ("Frame %d out of seq but already have copy - release it", NS); + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); + } + else + { + Debugprintf ("Frame %d out of seq - save", NS); + } + + Buffer->CHAIN = 0; + LINK->RXFRAMES[NS] = Buffer; + goto CheckPF; + } + + // IN SEQUENCE FRAME + + // Remove any stored frame with this seq + + if (LINK->RXFRAMES[NS]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); + + if (LINK->L2STATE == 6) // REJ? + { + // If using REJ we can cancel REJ state. + // If using SREJ we only cancel REJ if we have no stored frames + + if (LINK->Ver2point2) + { + // see if any frames saved. + + int i; + + for (i = 0; i < 8; i++) + { + if (LINK->RXFRAMES[i]) + goto stayinREJ; + } + // Drop through if no stored frames + } + + // CANCEL REJ + + LINK->L2STATE = 5; + LINK->L2FLAGS &= ~REJSENT; + } + +stayinREJ: + + PROC_I_FRAME(LINK, PORT, Buffer); // Passes on or releases Buffer + + +CheckPF: + + if (LINK->Ver2point2 == 0) // Unless using SREJ + { + if (LINK->L2FLAGS & REJSENT) + { + return; // DONT SEND ANOTHER TILL REJ IS CANCELLED + } + } + + if (CTL & PFBIT) + { + if (LINK->L2STATE == 6) + LINK->L2FLAGS |= REJSENT; // Set "REJ Sent" + else + { + // we have all frames. Clear anything in RXFRAMES + + int n = 0; + + while (n < 8) + { + if (LINK->RXFRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); + + n++; + } + } + LINK->L2ACKREQ = 0; // CANCEL RR NEEDED + + SEND_RR_RESP(LINK, PFBIT); + + // RECORD TIME + + LINK->LAST_F_TIME = REALTIMETICKS; + } +} + + +VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + int Length; + char * Info; + UCHAR PID; + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)Buffer; + UCHAR * EOA; + int n = 8; // Max Digis + + LINK->LINKNR++; // INCREMENT OUR N(R) + LINK->LINKNR &= 7; // MODULO 8 + + // ATTACH I FRAMES TO LINK TABLE RX QUEUE - ONLY DATA IS ADDED (NOT ADDRESSES) + + // IF DISC PENDING SET, IGNORE FRAME + + if (LINK->L2FLAGS & DISCPENDING) + { + ReleaseBuffer(Buffer); + return; + } + + // Copy data down the buffer so PID comes after Header (DATAMESSAGE format) + + Length = Buffer->LENGTH - (MSGHDDRLEN + 15); // Buffer Header + addrs + CTL + Info = &Buffer->PID; + + // Adjust for DIGIS + + EOA = &Buffer->ORIGIN[6]; // End of address Bit + + while (((*EOA & 1) == 0) && n--) + { + Length -= 7; + Info += 7; + EOA += 7; + } + + PID = EOA[2]; + + switch(PID) + { + case 0xcc: + case 0xcd: + + // IP Message + + if (n < 8) // If digis, move data back down buffer + { + memmove(&Buffer->PID, &EOA[2], Length); + Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); + } + + Q_IP_MSG( Buffer); + break; + + case 8: + + // NOS FRAGMENTED IP + + if (n < 8) // If digis, move data back down buffer + { + memmove(&Buffer->PID, &EOA[2], Length); + Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); + } + + C_Q_ADD(&LINK->L2FRAG_Q, Buffer); + + if (Buffer->L2DATA[0] == 0) + { + // THERE IS A WHOLE MESSAGE ON FRAG_Q - PASS TO IP + + while(LINK->L2FRAG_Q) + { + Buffer = Q_REM(&LINK->L2FRAG_Q); + Q_IP_MSG( Buffer); + } + } + break; + + default: + + if (Length < 1 || Length > 257) + { + ReleaseBuffer(Buffer); + return; + } + + // Copy Data back over + + memmove(&Msg->PID, Info, Length); + + Buffer->LENGTH = Length + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Buffer); + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER +} + +//*** CHECK RECEIVED N(R) COUNT + +VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL) +{ + UCHAR NR = (CTL >> 5) & 7; + + if (NR >= LINK->LINKWS) // N(R) >= WINDOW START? + { + // N(R) ABOVE OR EQUAL TO WINDOW START - OK IF NOT ABOVE N(S), OR N(S) BELOW WS + + if (NR > LINK->LINKNS) // N(R) <= WINDOW END? + { + // N(R) ABOVE N(S) - DOES COUNT WRAP? + + if (LINK->LINKNS >= LINK->LINKWS) // Doesnt wrap + goto BadNR; + } + +GoodNR: + + if ((CTL & 0x0f) == SREJ) + if ((CTL & PFBIT) == 0) + return; // SREJ without F doesn't ACK anything + + LINK->LINKWS = NR; // NEW WINDOW START = RECEIVED N(R) + ACKMSG(LINK); // Remove any acked messages + return; + } + + // N(R) LESS THAN WINDOW START - ONLY OK IF WINDOW WRAPS + + if (NR <= LINK->LINKNS) // N(R) <= WINDOW END? + goto GoodNR; + +BadNR: + + // RECEIVED N(R) IS INVALID + + LINK->SDREJF |= SDNRER; // FLAG A REJECT CONDITION + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE +} + +VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS) +{ + int Resent = (LINK->LINKNS - NS) & 7; // FRAMES TO RESEND + + LINK->LINKNS = NS; // RESET N(S) + + if (LINK->LINKTYPE == 3) // mode-Node + { + if (LINK->NEIGHBOUR) + LINK->NEIGHBOUR->NBOUR_RETRIES += Resent; + } +} + +int COUNT_AT_L2(struct _LINKTABLE * LINK) +{ + // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN BX) + + int count = 0, abovelink = 0; + int n = 0; + + if (LINK == NULL) + return 0; + + abovelink = C_Q_COUNT((UINT *)&LINK->TX_Q); + + // COUNT FRAMES IN TSLOTS + + while (n < 8) + { + if (LINK->FRAMES[n]) + count++; + n++; + } + +// ADD AL,AH ; TOTAL IN AL, NUMBER ABOVE LINK IN AH + + return abovelink + count; +} + +//*** RESET HDLC AND PURGE ALL QUEUES ETC. + +VOID RESET2X(struct _LINKTABLE * LINK) +{ + LINK->SDREJF = 0; // CLEAR FRAME REJECT FLAGS + LINK->LINKWS = 0; // CLEAR WINDOW POINTERS + LINK->LINKOWS = 0; + LINK->LINKNR = 0; // CLEAR N(R) + LINK->LINKNS = 0; // CLEAR N(S) + LINK->SDTSLOT= 0; + LINK->L2STATE = 5; // RESET STATE + LINK->L2FLAGS = 0; +} + + +VOID CLEARL2QUEUES(struct _LINKTABLE * LINK) +{ + // GET RID OF ALL FRAMES THAT ARE QUEUED + + int n = 0; + + while (n < 8) + { + while (LINK->FRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->FRAMES[n])); + while (LINK->RXFRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); + n++; + } + + // GET RID OF ALL FRAMES THAT ARE + // QUEUED ON THE TX HOLDING QUEUE, RX QUEUE AND LEVEL 3 QUEUE + + + while (LINK->TX_Q) + ReleaseBuffer(Q_REM(&LINK->TX_Q)); + + while (LINK->RX_Q) + ReleaseBuffer(Q_REM(&LINK->RX_Q)); + +} + +VOID RESET2(struct _LINKTABLE * LINK) +{ + CLEARL2QUEUES(LINK); + RESET2X(LINK); +} + +VOID SENDSABM(struct _LINKTABLE * LINK) +{ + L2SENDCOMMAND(LINK, SABM | PFBIT); +} + + +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // TIME STAMP IT + + time(&Buffer->Timestamp); + + if (PORT->TXPORT) + { + Buffer->PORT = PORT->TXPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->TXPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); +} + + +UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg) +{ + // COPY ADDRESSES FROM LINK TABLE TO MESSAGE _BUFFER + + UCHAR * ptr1 = &LINK->DIGIS[0]; + UCHAR * ptr2 = &Msg->CTL; + int Digis = 8; + + memcpy(&Msg->DEST[0], &LINK->LINKCALL[0], 14); // COPY DEST AND ORIGIN + + Msg->DEST[6] |= 0x60; + Msg->ORIGIN[6] |= 0x60; + + while (Digis) + { + if (*(ptr1)) // any more to copy? + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + Digis--; + } + else + break; + } + + *(ptr2 - 1) |= 1; // SET END OF ADDRESSES + + return ptr2; // Pointer to CTL +} + +VOID SDETX(struct _LINKTABLE * LINK) +{ + // Start sending frsmes if possible + + struct PORTCONTROL * PORT; + int Outstanding; + UCHAR * ptr1, * ptr2; + UCHAR CTL; + int count; + MESSAGE * Msg; + MESSAGE * Buffer; + + // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS + +// if (LINK->L2RESEQ_Q) +// return; + + if (LINK->LINKPORT->PORTNUMBER == 19) + { + int i = 0; + } + + Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS + + if (Outstanding < 0) + Outstanding += 8; // allow for wrap + + if (Outstanding >= LINK->LINKWINDOW) // LIMIT + return; + + // See if we can load any more frames into the frame holding q + + while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL) + { + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + } + + // dont send while poll outstanding + + while ((LINK->L2FLAGS & POLLSENT) == 0) + { + Msg = LINK->FRAMES[LINK->LINKNS]; // is next frame available? + + if (Msg == NULL) + return; + + // send the frame + + // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr2 NOW POINTS TO COMMAND BYTE + + // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER + + CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS + CTL |= LINK->LINKNS << 1; // BITS 1-3 OF CONTROL BYTE + + LINK->LINKNS++; // INCREMENT NS + LINK->LINKNS &= 7; // mod 8 + + // SET P BIT IF END OF WINDOW OR NO MORE TO SEND + + if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 + { + Outstanding = LINK->LINKNS - LINK->LINKOWS; + + if (Outstanding < 0) + Outstanding += 8; // allow for wrap + + // if at limit, or no more to send, set P) + + if (Outstanding >= LINK->LINKWINDOW || LINK->FRAMES[LINK->LINKNS] == NULL) + { + CTL |= PFBIT; + LINK->L2FLAGS |= POLLSENT; + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) + + Buffer->Linkptr = LINK; + } + } + + *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) + + count = Msg->LENGTH - MSGHDDRLEN; + + if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY + { + ptr1 = (UCHAR *)Msg; + ptr1 += MSGHDDRLEN; + memcpy(ptr2, ptr1, count); + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } + + } +} + +VOID L2TimerProc() +{ + int i = MAXLINKS; + struct _LINKTABLE * LINK = LINKS; + struct PORTCONTROL * PORT = PORTTABLE; + + while (i--) + { + if (LINK->LINKCALL[0] == 0) + { + LINK++; + continue; + } + + // CHECK FOR TIMER EXPIRY OR BUSY CLEARED + + PORT = LINK->LINKPORT; + + if (PORT == NULL) + { + LINK++; + continue; // just ion case!! + } + + if (LINK->L2TIMER) + { + LINK->L2TIMER--; + if (LINK->L2TIMER == 0) + { + L2TIMEOUT(LINK, PORT); + LINK++; + continue; + } + } + else + { + // TIMER NOT RUNNING - MAKE SURE STATE NOT BELOW 5 - IF + // IT IS, SOMETHING HAS GONE WRONG, AND LINK WILL HANG FOREVER + + if (LINK->L2STATE < 5 && LINK->L2STATE != 2 && LINK->L2STATE != 1) // 2 = CONNECT - PROBABLY TO CQ + LINK->L2TIMER = 2; // ARBITRARY VALUE + } + + // TEST FOR RNR SENT, AND NOT STILL BUSY + + if (LINK->L2FLAGS & RNRSENT) + { + // Was busy + + if (RR_OR_RNR(LINK) != RNR) // SEE IF STILL BUSY + { + // Not still busy - tell other end + + // Just sending RR will hause a hang of RR is missed, and other end does not poll on Busy + // Try sending RR CP, so we will retry if not acked + + LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER + + if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILl REPORT ANYWAY + { + SendSupervisCmd(LINK); + LINK++; + continue; + } + } + } + else + { + // NOT BUSY + + if (LINK->L2ACKREQ) // DELAYED ACK TIMER + { + if (LINK->L2RETRIES == 0) // DONT SEND RR RESPONSE WHILEST RR(P) OUTSTANDING + { + LINK->L2ACKREQ--; + if (LINK->L2ACKREQ == 0) + { + SEND_RR_RESP(LINK, 0); // NO F BIT + LINK++; + continue; + } + } + } + } + + // CHECK FOR REJ TIMEOUT + + if (LINK->REJTIMER) + { + LINK->REJTIMER--; + if (LINK->REJTIMER == 0) // {REJ HAS TIMED OUT (THIS MUST BE A VERSION 1 SESSION) + { + // CANCEL REJ STATE + + if (LINK->L2STATE == 6) // REJ? + LINK->L2STATE = 5; // CLEAR REJ + } + } + + // See if time for link validation poll + + if (LINK->L2SLOTIM) + { + LINK->L2SLOTIM--; + if (LINK->L2SLOTIM == 0) // Time to poll + { + SendSupervisCmd(LINK); + LINK++; + continue; + } + } + + // See if idle too long + + LINK->KILLTIMER++; + + if (L2KILLTIME && LINK->KILLTIMER > L2KILLTIME) + { + // CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN + + LINK->KILLTIMER = 0; + LINK->L2TIMER = 1; // TO FORCE DISC + LINK->L2STATE = 4; // DISCONNECTING + + // TELL OTHER LEVELS + + InformPartner(LINK, NORMALCLOSE); + } + LINK++; + } +} + +VOID SendSupervisCmd(struct _LINKTABLE * LINK) +{ + // Send Super Command RR/RNR/REJ(P) + + UCHAR CTL; + + if (LINK->VER1FLAG == 1) + { + // VERSION 1 TIMEOUT + + // RESET TO RESEND I FRAMES + + LINK->LINKNS = LINK->LINKOWS; + + SDETX(LINK); // PREVENT FRMR (I HOPE) + } + + // SEND RR COMMAND - EITHER AS LINK VALIDATION POLL OR FOLLOWING TIMEOUT + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + + CTL = RR_OR_RNR(LINK); + +// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PFBIT; + + LINK->L2FLAGS |= POLLSENT; + + L2SENDCOMMAND(LINK, CTL); + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY +} + +void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF) +{ + UCHAR CTL; + + CTL = RR_OR_RNR(LINK); + +// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PF; + + L2SENDRESPONSE(LINK, CTL); + + ACKMSG(LINK); // SEE IF STILL WAITING FOR ACK +} + +VOID ACKMSG(struct _LINKTABLE * LINK) +{ + // RELEASE ANY ACKNOWLEDGED FRAMES + + while (LINK->LINKOWS != LINK->LINKWS) // is OLD WINDOW START EQUAL TO NEW WINDOW START? + { + // No, so frames to ack + + if (LINK->FRAMES[LINK->LINKOWS]) + ReleaseBuffer(Q_REM(&LINK->FRAMES[LINK->LINKOWS])); + else + { + char Call1[12], Call2[12]; + + Call1[ConvFromAX25(LINK->LINKCALL, Call1)] = 0; + Call2[ConvFromAX25(LINK->OURCALL, Call2)] = 0; + + Debugprintf("Missing frame to ack Seq %d Calls %s %s", LINK->LINKOWS, Call1, Call2); + } + + LINK->LINKOWS++; // INCREMENT OLD WINDOW START + LINK->LINKOWS &= 7; // MODULO 8 + + // SOMETHING HAS BEEN ACKED - RESET RETRY COUNTER + + if (LINK->L2RETRIES) + LINK->L2RETRIES = 1; // MUSTN'T SET TO ZERO - COULD CAUSE PREMATURE RETRANSMIT + + } + + if (LINK->LINKWS != LINK->LINKNS) // IS N(S) = NEW WINDOW START? + { + // NOT ALL I-FRAMES HAVE BEEN ACK'ED - RESTART TIMER + + LINK->L2TIMER = LINK->L2TIME; + return; + } + + // ALL FRAMES HAVE BEEN ACKED - CANCEL TIMER UNLESS RETRYING + // IF RETRYING, MUST ONLY CANCEL WHEN RR(F) RECEIVED + + if (LINK->VER1FLAG == 1 || LINK->L2RETRIES == 0) // STOP TIMER IF LEVEL 1 or not retrying + { + LINK->L2TIMER = 0; + LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) SET (IN CASE TALKING TO OLD BPQ!) + } + + // IF DISCONNECT REQUEST OUTSTANDING, AND NO FRAMES ON TX QUEUE, SEND DISC + + if (LINK->L2FLAGS & DISCPENDING && LINK->TX_Q == 0) + { + LINK->L2FLAGS &= ~DISCPENDING; + + LINK->L2TIMER = 1; // USE TIMER TO SEND DISC + LINK->L2STATE = 4; // DISCONNECTING + } +} + +VOID CONNECTFAILED(); + +VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) +{ + // TIMER EXPIRED + + // IF LINK UP (STATE 5 OR ABOVE) SEND RR/RNR AS REQUIRED + // IF S2, REPEAT SABM + // IF S3, REPEAT FRMR + // IF S4, REPEAT DISC + + + PORT->L2TIMEOUTS++; // FOR STATS + + if (LINK->L2STATE == 0) + return; + + if (LINK->L2STATE == 1) + { + // XID + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - Give up + + CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED + CLEAROUTLINK(LINK); + return; + } + + L2SENDXID(LINK); + return; + } + + + if (LINK->L2STATE == 2) + { + // CONNECTING + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - Give up + + CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + SENDSABM(LINK); + return; + } + + if (LINK->L2STATE == 4) + { + // DISCONNECTING + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - JUST CLEAR OUT LINK + + CLEAROUTLINK(LINK); + return; + } + + L2SENDCOMMAND(LINK, DISC | PFBIT); + return; + } + + if (LINK->L2STATE == 3) + { + // FRMR + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - RESET LINK + + LINK->L2RETRIES = 0; + LINK->L2STATE = 2; + SENDSABM(LINK); + return; + } + } + + // STATE 5 OR ABOVE + + // SEND RR(P) UP TO N2 TIMES + + LINK->L2RETRIES++; + + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N TIMES SEND A COUPLE OF DISCS AND THEN CLOSE + + InformPartner(LINK, RETRIEDOUT); // TELL OTHER END ITS GONE + + LINK->L2RETRIES -= 1; // Just send one DISC + LINK->L2STATE = 4; // CLOSING + + L2SENDCOMMAND(LINK, DISC | PFBIT); + return; + } + + SendSupervisCmd(LINK); +} + +VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) +{ + PORT->L2FRMRTX++; + + LINK->L2STATE = 3; // ENTER FRMR STATE + + LINK->L2TIMER = LINK->L2TIME; //SET TIMER + + SENDFRMR(LINK); +} + +VOID SENDFRMR(struct _LINKTABLE * LINK) +{ + // RESEND FRMR + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + UCHAR * ptr; + + Buffer = SETUPL2MESSAGE(LINK, FRMR); + + if (Buffer == NULL) + return; + + Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE + + ptr = &Buffer->PID; + + *(ptr++) = LINK->SDRBYTE; // MOVE REJECT C-BYTE + + *(ptr++) = LINK->LINKNR << 5 | LINK->LINKNS << 1; + + *(ptr++) = LINK->SDREJF; // MOVE REJECT FLAGS + + Buffer->LENGTH += 3; + + PORT = LINK->LINKPORT; + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + + return; +} + +VOID CLEAROUTLINK(struct _LINKTABLE * LINK) +{ + CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS + + memset(LINK, 0, sizeof(struct _LINKTABLE)); +} + +VOID L2SENDXID(struct _LINKTABLE * LINK) +{ + // Set up and send XID + + struct PORTCONTROL * PORT; + UCHAR * ptr; + unsigned int xidval; + MESSAGE * Buffer; + + if (LINK->LINKPORT == 0) + return; //??? has been zapped + + Buffer = SETUPL2MESSAGE(LINK, XID | PFBIT); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + LINK->L2TIMER = 10*3; // SET TIMER + return; + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + ptr = &Buffer->PID; + + // Set up default XID Mod 8 + + *ptr++ = 0x82; // FI + *ptr++ = 0x80; // GI + *ptr++ = 0x0; + *ptr++ = 0x10; // Length 16 + + *ptr++ = 0x02; // Classes of Procedures + *ptr++ = 0x02; // Length + *ptr++ = 0x00; // + *ptr++ = 0x21; // ABM Half Duplex + + // We offer REJ, SREJ and SREJ Multiframe + + *ptr++ = 0x03; // Optional Functions + *ptr++ = 0x03; // Len + + // Sync TX, SREJ Multiframe 16 bit FCS, Mod 8, TEST, + // Extended Addressing, REJ, SREJ + + xidval = OPMustHave | OPSREJ | OPSREJMult | OPREJ | OPMod8; + *ptr++ = xidval >> 16; + *ptr++ = xidval >> 8; + *ptr++ = xidval; + + + *ptr++ = 0x06; // RX Packet Len + *ptr++ = 0x02; // Len + *ptr++ = 0x08; // + *ptr++ = 0x00; // 2K bits (256) Bytes + + *ptr++ = 0x08; // RX Window + *ptr++ = 0x01; // Len + *ptr++ = 0x07; // 7 + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND + + Buffer->Linkptr = LINK; + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } +} + + + + + + +VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD) +{ + // SEND COMMAND IN CMD + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + + if (LINK->LINKPORT == 0) + return; //??? has been zapped + + Buffer = SETUPL2MESSAGE(LINK, CMD); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + if (CMD & PFBIT) // RESPONSE EXPECTED? + LINK->L2TIMER = 10*3; // SET TIMER + + return; + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + if (CMD & PFBIT) // RESPONSE EXPECTED? + { + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND + + Buffer->Linkptr = LINK; + } + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } +} + + + + + + +VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD) +{ + // SEND Response IN CMD + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + + Buffer = SETUPL2MESSAGE(LINK, CMD); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + if (CMD & PFBIT) // RESPONSE EXPECTED? + LINK->L2TIMER = 10*3; // SET TIMER + + return; + } + + Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + + PORT = LINK->LINKPORT; + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + +} + + +MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD) +{ + MESSAGE * Buffer; + UCHAR * ptr; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return NULL; + + ptr = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr NOW POINTS TO COMMAND BYTE + + *(ptr)++ = CMD; + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH + + return Buffer; +} + + +VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason); + +VOID InformPartner(struct _LINKTABLE * LINK, int Reason) +{ + // LINK IS DISCONNECTING - IF THERE IS A CROSSLINK, SEND DISC TO IT + + if (LINK->LINKTYPE == 3) + { + L3LINKCLOSED(LINK, Reason); + return; + } + + if (LINK->CIRCUITPOINTER) + CloseSessionPartner(LINK->CIRCUITPOINTER); + +} + + +UINT RR_OR_RNR(struct _LINKTABLE * LINK) +{ + UCHAR Temp; + TRANSPORTENTRY * Session; + + LINK->L2FLAGS &= ~RNRSENT; + + // SET UP APPROPRIATE SUPER COMMAND + + if (LINK->LINKTYPE == 3) + + // Node to Node - only busy if short of buffers + + goto CHKBUFFS; + +// UP OR DOWN LINK - SEE IF SESSION IS BUSY + + if (LINK->CIRCUITPOINTER == 0) + goto CHKBUFFS; // NOT CONNECTED + + Session = LINK->CIRCUITPOINTER; // TO CIRCUIT ENTRY + + Temp = CHECKIFBUSYL2(Session); //TARGET SESSION BUSY? + + if (Temp & L4BUSY) + goto SENDRNR; // BUSY + +CHKBUFFS: + + if (QCOUNT < 20) + goto SENDRNR; // NOT ENOUGH + + // SEND REJ IF IN REJ STATE + + if (LINK->L2STATE == 6) + { + + // We may have the needed frame in RXFRAMES + +CheckNSLoop2: + + if (LINK->RXFRAMES[LINK->LINKNR]) + { + // We have the first missing frame. Process it. + + struct PORTCONTROL * PORT = LINK->LINKPORT; + MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); + + Debugprintf("L2 about to send REJ - process saved Frame %d", LINK->LINKNR); + PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer + + // NR has been updated. + + // Clear REJ if we have no more saved + + if (LINK->Ver2point2) // Using SREJ? + { + // see if any frames saved. + + int i; + + for (i = 0; i < 8; i++) + { + if (LINK->RXFRAMES[i]) + goto stayinREJ2; + } + // Drop through if no stored frames + } + + LINK->L2STATE = 5; + LINK->L2FLAGS &= ~REJSENT; +stayinREJ2: + LINK->L2ACKREQ = 0; // Cancel Resptime (Set by PROC_I_FRAME) + + goto CheckNSLoop2; // See if OK or we have another saved frame + } + if (LINK->L2STATE == 6) + + // if we support SREJ send that instesd or REJ + + if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi + return SREJ; + else + return REJ; + } + return RR; + +SENDRNR: + + LINK->L2FLAGS |= RNRSENT; // REMEMBER + + return RNR; +} + + +VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg); + +VOID CONNECTFAILED(struct _LINKTABLE * LINK) +{ + ConnectFailedOrRefused(LINK, "Failure with"); +} +VOID CONNECTREFUSED(struct _LINKTABLE * LINK) +{ + ConnectFailedOrRefused(LINK, "Busy from"); +} + +VOID L3CONNECTFAILED(); + +VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) +{ + // IF DOWNLINK, TELL PARTNER + // IF CROSSLINK, TELL ROUTE CONTROL + + struct DATAMESSAGE * Buffer; + UCHAR * ptr1; + char Normcall[10]; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * InSession; + + if (LINK->LINKTYPE == 3) + { + L3CONNECTFAILED(LINK); // REPORT TO LEVEL 3 + return; + } + + if (LINK->CIRCUITPOINTER == 0) // No crosslink?? + return; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr1 = SetupNodeHeader(Buffer); + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr1 += sprintf(ptr1, "%s %s\r", Msg, Normcall); + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY + InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION + + CLEARSESSIONENTRY(Session); + + if (InSession) + { + InSession->L4CROSSLINK = NULL; // CLEAR REVERSE LINK + C_Q_ADD(&InSession->L4TX_Q, Buffer); + PostDataAvailable(InSession); + } + else + ReleaseBuffer(Buffer); +} + +VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK) +{ + // LINK SETUP COMPLETE + + struct DATAMESSAGE * Buffer; + UCHAR * ptr1; + char Normcall[10]; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * InSession; + + if (LINK->LINKTYPE == 3) + return; + + // UP/DOWN LINK + + if (LINK->CIRCUITPOINTER == 0) // No crosslink?? + return; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr1 = SetupNodeHeader(Buffer); + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr1 += sprintf(ptr1, "Connected to %s\r", Normcall); + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY + Session->L4STATE = 5; + InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION + + if (InSession) + { + C_Q_ADD(&InSession->L4TX_Q, Buffer); + PostDataAvailable(InSession); + } +} + + +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK) +{ + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + LINK->CIRCUITPOINTER = NewSess; // SETUP LINK-CIRCUIT CONNECTION + + memcpy(NewSess->L4USER, LINK->LINKCALL, 7); + memcpy(NewSess->L4MYCALL, MYCALL, 7); // ALWAYS USE _NODE CALL + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->L4TARGET.LINK = LINK; + + NewSess->L4CIRCUITTYPE = L2LINK | UPLINK; + + NewSess->L4STATE = 5; // SET LINK ACTIVE + + NewSess->SESSPACLEN = LINK->LINKPORT->PORTPACLEN; + + + NewSess->SESSIONT1 = L4T1; // Default + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + return NewSess; + } + Index++; + NewSess++; + } + + return NULL; +} + + +VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly) // Digi it (if enabled) +{ + // WE MAY HAVE DISABLED DIGIPEAT ALTOGETHER, (DIGIFLAG=0), + // OR ALLOW ALLOW ONLY UI FRAMES TO BE DIGIED (DIGIFLAG=-1) + + // toPort and UIOnly are used for Cross Port digi feature + + int n; + + if (PORT->DIGIFLAG == 0 && toPort == 0) + { + ReleaseBuffer(Buffer); + return; + } + + OurCall[6] |= 0x80; // SET HAS BEEN REPEATED + + // SEE IF UI FRAME - scan forward for end of address bit + + n = 8; + + while ((OurCall[6] & 1) == 0) + { + OurCall += 7; + + if ((OurCall - &Buffer->CTL) > 56) + { + // Run off end before findin end of address + + ReleaseBuffer(Buffer); + return; + } + } + + if (toPort) // Cross port digi + { + if (((OurCall[7] & ~PFBIT) == 3) || UIOnly == 0) + { + // UI or Digi all + + Buffer->PORT = toPort; // update port no in header + PORT = GetPortTableEntryFromPortNum(toPort); + + if (PORT == NULL) + ReleaseBuffer(Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + return; + } + else + { + ReleaseBuffer(Buffer); + return; + } + } + + if ((OurCall[7] & ~PFBIT) == 3) + { + // UI + + // UI FRAME. IF DIGIMASK IS NON-ZERO, SEND TO ALL PORTS SET, OTHERWISE SEND TO DIGIPORT + + PORT->L2DIGIED++; + + if (toPort) + { + // Cross port digi + + PORT = GetPortTableEntryFromPortNum(toPort); + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + if (PORT == NULL) + ReleaseBuffer(Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + + return; + } + + if (PORT->DIGIMASK == 0) + { + if (PORT->DIGIPORT) // Cross Band Digi? + { + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + DigiToMultiplePorts(PORT, Buffer); + ReleaseBuffer(Buffer); + } + return; + } + + // Not UI - Only Digi if Digiflag not -1 + + if (PORT->DIGIFLAG == -1) + { + ReleaseBuffer(Buffer); + return; + } + + PORT->L2DIGIED++; + + if (PORT->DIGIPORT) // Cross Band Digi? + { + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + PUT_ON_PORT_Q(PORT, Buffer); +} + +BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg) +{ + TRANSPORTENTRY * L4 = L4TABLE; + struct DATAMESSAGE * Buffer; + int i = MAXCIRCUITS; + UCHAR * ptr; + + while (i--) + { + if ((CountBits(L4->LISTEN) == 1) && ((1 << ((Msg->PORT & 0x7f) - 1) && L4->LISTEN))) + { + // See if he is calling our call + + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + memcpy(ourcall, L4->L4USER, 7); + ourcall[6] ^= 0x1e; // Flip SSID + + if (CompareCalls(Msg->DEST, ourcall)) + { + // Get Session Entry for Downlink + + TRANSPORTENTRY * NewSess = L4TABLE; + struct _LINKTABLE * LINK; + char Normcall[10]; + + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + L4->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = L4; + + memcpy(NewSess->L4USER, L4->L4USER, 7); + memcpy(NewSess->L4MYCALL, L4->L4USER, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = L2LINK+UPLINK; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = L4->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(Msg->ORIGIN, ourcall, L4->LISTEN, &LINK); + + if (LINK == NULL) + return FALSE; + + memcpy(LINK->LINKCALL, Msg->ORIGIN, 7); + LINK->LINKCALL[6] &= 0xFE; + memcpy(LINK->OURCALL, ourcall, 7); + + LINK->LINKPORT = PORT; + + LINK->L2TIME = PORT->PORTT1; +/* + // Copy Digis + + n = 7; + ptr = &LINK->DIGIS[0]; + + while (axcalls[n]) + { + memcpy(ptr, &axcalls[n], 7); + n += 7; + ptr += 7; + + LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI + } +*/ + LINK->LINKTYPE = 2; // DOWNLINK + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + LINK->L2STATE = 5; // CONNECTED + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = L4->SESSPACLEN = PORT->PORTPACLEN; + + L2SENDUA(PORT, Msg, Msg); // RESET OF DOWN/CROSSLINK + + L4->LISTEN = FALSE; // Take out of listen mode + + // Tell User + + Buffer = GetBuff(); + + if (Buffer == NULL) + return TRUE; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr = &Buffer->L2DATA[0]; + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr += sprintf(ptr, "Incoming call from %s\r", Normcall); + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); + + C_Q_ADD(&L4->L4TX_Q, Buffer); + PostDataAvailable(L4); + + return TRUE; + + } + Index++; + NewSess++; + } + return FALSE; + } + } + L4++; + } + return FALSE; +} diff --git a/L3Code.c b/L3Code.c new file mode 100644 index 0000000..9278748 --- /dev/null +++ b/L3Code.c @@ -0,0 +1,1470 @@ +/* +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 +*/ + +/* +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 +*/ + +// +// C replacement for L3Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "CHeaders.h" + +VOID UPDATEDESTLIST(); +VOID MOVEALL(dest_list * DEST); +VOID MOVE3TO2(dest_list * DEST); +VOID CLEARTHIRD(dest_list * DEST); +VOID L3TRYNEXTDEST(struct ROUTE * ROUTE); +VOID SendNETROMRoute(struct PORTCONTROL * PORT, unsigned char * axcall); + +extern BOOL NODESINPROGRESS ;; +PPORTCONTROL L3CURRENTPORT; +extern dest_list * CURRENTNODE; + +int L3_10SECS = 10; + + +VOID L3BG() +{ + // TRANSFER MESSAGES FROM DEST TO LINK + + int n = MAXDESTS; + struct DEST_LIST * DEST = DESTS; // NODE LIST + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + + struct _LINKTABLE * LINK; + + while (n--) + { + if (DEST->DEST_CALL[0]) // Active entry? + { + while(DEST->DEST_Q) // FRAMES TO SEND? + { + int ActiveRoute = DEST->DEST_ROUTE; + + if (ActiveRoute) + { + ROUTE = DEST->NRROUTE[ActiveRoute - 1].ROUT_NEIGHBOUR; + if (ROUTE) + LINK = ROUTE->NEIGHBOUR_LINK; + else + LINK= NULL; + + if (LINK) + { + if (LINK->L2STATE == 0) + { + // LINK ENTRY IS INVALID - IT PROBABLY HAS BEEN 'ZAPPED', SO CANCEL IT + + ROUTE->NEIGHBOUR_LINK = NULL; + } + else + { + if (LINK->L2STATE < 5) + { + goto NextDest; // Wait for it to activate + } + else + { + ROUTE->NBOUR_IFRAMES++; + C_Q_ADD(&LINK->TX_Q, Q_REM(&DEST->DEST_Q)); + continue; // See if more + } + } + } + // Drop through to Activate + } + + if (ACTIVATE_DEST(DEST) == FALSE) + { + // Node has no routes - get rid of it + + REMOVENODE(DEST); + return; // Avoid riskof looking at lod entries + } + } + } + +NextDest: + DEST++; + } +} + +BOOL ACTIVATE_DEST(struct DEST_LIST * DEST) +{ + int n = MAXDESTS; + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + struct _LINKTABLE * LINK; + + int ActiveRoute; + + if (DEST->DEST_ROUTE == 0) // ALREADY HAVE A SELECTED ROUTE? + DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + + ActiveRoute = DEST->DEST_ROUTE - 1; + + ROUTE = DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR; + + if (ROUTE == 0) + { + // Currnet Route not present + // If current route is 1, then we must have INP3 routes (or entry is corrupt) + + if (DEST->DEST_ROUTE != 1) + goto NOROUTETODEST; + + // Current Route is 1 + + if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0) + return FALSE; // No INP3 so No Routes + + DEST->DEST_ROUTE = 4; // First INP3 + ROUTE = DEST->ROUTE[0].ROUT_NEIGHBOUR; + } + + LINK = ROUTE->NEIGHBOUR_LINK; + + if (LINK == 0) + { + // Need to Activate Link + + // SET UP LINK TABLE ENTRY + + return L2SETUPCROSSLINK(ROUTE); + } + + // We mst be waiting for link to come up + + return TRUE; + +NOROUTETODEST: + + // CURRENT NEIGHBOUR NOT DEFINED - RESET TO USE FIRST + + if (DEST->DEST_ROUTE == 1) + return FALSE; // First not defined so give up + + DEST->DEST_ROUTE = 0; + return TRUE; +} + +char Call1[10]; +char Call2[10]; +char Call3[10]; + +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT) +{ + // PROCESS A NET/ROM 'NODES' MESSAGE + + // UPDATE _NEIGHBOURS LIST WITH ORIGINATING CALL, AND + // DESTINATION LIST WITH ANY PRACTICAL ROUTES + + + struct DEST_LIST * DEST; + struct ROUTE * ROUTE; + int Portno = PORT->PORTNUMBER; + time_t Stamp; + int HH, MM; + int Msglen = Msg->LENGTH; + int n; + UCHAR * ptr1, * ptr2, * saveptr; + int Qual; + APPLCALLS * APPL; + int App; + + + if (PORT->PORTQUALITY == 0 || PORT->INP3ONLY) + return; + + // SEE IF OUR CALL - DONT WANT TO PUT IT IN LIST! + + if (CompareCalls(Msg->ORIGIN, NETROMCALL)) + return; + + if (CheckExcludeList(Msg->ORIGIN) == 0) + return; + + for (App = 0; App < NumberofAppls; App++) + { + APPL=&APPLCALLTABLE[App]; + + if (APPL->APPLHASALIAS == 0 && CompareCalls(Msg->ORIGIN, APPL->APPLCALL)) + return; + } + + Msg->ORIGIN[6] &= 0x1E; // MASK OFF LAST ADDR BIT +/* + // validate call ptr = &Buffer->ORIGIN[0]; + n = 6; + + while(n--) + { + // Try a bit harder to detect corruption + + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + +*/ + // SEE IF ORIGINATING CALL IS IN NEIGHBOUR LIST - IF NOT ADD IT + + if (FindNeighbour(Msg->ORIGIN, Portno, &ROUTE) == 0) + { + // Not in list + + if (ROUTE == NULL) + return; // Table full + + // CREATE NEIGHBOUR RECORD + + memcpy(ROUTE->NEIGHBOUR_CALL, Msg->ORIGIN, 7); + + ROUTE->NEIGHBOUR_PORT = Portno; + ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY; + + ROUTE->NEIGHBOUR_LINK = 0; // CANT HAVE A LINK IF NEW _NODE + + ROUTE->NoKeepAlive = PORT->PortNoKeepAlive; + } + + // if locked route with quality zero ignore + + if ((ROUTE->NEIGHBOUR_FLAG & 1)) // LOCKED ROUTE + if (ROUTE->NEIGHBOUR_QUAL == 0) + return; + + // If Ignoreunlocked set, ignore it not locked + + if ((ROUTE->NEIGHBOUR_FLAG & 1) == 0) // LOCKED ROUTE + if (PORT->IgnoreUnlocked) + return; + + SendNETROMRoute(PORT, Msg->ORIGIN); + + // if not locked, update route quality from port quality (may have changed config and not cleared SAVENODES + + if ((ROUTE->NEIGHBOUR_FLAG & 1) == 0) // Not LOCKED ROUTE + ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY; + + // GET TIME FROM BIOS DATA AREA OR RTC + + time((time_t *)&Stamp); + + Stamp = Stamp % 86400; // Secs into day + HH = (int)(Stamp / 3600); + + Stamp -= HH * 3600; + MM = (int)(Stamp / 60); + + ROUTE->NEIGHBOUR_TIME = 256 * HH + MM; + + // GET QUALITY + + Qual = ROUTEQUAL = ROUTE->NEIGHBOUR_QUAL; // FOR INITIAL ROUTE TABLE UPDATE + + // CHECK LINK IS IN DEST LIST + + if (FindDestination(Msg->ORIGIN, &DEST) == 0) + { + if (DEST == NULL) + return; // Tsble Full + + // CREATE DESTINATION RECORD + + memset(DEST, 0, sizeof(struct DEST_LIST)); + + memcpy(DEST->DEST_CALL, Msg->ORIGIN, 7); + + NUMBEROFNODES++; + } + + // ALWAYS UPDATE ALIAS IN CASE NOT PRESENT IN ORIGINAL TABLE + + ptr1 = &Msg->L2DATA[1]; + ptr2 = &DEST->DEST_ALIAS[0]; + + if (*ptr1 > ' ') // Only of present + { + // Validate Alias, mainly for DISABL KPC3 Problem + UCHAR c; + + n = 6; + while (n--) + { + c = *(ptr1++); + if (c < 0x20 || c > 0x7A) + c = ' '; + + *(ptr2++) = c; + } + } + else + ptr1 += 6; + + + // UPDATE QUALITY AND OBS COUNT + + PROCROUTES(DEST, ROUTE, Qual); + + Msglen -= (MSGHDDRLEN + 23); // LEVEL 2 HEADER FLAG and NODE MNEMONIC + + // PROCESS DESTINATIONS from message + + // ptr1 = start + + saveptr = ptr1 - 21; + + while (Msglen >= 21) // STILL AT LEAST 1 ENTRY LEFT + { + Msglen -= 21; + saveptr += 21; + ptr1 = saveptr; + + // SEE IF OUR CALL - DONT WANT TO PUT IT IN LIST! + + if (CompareCalls(ptr1, MYCALL)) + { + // But use it to get route quality setting from other end + + // As we now get qual ftom highest, only use this after a reload in + // case other end has changed. + if (ROUTE->FirstTimeFlag == 0) + { + // Check if route is via our node + + if (memcmp(ptr1, &ptr1[13], 7) == 0) + { + if (ROUTE->OtherendLocked == 0) // Dont update locked quality + ROUTE->OtherendsRouteQual = ptr1[20]; + + ROUTE->FirstTimeFlag = 1; // Only do it first time after load + } + } + continue; + } + + if (CheckExcludeList(ptr1) == 0) // Excluded + continue; + + for (n = 0; n < 32; n++) + { + if (CompareCalls(ptr1, APPLCALLTABLE[n].APPLCALL)) + continue; + } + + // MAKE SURE ITS NOT CORRUPTED + + n = 6; + + while (n--) + { + if (*(ptr1++) < 0x40) // Call + { + Call1[ConvFromAX25(Msg->ORIGIN, Call1)] = 0; + Call2[ConvFromAX25(saveptr, Call2)] = 0; + memcpy(Call3, saveptr + 7,6); + Call3[6] = 0; + Debugprintf("Corrupt Node Entry from %s for %s:%s", Call1, Call2, Call3); + goto IgnoreNode; + } + } + ptr1++; // skip ssid + + n = 6; + + while (n--) + { + if (*(ptr1) && ((*(ptr1) < 0x20 || *(ptr1) > 0x7A)) ) // Should we accept zeros or convert to spaces?? + { + Call1[ConvFromAX25(Msg->ORIGIN, Call1)] = 0; + Call2[ConvFromAX25(saveptr, Call2)] = 0; + memcpy(Call3, saveptr + 7,6); + Call3[6] = 0; + Debugprintf("Corrupt Node Entry from %s for %s:%s", Call1, Call2, Call3); + goto IgnoreNode; + } + ptr1++; + } + + ptr1 -= 13; // Back to start + + // CALCULATE ROUTE QUALITY + + // Experimantal Code to adjust received route qualities based on deduced quality + // settings at other end of link. + + // Don't mess with Application Qualities. There are almost always 255, and + // if not there is probably a good reason for the value chosen. + + if (CompareCalls(Msg->ORIGIN, &ptr1[13])) + { + // Application Node - Just do normal update + + Qual = (((ROUTEQUAL * ptr1[20]) + 128)) / 256; + } + else + { + // Try using the highest reported indirect route as remote qual + + if (ROUTE->OtherendLocked == 0) // Not locked + { + if (ptr1[20] > ROUTE->OtherendsRouteQual) + ROUTE->OtherendsRouteQual = ptr1[20]; + } + + // Treat 255 as 254, so 255 routes doen't get included with minquals + // designed to only include applcalls + + if (ptr1[20] == 255) + ptr1[20] = 254; + + Qual = (((ROUTEQUAL * ptr1[20]) + 128)) / 256; + + // I think we should normalize indirect routes twice + // as node not only sends differnet qual but adjusts incoming qual with it + + // or should we ............ + + // We don't touch appl callsigns so I think everything here is an indirect route + + if (ROUTE->OtherendsRouteQual && PORT->NormalizeQuality) + { + Qual = (Qual * ROUTEQUAL) / ROUTE->OtherendsRouteQual; +// not sure about this! Qual = (Qual * ROUTEQUAL) / ROUTE->OtherendsRouteQual; // Twice + + if (Qual > ROUTEQUAL) + Qual = ROUTEQUAL; + } + } + + // SEE IF BELOW MIN QUAL FOR AUTO UPDATES + + if (Qual < MINQUAL) + continue; + + // CHECK LINK IS IN DEST LIST + + if (FindDestination(ptr1, &DEST) == 0) + { + if (DEST == NULL) + continue; + + // CREATE DESTINATION RECORD + + memset(DEST, 0, sizeof(struct DEST_LIST)); + memcpy(DEST->DEST_CALL, ptr1, 7); + NUMBEROFNODES++; + } + + ptr1 += 7; + + // UPDATE ALIAS + + memcpy(DEST->DEST_ALIAS, ptr1, 6); + + ptr1 += 6; + + // NOW POINTING AT BEST NEIGHBOUR - IF THIS IS US, THEN ROUTE IS A LOOP + + if (CompareCalls(ptr1, NETROMCALL)) + Qual = 0; + + // DEST IS NOW IN TABLE - + + // 1. SEE IF THIS ROUTE IS IN TABLE - IF SO UPDATE QUALITY, + // IF NOT, ADD THIS ONE IF IT HAS HIGHER QUALITY THAN EXISTING ONES + + + PROCROUTES(DEST, ROUTE, Qual); + ptr1 += 8; +IgnoreNode:; + } +} + +VOID PROCROUTES(struct DEST_LIST * DEST, struct ROUTE * ROUTE, int Qual) +{ + // ADD NEIGHBOUR ADDRESS IN ROUTE TO NODE's ROUTE TABLE IF BETTER QUALITY THAN THOSE PRESENT + + int Index = 0; + struct NR_DEST_ROUTE_ENTRY Temp; + + if (DEST->DEST_STATE & 0x80) // BBS ENTRY + return; + + for (Index = 0; Index < 4; Index++) + { + if (DEST->NRROUTE[Index].ROUT_NEIGHBOUR == ROUTE) + { + if (Index == 0) + { + // THIS IS A REFRESH OF BEST - IF THIS ISNT ACTIVE ROUTE, MAKE IT ACTIVE + + if (DEST->DEST_ROUTE > 1) // LEAVE IT IF NOT SELECTED OR ALREADY USING BEST + DEST->DEST_ROUTE = 1; + } + + goto UpdatateThisEntry; + } + } + + // NOT IN ANY ROUTE + + Index = 0; + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == 0) + goto UpdatateThisEntry; // SPARE ENTRY, SO USE IT + + if (DEST->NRROUTE[0].ROUT_QUALITY < Qual) + { + // New route is better than best, so move other two down and add here + + DEST->NRROUTE[2] = DEST->NRROUTE[1]; + DEST->NRROUTE[1] = DEST->NRROUTE[0]; + + DEST->DEST_ROUTE = 0; // Se we will switch to new one + + goto UpdatateThisEntry; + } + + Index = 1; + + if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) + goto UpdatateThisEntry; // SPARE ENTRY, SO USE IT + + if (DEST->NRROUTE[1].ROUT_QUALITY < Qual) + { + // New route is better than second, so move down and add here + + DEST->NRROUTE[2] = DEST->NRROUTE[1]; + goto UpdatateThisEntry; + } + + Index = 2; + + if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == 0) + goto UpdatateThisEntry; + + if (DEST->NRROUTE[2].ROUT_QUALITY < Qual) + { + // New route is better than third, so add here + goto UpdatateThisEntry; + } + + // THIS ROUTE IS WORSE THAN ANY OF THE CURRENT 3 - IGNORE IT + + return; + + +UpdatateThisEntry: + + DEST->NRROUTE[Index].ROUT_NEIGHBOUR = ROUTE; + + // I DONT KNOW WHY I DID THIS, BUT IT CAUSES REFLECTED ROUTES + // TO BE SET UP WITH OBS = 0. THIS MAY PREVENT A VALID ALTERNATE + // VIA THE SAME NODE TO FAIL TO BE FOUND. SO I'LL TAKE OUT THE + // TEST AND SEE IF ANYTHING NASTY HAPPENS + // IT DID - THIS IS ALSO CALLED BY CHECKL3TABLES. TRY RESETING + // OBS, BUT NOT QUALITY + + if ((DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) == 0) + DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; // SET OBSOLESCENCE COUNT + + if (Qual) + DEST->NRROUTE[Index].ROUT_QUALITY = Qual; // IF ZERO, SKIP UPDATE + + // IT IS POSSIBLE ROUTES ARE NOW OUT OF ORDER + +SORTROUTES: + + if (DEST->NRROUTE[1].ROUT_QUALITY > DEST->NRROUTE[0].ROUT_QUALITY) + { + // SWAP 1 AND 2 + + Temp = DEST->NRROUTE[0]; + + DEST->NRROUTE[0] = DEST->NRROUTE[1]; + DEST->NRROUTE[1] = Temp; + + DEST->DEST_ROUTE = 0; // FORCE A RE-ASSESSMENT + } + + if (DEST->NRROUTE[2].ROUT_QUALITY > DEST->NRROUTE[1].ROUT_QUALITY) + { + // SWAP 2 AND 3 + + Temp = DEST->NRROUTE[1]; + + DEST->NRROUTE[1] = DEST->NRROUTE[2]; + DEST->NRROUTE[2] = Temp; + + goto SORTROUTES; // 1 AND 2 MAY NOW BE WRONG! + } +} + +int COUNTNODES(struct ROUTE * ROUTE) +{ + // COUNT NODES WITH ROUTE VIA NEIGHBOUR + + int count = 0; + int n = MAXDESTS; + struct DEST_LIST * DEST = DESTS; // NODE LIST + + while (n--) + { + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->ROUTE[0].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->ROUTE[1].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->ROUTE[2].ROUT_NEIGHBOUR == ROUTE) + count++; + + DEST++; + } + return count; +} + +VOID SENDNODE00(); +VOID L3BG(); + + + +VOID SENDNEXTNODESFRAGMENT(); + +VOID SENDNODESMSG() +{ + if (NODESINPROGRESS) + return; + + L3CURRENTPORT = PORTTABLE; + SENDNEXTNODESFRAGMENT(); +} + +int fragmentCount = 0; // Node broadcast packets sent to current port + +VOID SENDNEXTNODESFRAGMENT() +{ + // SEND NEXT FRAGMENT OF A NODES BROADCAST + + // FRAGMENTS ARE SENT AT 10 SECONDS INTERVALS - PARTLY TO REDUCE + // QRM, PARTLY TO REDUCE LOAD ON BUFFERS (AND TX POWER SUPPLIES!) + + // SEND TO PORT IN CURRENTPORT, STARTING AT CURRENTNODE + + struct PORTCONTROL * PORT = L3CURRENTPORT; + dest_list * DEST = CURRENTNODE; + MESSAGE * Buffer; + int Count; + int Qual; + int CURRENTPORTNO, TXMINQUAL; + UCHAR * ptr1, * ptr2; + + if (DEST == 0) + { + // FIRST FRAGMENT TO A PORT + + fragmentCount = 0; + + while (PORT->PORTQUALITY == 0 || PORT->TXPORT || PORT->INP3ONLY) + // Don't send NODES to Shared TX or INP3 Port + { + // No NODES to this port, so go to next + + PORT = PORT->PORTPOINTER; + if (PORT == NULL) + { + // Finished + + NODESINPROGRESS = 0; + return; + } + } + + L3CURRENTPORT = PORT; + + DEST = CURRENTNODE = DESTS; // START OF LIST + NODESINPROGRESS = 1; + } + + CURRENTPORTNO = PORT->PORTNUMBER; + + TXMINQUAL = PORT->PORTMINQUAL; + + if (TXMINQUAL == 0) + TXMINQUAL = 1; // Dont send zero + + Buffer = GetBuff(); + + if (Buffer == 0) + return; + + Buffer->PORT = CURRENTPORTNO; + + memcpy(Buffer->ORIGIN, NETROMCALL, 7); + memcpy(Buffer->DEST, NODECALL, 7); + + Buffer->ORIGIN[6] |= 0x61; // SET CMD END AND RESERVED BITS + + Buffer->CTL = UI; + Buffer->PID = 0xCF; // Netrom + + ptr1 = &Buffer->L2DATA[0]; + + *(ptr1++) = 0xff; // Nodes Flag + + memcpy(ptr1, MYALIASTEXT, 6); + + ptr1+= 6; + + // ADD DESTINATION INFO (UNLESS BBS ONLY) + + // If NODE = 0 just send application nodes + + Count = PORT->NODESPACLEN; + + if (Count == 0) + Count = 256; + + if (Count < 50) // STUPIDLY SMALL? + Count = 50; // EVEN THIS IS RATHER SILLY + + Count -= 22; // Fixed Part + + Count /= 21; // 21 Bytres per entry + + while (Count) + { + if (DEST >= ENDDESTLIST) + { + CURRENTNODE = 0; // Finished on this port + L3CURRENTPORT = PORT->PORTPOINTER; + if (L3CURRENTPORT == NULL) + { + // Finished + + NODESINPROGRESS = 0; + } + goto Sendit; + } + + if (DEST->NRROUTE[0].ROUT_QUALITY >= TXMINQUAL && + DEST->NRROUTE[0].ROUT_OBSCOUNT >= OBSMIN && + (NODE == 1 || DEST->DEST_STATE & 0x80)) // Only send appl nodes if DEST = 0; + { + // Send it + + ptr2 = &DEST->DEST_CALL[0]; + memcpy(ptr1, ptr2, 13); // Dest and Alias + ptr1 += 13; + + ptr2 = (UCHAR *)DEST->NRROUTE[0].ROUT_NEIGHBOUR; + + if (ptr2 == 0) + + // DUMMY POINTER IN BBS ENTRY - PUT IN OUR CALL + + ptr2 = MYCALL; + + memcpy(ptr1, ptr2, 7); // Neighbour Call + ptr1 += 7; + + Qual = 100; + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR && DEST->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_PORT == CURRENTPORTNO) + { + // BEST NEIGHBOUR IS ON CURRENT PORT - REDUCE QUALITY BY QUAL_ADJUST + + Qual -= PORT->QUAL_ADJUST; + } + + Qual *= DEST->NRROUTE[0].ROUT_QUALITY; + Qual /= 100; + + *(ptr1++) = (UCHAR)Qual; + + Count--; + } + DEST++; + } + + CURRENTNODE = DEST; + +Sendit: + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + if (Buffer->LENGTH > 35 || fragmentCount == 0) // Always send first even if no other nodes + { + PUT_ON_PORT_Q(PORT, Buffer); + fragmentCount++; + } + else + ReleaseBuffer(Buffer); + +} + +VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason) +{ + // L2 SESSION HAS SHUT DOWN (PROBABLY DUE TO INACTIVITY) + + struct ROUTE * ROUTE; + + // CLEAR NEIGHBOUR + + ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR + + if (ROUTE) + { + LINK->NEIGHBOUR = NULL; // Clear links + ROUTE->NEIGHBOUR_LINK = NULL; + + CLEARACTIVEROUTE(ROUTE, Reason); // CLEAR ASSOCIATED DEST ENTRIES + } +} + +VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason) +{ + // FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND + // SET INACTIVE + + dest_list * DEST; + int n; + + if (Reason != NORMALCLOSE || ROUTE->INP3Node) + TellINP3LinkGone(ROUTE); + + DEST = DESTS; + n = MAXDESTS; + + DEST--; // So we can increment at start of loop + + while (n--) + { + DEST++; + + if (DEST->DEST_ROUTE == 0) + continue; + + if (DEST->ROUTE[DEST->DEST_ROUTE].ROUT_NEIGHBOUR == ROUTE) // Is this the active route + { + // Yes, so clear + + DEST->DEST_ROUTE = 0; // SET NO ACTIVE ROUTE + } + } +} + + +VOID L3TimerProc() +{ + int i; + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + + struct _LINKTABLE * LINK; + + // CHECK FOR EXCESSIVE BUFFERS QUEUED AT LINK LEVEL + + if (QCOUNT < 100) + { + LINK = LINKS; + i = MAXLINKS; + + while (i--); + { + if (LINK->LINKTYPE == 3) // Only if Internode + { + if (COUNT_AT_L2(LINK) > 50) + { + Debugprintf("Excessive L3 Queue"); + L3LINKCLOSED(LINK, LINKSTUCK); // REPORT TO LEVEL 3 + CLEAROUTLINK(LINK); + } + } + LINK++; + } + } + + STATSTIME++; + + if (IDTIMER) // Not if Disabled + { + IDTIMER--; + + if (IDTIMER == 0) + { + IDTIMER = IDINTERVAL; + SENDIDMSG(); + } + } + + // CHECK FOR BEACON + + if (BTTIMER) // Not if Disabled + { + BTTIMER--; + + if (BTTIMER == 0) + { + BTTIMER = BTINTERVAL; + SENDBTMSG(); + } + } + + // CHECK FOR NODES BROADCAST + + if (L3TIMER) // Not if Disabled + { + L3TIMER--; + + if (L3TIMER == 0) + { + // UPDATE DEST LIST AND SEND 'NODES' MESSAGE + + L3TIMER = L3INTERVAL; + UPDATEDESTLIST(); + SENDNODESMSG(); + } + } + + // TIDY ROUTES + + ROUTE = NEIGHBOURS; + i = MAXNEIGHBOURS; + + ROUTE--; + + while (i--) + { + ROUTE++; + + if (ROUTE->NEIGHBOUR_FLAG & 1) // Locked? + continue; + + if (ROUTE->NEIGHBOUR_LINK) // Has an active Session + continue; + + if (COUNTNODES(ROUTE) == 0) // NODES USING THIS DESTINATION + { + // IF NUMBER USING ROUTE IS ZERO, DELETE IT + + memset(ROUTE, 0, sizeof (struct ROUTE)); + } + } +} + +VOID L3FastTimer() +{ + // CALLED ONCE PER SECOND - USED ONLY TO SEND NEXT PART OF A NODES OR + // ID MESSAGE SEQUENCE + + MESSAGE * Msg; + struct PORTCONTROL * PORT ; + + INP3TIMER(); + + L3_10SECS--; + + if (L3_10SECS == 0) + { + L3_10SECS = 10; + + if (IDMSG_Q) // ID/BEACON TO SEND + { + Msg = Q_REM(&IDMSG_Q); + + PORT = GetPortTableEntryFromPortNum(Msg->PORT); + + if (PORT && PORT->TXPORT == 0) // DONT SEND IF SHARED TX + PUT_ON_PORT_Q(PORT, Msg); + else + ReleaseBuffer(Msg); + } + + if (NODESINPROGRESS) + SENDNEXTNODESFRAGMENT(); + } +} + + +VOID UPDATEDESTLIST() +{ + // DECREMENT OBS COUNTERS ON EACH ROUTE, AND REMOVE 'DEAD' ENTRIES + + dest_list * DEST; + int n; + + DEST = DESTS; + n = MAXDESTS; + + DEST--; // So we can increment at start of loop + + while (n--) + { + DEST++; + + if (DEST->DEST_CALL[0] == 0) // SPARE ENTRY + continue; + + if (DEST->DEST_STATE & 0x80) // LOCKED DESTINATION + continue; + +UPDEST000: + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == 0) // NO DESTINATIONS - DELETE ENTRY unless inp3 routes + { + // Any INP3 Routes? + + if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0) + { + // NO ROUTES LEFT TO DEST - REMOVE IT + + REMOVENODE(DEST); // Unchain, Clear queue and zap + } + continue; + } + + if (DEST->NRROUTE[0].ROUT_OBSCOUNT == 0) + { + // FAILED IN USE - DELETE + + MOVEALL(DEST); + goto UPDEST000; //LOOP BACK TO PROCESS MOVED ENTRIES + } + + if ((DEST->NRROUTE[0].ROUT_OBSCOUNT & 0x80) == 0) // Locked? + { + DEST->NRROUTE[0].ROUT_OBSCOUNT--; + + if (DEST->NRROUTE[0].ROUT_OBSCOUNT == 0) // Timed out + { + MOVEALL(DEST); + goto UPDEST000; //LOOP BACK TO PROCESS MOVED ENTRIES + } + } + + // Process Next Neighbour + +UPDEST010: + + if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) + continue; // NO MORE DESTINATIONS + + if (DEST->NRROUTE[1].ROUT_OBSCOUNT == 0) + { + // FAILED IN USE - DELETE + + MOVE3TO2(DEST); + goto UPDEST010; //LOOP BACK TO PROCESS MOVED ENTRIES + } + + if ((DEST->NRROUTE[1].ROUT_OBSCOUNT & 0x80) == 0) // Locked? + { + DEST->NRROUTE[1].ROUT_OBSCOUNT--; + + if (DEST->NRROUTE[1].ROUT_OBSCOUNT == 0) // Timed out + { + MOVE3TO2(DEST); + goto UPDEST010; //LOOP BACK TO PROCESS MOVED ENTRIES + } + } + + // Process Next Neighbour + + if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == 0) + continue; // NO MORE DESTINATIONS + + if (DEST->NRROUTE[2].ROUT_OBSCOUNT == 0) + { + // FAILED IN USE - DELETE + + CLEARTHIRD(DEST); + continue; + } + + if ((DEST->NRROUTE[2].ROUT_OBSCOUNT & 0x80) == 0) // Locked? + { + DEST->NRROUTE[2].ROUT_OBSCOUNT--; + + if (DEST->NRROUTE[2].ROUT_OBSCOUNT == 0) // Timed out + { + CLEARTHIRD(DEST); + continue; + } + } + } +} + + +VOID MOVEALL(dest_list * DEST) +{ + DEST->NRROUTE[0] = DEST->NRROUTE[1]; + MOVE3TO2(DEST); +} + +VOID MOVE3TO2(dest_list * DEST) +{ + DEST->NRROUTE[1] = DEST->NRROUTE[2]; + CLEARTHIRD(DEST); +} + +VOID CLEARTHIRD(dest_list * DEST) +{ + memset(&DEST->NRROUTE[2], 0, sizeof (struct NR_DEST_ROUTE_ENTRY)); + DEST->DEST_ROUTE = 0; // CANCEL ACTIVE ROUTE, SO WILL RE-ASSESS BEST +} + +// L4 Flags Values + +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED + +VOID REMOVENODE(dest_list * DEST) +{ + TRANSPORTENTRY * L4 = L4TABLE; + int n = MAXCIRCUITS; + + // Remove a node, either because routes have gone, or APPL API has invalidated it + + while (DEST->DEST_Q) + ReleaseBuffer(Q_REM(&DEST->DEST_Q)); + + // MAY NEED TO CHECK FOR L4 CIRCUITS USING DEST, BUT PROBABLY NOT, + // AS THEY SHOULD HAVE TIMED OUT LONG AGO + + // Not necessarily true with INP3, so had better check + + while (n--) + { + if (L4->L4USER[0]) + { + if (L4->L4TARGET.DEST == DEST) + { + // Session to/from this Dest + + TRANSPORTENTRY * Partner = L4->L4CROSSLINK; + char Nodename[20]; + + Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate + + Debugprintf("Delete Node for %s Called with active L4 Session - State %d", + Nodename, L4->L4STATE); + + if (Partner) + { + // if connnecting, send error message and drop back to command level + + if (L4->L4STATE == 2) + { + struct DATAMESSAGE * Msg = GetBuff(); + Partner->L4CROSSLINK = 0; // Back to command lewel + + if (Msg) + { + UCHAR * ptr1; + + Msg->PID = 0xf0; + ptr1 = SetupNodeHeader(Msg); + ptr1 += sprintf(ptr1, "Error - Node %s has disappeared\r", Nodename); + + Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg); + C_Q_ADD(&Partner->L4TX_Q, Msg); + PostDataAvailable(Partner); + } + } + else + { + // Failed in session - treat as if a L4DREQ received + + CLOSECURRENTSESSION(Partner); + } + } + CLEARSESSIONENTRY(L4); + } + } + L4++; + } + memset(DEST, 0, sizeof(struct DEST_LIST)); + NUMBEROFNODES--; +} + +VOID L3CONNECTFAILED(struct _LINKTABLE * LINK) +{ + // L2 LINK SETUP HAS FAILED - SEE IF ANOTHER NEIGHBOUR CAN BE USED + + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + + + ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR + + if (ROUTE == NULL) + return; // NOTHING ??? + + TellINP3LinkSetupFailed(ROUTE); + + ROUTE->NEIGHBOUR_LINK = 0; // CLEAR IT + + L3TRYNEXTDEST(ROUTE); // RESET ASSOCIATED DEST ENTRIES +} + + +VOID L3TRYNEXTDEST(struct ROUTE * ROUTE) +{ + // FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND + // SET NEXT BEST NEIGHBOUR (IF ANY) ACTIVE + + int n = MAXDESTS; + struct DEST_LIST * DEST = DESTS; // NODE LIST + int ActiveRoute; + + while (n--) + { + ActiveRoute = DEST->DEST_ROUTE; + + if (ActiveRoute) + { + ActiveRoute --; // Routes numbered 1 - 6, idex from 0 + + if (DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR == ROUTE) + { + // We were best + + // NEIGHBOUR HAS FAILED - DECREMENT OBSCOUNT + // AND TRY TO ACTIVATE ANOTHER (IF ANY) + + if (DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT) + { + // IF ALREADY ZERO - WILL BE DELETED BY NEXT NODES UPDATE + + if ((DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT & 0x80) == 0) + { + // not Locked + + DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT--; + + // if ROUTE HAS EXPIRED - WE SHOULD CLEAR IT, AND MOVE OTHERS (IF ANY) UP + } + } + + // REMOVE FIRST MESSAGE FROM DEST_Q. L4 WILL RETRY - IF IT IS LEFT HERE + // WE WILL TRY TO ACTIVATE THE DESTINATION FOR EVER + + if (DEST->DEST_Q) + ReleaseBuffer(Q_REM(&DEST->DEST_Q)); + + DEST->DEST_ROUTE++; // TO NEXT + + if (DEST->DEST_ROUTE = 7) + DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + } + } + DEST++; + } +} + +VOID CHECKNEIGHBOUR(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg) +{ + // MESSAGE RECEIVED ON LINK WITH NO ROUTE - SET ONE UP + + struct ROUTE * ROUTE; + struct PORTCONTROL * PORT = LINK->LINKPORT; + int Portno = PORT->PORTNUMBER; + + if (FindNeighbour(LINK->LINKCALL, Portno, &ROUTE) == 0) + { + if (ROUTE == 0) + goto L3CONN08; // TABLE FULL?? + + // FIRST MAKE SURE WE ARE ALLOWING NETWORK ACTIVITY ON THIS PORT + + if (PORT->PORTQUALITY == 0 && PORT->INP3ONLY == 0) + return; + + ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY; + ROUTE->INP3Node = PORT->INP3ONLY; + + memcpy(ROUTE->NEIGHBOUR_CALL, LINK->LINKCALL, 7); + + ROUTE->NEIGHBOUR_PORT = Portno; + ROUTE->NoKeepAlive = PORT->PortNoKeepAlive; + } + + // SET THIS AS ACTIVE LINK IF NONE PRESENT + + if (ROUTE->NEIGHBOUR_LINK == 0) + { + ROUTE->NEIGHBOUR_LINK = LINK; + ROUTE->NEIGHBOUR_PORT = Portno; + } + +L3CONN08: + + LINK->NEIGHBOUR = ROUTE; // SET LINK - NEIGHBOUR +} + +struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg) +{ + // CHECK THAT FAR NODE IS IN 'NODES'. + // RETURNS POINTER TO NEIGHBOUR ENTRY + + struct ROUTE * ROUTE; + struct DEST_LIST * DEST; + int Qual; + + ROUTE = LINK->NEIGHBOUR; + + if (FindDestination(Msg->L3SRCE, &DEST)) + return DEST; // Ok + + if (DEST == NULL) + return NULL; // Tsble Full + + // ADD DESTINATION VIA NEIGHBOUR, UNLESS ON BLACK LIST + +#ifdef EXCLUDEBITS + + if (CheckExcludeList(Msg->L3SRCE) == 0) + return 0; + +#endif + + memcpy(DEST->DEST_CALL, Msg->L3SRCE, 7); + + NUMBEROFNODES++; + + // MAKE SURE NEIGHBOUR IS DEFINED FOR DESTINATION + + // IF NODE is NEIGHBOUR, THEN CAN USE NEIGHBOUR QUALITY, + // OTHERWISE WE DONT KNOW ROUTE, SO MUST SET QUAL TO 0 + + + Qual = 0; // DONT KNOW ROUTING, SO SET QUALITY TO ZERO + + PROCROUTES(DEST, ROUTE, Qual); // ADD NEIGHBOUR IF NOT PRESENT + + if (DEST->DEST_ROUTE == 0) + { + // MAKE CURRENT NEIGHBOUR ACTIVE + + int n = 0; + + DEST->DEST_ROUTE = 1; + + while (n < 3) + { + if (DEST->NRROUTE[n].ROUT_NEIGHBOUR == ROUTE) + break; + + DEST->DEST_ROUTE++; + n++; + } + + if (DEST->DEST_ROUTE > 3) + { + DEST->DEST_ROUTE = 1; // CURRENT NEIGHBOUR ISNT IN DEST LIST - SET TO USE BEST + return DEST; // Can't update OBS + } + } + + // REFRESH OBS COUNT + + if (DEST->DEST_ROUTE) + { + int Index = DEST->DEST_ROUTE -1; + + if (DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) // Locked: + return DEST; + + DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; + } + return DEST; +} + +VOID REFRESHROUTE(TRANSPORTENTRY * Session) +{ + // RESET OBS COUNT ON CURRENT ROUTE TO DEST FOR SESSION IN [EBX] + // CALLED WHEN INFO ACK RECEIVED, INDICATING ROUTE IS STILL OK + + struct DEST_LIST * DEST; + int Index; + + DEST = Session->L4TARGET.DEST; + + if (DEST == 0) + return; // No Dest ??? + + Index = DEST->DEST_ROUTE; + + if (Index == 0) + return; // NONE ACTIVE??? + + Index--; + + if (DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) + return; // Locked + + DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; +} + + diff --git a/L4Code.c b/L4Code.c new file mode 100644 index 0000000..61c5f3b --- /dev/null +++ b/L4Code.c @@ -0,0 +1,2395 @@ +/* +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 +*/ + +// +// C replacement for L4Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "CHeaders.h" + +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session); +VOID SENDL4DISC(TRANSPORTENTRY * Session); +int C_Q_COUNT(VOID * Q); +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); +VOID InformPartner(struct _LINKTABLE * LINK); +VOID IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer); +VOID SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG); +BOOL FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex); +int GETBUSYBIT(TRANSPORTENTRY * L4); +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions); +VOID SETUPNEWCIRCUIT(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, + TRANSPORTENTRY * L4, char * BPQPARAMS, int ApplMask, int * BPQNODE); +extern char * ALIASPTR; +VOID SendConACK(struct _LINKTABLE * LINK, TRANSPORTENTRY * L4, L3MESSAGEBUFFER * L3MSG, BOOL BPQNODE, UINT Applmask, UCHAR * ApplCall); +VOID L3SWAPADDRESSES(L3MESSAGEBUFFER * L3MSG); +VOID L4TIMEOUT(TRANSPORTENTRY * L4); +struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg); +int CHECKIFBUSYL4(TRANSPORTENTRY * L4); +VOID AUTOTIMER(); +VOID NRRecordRoute(UCHAR * Buff, int Len); +VOID REFRESHROUTE(TRANSPORTENTRY * Session); +VOID ACKFRAMES(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY * L4, int NR); +VOID SENDL4IACK(TRANSPORTENTRY * Session); +VOID CHECKNEIGHBOUR(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg); +VOID ProcessINP3RIF(struct ROUTE * Route, UCHAR * ptr1, int msglen, int Port); +VOID ProcessRTTMsg(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff, int Len, int Port); +VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall); +void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode); + +extern UINT APPLMASK; + +extern BOOL LogL4Connects; +extern BOOL LogAllConnects; + +// L4 Flags Values + +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED + +extern APPLCALLS * APPL; + +VOID NETROMMSG(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG) +{ + // MAKE SURE PID IS 0CF - IN CASE SOMEONE IS SENDING L2 STUFF ON WHAT + // WE THINK IS A _NODE-_NODE LINK + + struct DEST_LIST * DEST; + + int n; + + if (L3MSG->L3PID != 0xCF) + { + ReleaseBuffer(L3MSG); + return; + } + + if (LINK->NEIGHBOUR == 0) + { + // NO ROUTE ASSOCIATED WITH THIS CIRCUIT - SET ONE UP + + CHECKNEIGHBOUR(LINK, L3MSG); + + if (LINK->NEIGHBOUR == 0) + { + // COULDNT SET UP NEIGHBOUR - CAN ONLY THROW IT AWAY + + ReleaseBuffer(L3MSG); + return; + } + } + + // See if a INP3 RIF (first Byte 0xFF) + + if (L3MSG->L3SRCE[0] == 0xff) + { + // INP3 + + ProcessINP3RIF(LINK->NEIGHBOUR, &L3MSG->L3SRCE[1], L3MSG->LENGTH - 9, L3MSG->Port); + ReleaseBuffer(L3MSG); + return; + } + + APPLMASK = 0; // NOT APPLICATION + + if (NODE) // _NODE SUPPORT INCLUDED? + { + + if (CompareCalls(L3MSG->L3DEST, MYCALL)) + { + FRAMEFORUS(LINK, L3MSG, APPLMASK, MYCALL); + return; + } + } + + // CHECK ALL L4 CALLS + + APPLMASK = 1; + ALIASPTR = &CMDALIAS[0][0]; + + n = NumberofAppls; + + APPL = APPLCALLTABLE; + + while (n--) + { + if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr + { + if (CompareCalls(L3MSG->L3DEST, APPL->APPLCALL)) + { + FRAMEFORUS(LINK, L3MSG, APPLMASK, APPL->APPLCALL); + return; + } + } + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + APPL++; + } + + // IS IT INP3 (L3RTT) + + if (CompareCalls(L3MSG->L3DEST, L3RTT)) + { + ProcessRTTMsg(LINK->NEIGHBOUR, L3MSG, L3MSG->LENGTH, L3MSG->Port); + return; + } + + L3MSG->L3TTL--; + + if (L3MSG->L3TTL == 0) + { + ReleaseBuffer(L3MSG); + return; + } + + // If it is a record route frame we should add our call to the list before sending on + + if (L3MSG->L4FLAGS == 0 && L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0) + { + // Add our call on end, and increase count + + int Len = L3MSG->LENGTH; + int Count; + + UCHAR * ptr = (UCHAR *)L3MSG; + + if (Len < 248) + { + ptr += (Len - 1); + + Count = (*(ptr++)) & 0x7F; // Mask End of Route + + memcpy(ptr, MYCALL, 7); + + ptr += 7; + + Count--; + *(ptr) = Count; + + if (Count) + L3MSG->LENGTH += 8; + } + } + + if (L3MSG->L3TTL > L3LIVES) + L3MSG->L3TTL = L3LIVES; // ENFORCE LIMIT ON ALL FRAMES SENT + + if (FindDestination(L3MSG->L3DEST, &DEST) == 0) + { + ReleaseBuffer(L3MSG); // CANT FIND DESTINATION + return; + } + + // IF MESSAGE ORIGINTED HERE, THERE MUST BE A ROUTING LOOP - + // THERE IS LITTLE POINT SENDING IT OVER THE SAME ROUTE AGAIN, + // SO SET ANOTHER ROUTE ACTIVE IF POSSIBLE + + if (CompareCalls(L3MSG->L3SRCE, MYCALL) || CompareCalls(L3MSG->L3SRCE, APPLCALLTABLE->APPLCALL)) + { + // MESSAGE HAS COME BACK TO ITS STARTING POINT - ACTIVATE ANOTHER ROUTE, + // UNLESS THERE IS ONLY ONE, IN WHICH CASE DISCARD IT + + if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) // No more routes + { + ReleaseBuffer(L3MSG); + return; + } + + DEST->DEST_ROUTE++; + + if (DEST->DEST_ROUTE == 4) // TO NEXT + DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + } + + // IF CURRENT ROUTE IS BACK THE WAY WE CAME, THEN ACTIVATE + //ANOTHER (IF POSSIBLE). + + if (DEST->DEST_ROUTE) + { + if (DEST->NRROUTE[DEST->DEST_ROUTE -1].ROUT_NEIGHBOUR == LINK->NEIGHBOUR) + { + // Current ROUTE IS BACK THE WAY WE CAME - ACTIVATE ANOTHER IF POSSIBLE + + DEST->DEST_ROUTE++; + if (DEST->DEST_ROUTE == 4) + DEST->DEST_ROUTE =1; + } + goto NO_PROBLEM; + } + else + { + // DONT HAVE AN ACTIVE ROUTE + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == LINK->NEIGHBOUR) + { + // FIRST ROUTE IS BACK THE WAY WE CAME - ACTIVATE ANOTHER IF POSSIBLE + + DEST->DEST_ROUTE = 2; // WILL BE RESET BY L3 CODE IF THERE IS NOT OTHER ROUTE + } + } + +NO_PROBLEM: + + CHECKL3TABLES(LINK, L3MSG); + +// EVEN IF WE CANT PUT ORIGINATING NODE INTO OUR TABLES, PASS MSG ON +// ANYWAY - THE FINAL TARGET MAY HAVE ANOTHER WAY BACK + + + C_Q_ADD(&DEST->DEST_Q, L3MSG); + + L3FRAMES++; +} + +VOID SENDL4MESSAGE(TRANSPORTENTRY * L4, struct DATAMESSAGE * Msg) +{ + L3MESSAGEBUFFER * L3MSG; + struct DEST_LIST * DEST; + struct DATAMESSAGE * Copy; + int FRAGFLAG = 0; + int Len; + + // These make it simpler to understand code + +#define NullPKTLen 4 + sizeof(void *) // 4 is Port, Len, PID +#define MaxL4Len 236 + 4 + sizeof(void *) // Max NETROM Size + + + if (Msg->LENGTH == NullPKTLen) + { + // NO DATA - DISCARD IT + + ReleaseBuffer(Msg); + return; + } + + L3MSG = GetBuff(); + + if (L3MSG == 0) + { + // DONT THINK WE SHOULD GET HERE, UNLESS _QCOUNT IS CORRUPT, + // BUT IF WE DO, SHOULD RETURN MSG TO FREE Q - START TIMER, AND + // DROP THROUGH TO RELBUFF + + L4->L4TIMER = L4->SESSIONT1; + + ReleaseBuffer(Msg); + return; + } + + L3MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(L3MSG->L3SRCE, L4->L4MYCALL, 7); + + DEST = L4->L4TARGET.DEST; + memcpy(L3MSG->L3DEST, DEST->DEST_CALL, 7); + + L3MSG->L3TTL = L3LIVES; + + L3MSG->L4ID = L4->FARID; + L3MSG->L4INDEX = L4->FARINDEX; + + L3MSG->L4TXNO = L4->TXSEQNO; + + // SET UP RTT TIMER + + if (L4->RTT_TIMER == 0) + { + L4->RTT_SEQ = L4->TXSEQNO; + + L4->RTT_TIMER = GetTickCount(); + } + + L4->TXSEQNO++; + + + L4->L4LASTACKED = L3MSG->L4RXNO = L4->RXSEQNO; // SAVE LAST NUMBER ACKED + + // SEE IF CROSSSESSION IS BUSY + + GETBUSYBIT(L4); // Sets BUSY in NAKBITS if Busy + + L3MSG->L4FLAGS = L4INFO | L4->NAKBITS; + + L4->L4TIMER = L4->SESSIONT1; // SET TIMER + L4->L4ACKREQ = 0; // CANCEL ACK NEEDED + + Len = Msg->LENGTH; + + if (Len > MaxL4Len) // 236 DATA + 8 HEADER + { + // MUST FRAGMENT MESSAGE + + L3MSG->L4FLAGS |= L4MORE; + FRAGFLAG = 1; + + Len = MaxL4Len; + } + + Len += 20; // L3/4 Header + + L3MSG->LENGTH = Len; + + Len -= (20 + NullPKTLen); // Actual Data + + memcpy(L3MSG->L4DATA, Msg->L2DATA, Len); + + // CREATE COPY FOR POSSIBLE RETRY + + Copy = GetBuff(); + + if (Copy == 0) + { + // SHOULD NEVER HAPPEN + + ReleaseBuffer(Msg); + return; + } + + memcpy(Copy, L3MSG, L3MSG->LENGTH); + + // If we have fragmented, we should adjust length, or retry will send too much + // (bug in .asm code) + + if (FRAGFLAG) + Copy->LENGTH = MaxL4Len; + + C_Q_ADD(&L4->L4HOLD_Q, Copy); + + C_Q_ADD(&DEST->DEST_Q, L3MSG); + + DEST->DEST_COUNT++; // COUNT THEM + + L4FRAMESTX++; + + if (FRAGFLAG) + { + // MESSAGE WAS TOO BIG - ADJUST IT AND LOOP BACK + + Msg->LENGTH -= 236; + + memmove(Msg->L2DATA, &Msg->L2DATA[236], Msg->LENGTH - NullPKTLen); + + SENDL4MESSAGE(L4, Msg); + } +} + + +int GETBUSYBIT(TRANSPORTENTRY * L4) +{ + // SEE IF CROSSSESSION IS BUSY + + L4->NAKBITS &= ~L4BUSY; // Clear busy + + L4->NAKBITS |= CHECKIFBUSYL4(L4); // RETURNS AL WITH BUSY BIT SET IF CROSSSESSION IS BUSY + + return L4->NAKBITS; +} + +VOID Q_IP_MSG(PDATAMESSAGE Buffer) +{ + if (IPHOSTVECTOR.HOSTAPPLFLAGS & 0x80) + { + // CHECK WE ARENT USING TOO MANY BUFFERS + + if (C_Q_COUNT(&IPHOSTVECTOR.HOSTTRACEQ) > 20) + ReleaseBuffer(Q_REM((void *)&IPHOSTVECTOR.HOSTTRACEQ)); + + C_Q_ADD(&IPHOSTVECTOR.HOSTTRACEQ, Buffer); + return; + } + + ReleaseBuffer(Buffer); +} + +VOID SENDL4CONNECT(TRANSPORTENTRY * Session) +{ + PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)GetBuff(); + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + if (MSG == NULL) + return; + + if (DEST->DEST_CALL[0] == 0) + { + Debugprintf("Trying to send L4CREEQ to NULL Destination"); + ReleaseBuffer(MSG); + return; + } + + MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(MSG->L3SRCE, Session->L4MYCALL, 7); + memcpy(MSG->L3DEST, DEST->DEST_CALL, 7); + + MSG->L3TTL = L3LIVES; + + MSG->L4INDEX = Session->CIRCUITINDEX; + MSG->L4ID = Session->CIRCUITID; + MSG->L4TXNO = 0; + MSG->L4RXNO = 0; + MSG->L4FLAGS = L4CREQ; + + MSG->L4DATA[0] = L4DEFAULTWINDOW; // PROPOSED WINDOW + + memcpy(&MSG->L4DATA[1], Session->L4USER, 7); // ORIG CALL + memcpy(&MSG->L4DATA[8], Session->L4MYCALL, 7); + + Session->L4TIMER = Session->SESSIONT1; // START TIMER + memcpy(&MSG->L4DATA[15], &Session->SESSIONT1, 2); // AND PUT IN MSG + + MSG->LENGTH = (int)(&MSG->L4DATA[17] - (UCHAR *)MSG); + + if (Session->SPYFLAG) + { + MSG->L4DATA[17] = 'Z'; // ADD SPY ON BBS FLAG + MSG->LENGTH++; + } + C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG); +} + +void RETURNEDTONODE(TRANSPORTENTRY * Session) +{ + // SEND RETURNED TO ALIAS:CALL + + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff(); + char Nodename[20]; + + if (Msg) + { + Msg->PID = 0xf0; + + Nodename[DecodeNodeName(MYCALLWITHALIAS, Nodename)] = 0; // null terminate + + Msg->LENGTH = (USHORT)sprintf(&Msg->L2DATA[0], "Returned to Node %s\r", Nodename) + 4 + sizeof(void *); + C_Q_ADD(&Session->L4TX_Q, (UINT *)Msg); + PostDataAvailable(Session); + } +} + + +extern void * BUFFER; + +VOID L4BG() +{ + // PROCESS DATA QUEUED ON SESSIONS + + int n = MAXCIRCUITS; + TRANSPORTENTRY * L4 = L4TABLE; + int MaxLinks = MAXLINKS; + UCHAR Outstanding; + struct DATAMESSAGE * Msg; + struct PORTCONTROL * PORT; + struct _LINKTABLE * LINK; + int Msglen, Paclen; + + while (n--) + { + if (L4->L4USER[0] == 0) + { + L4++; + continue; + } + while (L4->L4TX_Q) + { + if (L4->L4CIRCUITTYPE & BPQHOST) + break; // Leave on TXQ + + // SEE IF BUSY - NEED DIFFERENT TESTS FOR EACH SESSION TYPE + + if (L4->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION - WILL NEED BUFFERS FOR SAVING COPY, + // AND POSSIBLY FRAGMENTATION + + if (QCOUNT < 15) + break; + + if (L4->FLAGS & L4BUSY) + { + // CHOKED - MAKE SURE TIMER IS RUNNING + + if (L4->L4TIMER == 0) + L4->L4TIMER = L4->SESSIONT1; + + break; + } + + // CHECK WINDOW + + Outstanding = L4->TXSEQNO - L4->L4WS; // LAST FRAME ACKED - GIVES NUMBER OUTSTANING + + // MOD 256, SO SHOULD HANDLE WRAP?? + + if (Outstanding > L4->L4WINDOW) + break; + + } + else if (L4->L4CIRCUITTYPE & L2LINK) + { + // L2 LINK + + LINK = L4->L4TARGET.LINK; + + if (COUNT_AT_L2(LINK) > 8) + break; + } + + // Not busy, so continue + + L4->L4KILLTIMER = 0; //RESET SESSION TIMEOUTS + + if(L4->L4CROSSLINK) + L4->L4CROSSLINK->L4KILLTIMER = 0; + + Msg = Q_REM((void *)&L4->L4TX_Q); + + if (L4->L4CIRCUITTYPE & PACTOR) + { + // PACTOR-like - queue to Port + + // Stream Number is in KAMSESSION + + Msg->PORT = L4->KAMSESSION; + PORT = L4->L4TARGET.PORT; + + C_Q_ADD(&PORT->PORTTX_Q, Msg); + + continue; + } + // non-pactor + + // IF CROSSLINK, QUEUE TO NEIGHBOUR, ELSE QUEUE ON LINK ENTRY + + if (L4->L4CIRCUITTYPE & SESSION) + { + SENDL4MESSAGE(L4, Msg); + ReleaseBuffer(Msg); + break; + } + + LINK = L4->L4TARGET.LINK; + + // If we want to enforce PACLEN this may be a good place to do it + + Msglen = Msg->LENGTH - (MSGHDDRLEN + 1); //Dont include PID + Paclen = L4->SESSPACLEN; + + if (Paclen == 0) + Paclen = 256; + + if (Msglen > Paclen) + { + // Fragment it. + // Is it best to send Paclen packets then short or equal length? + // I think equal length; + + int Fragments = (Msglen + Paclen - 1) / Paclen; + int Fraglen = Msglen / Fragments; + + if ((Msglen & 1)) // Odd + Fraglen ++; + + while (Msglen > Fraglen) + { + PDATAMESSAGE Fragment = GetBuff(); + + if (Fragment == NULL) + break; // Cant do much else + + Fragment->PORT = Msg->PORT; + Fragment->PID = Msg->PID; + Fragment->LENGTH = Fraglen + (MSGHDDRLEN + 1); + + memcpy(Fragment->L2DATA, Msg->L2DATA, Fraglen); + + C_Q_ADD(&LINK->TX_Q, Fragment); + + memcpy(Msg->L2DATA, &Msg->L2DATA[Fraglen], Msglen - Fraglen); + Msglen -= Fraglen; + Msg->LENGTH -= Fraglen; + } + + // Drop through to send last bit + + } + + C_Q_ADD(&LINK->TX_Q, Msg); + } + + // if nothing on TX Queue If there is stuff on hold queue, timer must be running + +// if (L4->L4TX_Q == 0 && L4->L4HOLD_Q) + if (L4->L4HOLD_Q) + { + if (L4->L4TIMER == 0) + { + L4->L4TIMER = L4->SESSIONT1; + } + } + + // now check for rxed frames + + while(L4->L4RX_Q) + { + Msg = Q_REM((void *)&L4->L4RX_Q); + + IFRM150(L4, Msg); + + if (L4->L4USER[0] == 0) // HAVE JUST CLOSED SESSION! + goto NextSess; + } + + // IF ACK IS PENDING, AND WE ARE AT RX WINDOW, SEND ACK NOW + + Outstanding = L4->RXSEQNO - L4->L4LASTACKED; + if (Outstanding >= L4->L4WINDOW) + SENDL4IACK(L4); +NextSess: + L4++; + } +} + +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session) +{ + + // RETURN ANY QUEUED BUFFERS TO FREE QUEUE + + while (Session->L4TX_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4TX_Q)); + + while (Session->L4RX_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4RX_Q)); + + while (Session->L4HOLD_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4HOLD_Q)); + + if (C_Q_COUNT(&Session->L4RESEQ_Q) > Session->L4WINDOW) + { + Debugprintf("Corrupt RESEQ_Q Q Len %d Free Buffs %d", C_Q_COUNT(&Session->L4RESEQ_Q), QCOUNT); + Session->L4RESEQ_Q = 0; + } + + while (Session->L4RESEQ_Q) + ReleaseBuffer(Q_REM((void *)&Session->L4RESEQ_Q)); + + if (Session->PARTCMDBUFFER) + ReleaseBuffer(Session->PARTCMDBUFFER); + + memset(Session, 0, sizeof(TRANSPORTENTRY)); +} + +VOID CloseSessionPartner(TRANSPORTENTRY * Session) +{ + // SEND CLOSE TO CROSSLINKED SESSION AND CLEAR LOCAL SESSION + + if (Session == NULL) + return; + + if (Session->L4CROSSLINK) + CLOSECURRENTSESSION(Session->L4CROSSLINK); + + CLEARSESSIONENTRY(Session); +} + + +VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session) +{ + MESSAGE * Buffer; + struct _LINKTABLE * LINK; + +// SHUT DOWN SESSION AND UNLINK IF CROSSLINKED + + if (Session == NULL) + return; + + Session->L4CROSSLINK = NULL; + + // IF STAY FLAG SET, KEEP SESSION, AND SEND MESSAGE + + if (Session->STAYFLAG) + { + RETURNEDTONODE(Session); + Session->STAYFLAG = 0; // Only do it once + return; + } + + if (Session->L4CIRCUITTYPE & BPQHOST) + { + // BPQ HOST MODE SESSION - INDICATE STATUS CHANGE + + PBPQVECSTRUC HOST = Session->L4TARGET.HOST; + HOST->HOSTSESSION = 0; + HOST->HOSTFLAGS |= 3; /// State Change + + PostStateChange(Session); + CLEARSESSIONENTRY(Session); + return; + } + + if (Session->L4CIRCUITTYPE & PACTOR) + { + // PACTOR-type (Session linked to Port) + + struct _EXTPORTDATA * EXTPORT = Session->L4TARGET.EXTPORT; + + // If any data is queued, move it to the port entry, so it can be sent before the disconnect + + while (Session->L4TX_Q) + { + Buffer = Q_REM((void *)&Session->L4TX_Q); + EXTPORT->PORTCONTROL.PORTTXROUTINE(EXTPORT, Buffer); + } + + EXTPORT->ATTACHEDSESSIONS[Session->KAMSESSION] = NULL; + + CLEARSESSIONENTRY(Session); + return; + } + + if (Session->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION TO CLOSE + + if (Session->L4HOLD_Q || Session->L4TX_Q) // WAITING FOR ACK or MORE TO SEND - SEND DISC LATER + { + Session->FLAGS |= DISCPENDING; // SEND DISC WHEN ALL DATA ACKED + return; + } + + SENDL4DISC(Session); + return; + } + + // Must be LEVEL 2 SESSION TO CLOSE + + // COPY ANY PENDING DATA TO L2 TX Q, THEN GET RID OF SESSION + + LINK = Session->L4TARGET.LINK; + + if (LINK == NULL) // just in case! + { + CLEARSESSIONENTRY(Session); + return; + } + + while (Session->L4TX_Q) + { + Buffer = Q_REM((void *)&Session->L4TX_Q); + C_Q_ADD(&LINK->TX_Q, Buffer); + } + + // NOTHING LEFT AT SESSION LEVEL + + LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK + + if ((LINK->LINKWS != LINK->LINKNS) || LINK->TX_Q) + { + // STILL MORE TO SEND - SEND DISC LATER + + LINK->L2FLAGS |= DISCPENDING; // SEND DISC WHEN ALL DATA ACKED + } + else + { + // NOTHING QUEUED - CAN SEND DISC NOW + + LINK->L2STATE = 4; // DISCONNECTING + LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC + } + + CLEARSESSIONENTRY(Session); + +} + +VOID L4TimerProc() +{ + // CHECK FOR TIMER EXPIRY + + int n = MAXCIRCUITS; + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + + while (n--) + { + if (L4->L4USER[0] == 0) + { + L4++; + continue; + } + + // CHECK FOR L4BUSY SET AND NO LONGER BUSY + + if (L4->NAKBITS & L4BUSY) + { + if ((CHECKIFBUSYL4(L4) & L4BUSY) == 0) + { + // no longer busy + + L4->NAKBITS &= ~L4BUSY; // Clear busy + L4->L4ACKREQ = 1; // SEND ACK + } + } + + if (L4->L4TIMER) // Timer Running? + { + L4->L4TIMER--; + if (L4->L4TIMER == 0) // Expired + L4TIMEOUT(L4); + } + + if (L4->L4ACKREQ) // DELAYED ACK Timer Running? + { + L4->L4ACKREQ--; + if (L4->L4ACKREQ == 0) // Expired + SENDL4IACK(L4); + } + + L4->L4KILLTIMER++; + + // IF BIT 6 OF APPLFLAGS SET, SEND MSG EVERY 11 MINS TO KEEP SESSION OPEN + + if (L4->L4CROSSLINK) // CONNECTED? + if (L4->SESS_APPLFLAGS & 0x40) + if (L4->L4KILLTIMER > 11 * 60) + AUTOTIMER(L4); + + if (L4->L4LIMIT == 0) + L4->L4LIMIT = L4LIMIT; + else + { + if (L4->L4KILLTIMER > L4->L4LIMIT) + { + L4->L4KILLTIMER = 0; + + // CLOSE THIS SESSION, AND ITS PARTNER (IF ANY) + + L4->STAYFLAG = 0; + + Partner = L4->L4CROSSLINK; + CLOSECURRENTSESSION(L4); + + if (Partner) + { + Partner->L4KILLTIMER = 0; //ITS TIMES IS ALSO ABOUT TO EXPIRE + CLOSECURRENTSESSION(Partner); // CLOSE THIS ONE + } + } + } + L4++; + } +} + +VOID L4TIMEOUT(TRANSPORTENTRY * L4) +{ + // TIMER EXPIRED + + // IF LINK UP REPEAT TEXT + // IF S2, REPEAT CONNECT REQUEST + // IF S4, REPEAT DISCONNECT REQUEST + + struct DATAMESSAGE * Msg; + struct DATAMESSAGE * Copy; + struct DEST_LIST * DEST; + + if (L4->L4STATE < 2) + return; + + if (L4->L4STATE == 2) + { + // RETRY CONNECT + + L4->L4RETRIES++; + + if (L4->L4RETRIES > L4N2) + { + // RETRIED N2 TIMES - FAIL LINK + + L4CONNECTFAILED(L4); // TELL OTHER PARTNER IT FAILED + CLEARSESSIONENTRY(L4); + return; + } + + Debugprintf("Retrying L4 Connect Request"); + + SENDL4CONNECT(L4); // Resend connect + return; + } + + if (L4->L4STATE == 4) + { + // RETRY DISCONNECT + + L4->L4RETRIES++; + + if (L4->L4RETRIES > L4N2) + { + // RETRIED N2 TIMES - FAIL LINK + + + CLEARSESSIONENTRY(L4); + return; + } + + SENDL4DISC(L4); // Resend connect + return; + } + // STATE 5 OR ABOVE - RETRY INFO + + + L4->FLAGS &= ~L4BUSY; // CANCEL CHOKE + + L4->L4RETRIES++; + + if (L4->L4RETRIES > L4N2) + { + // RETRIED N2 TIMES - FAIL LINK + + CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT) + return; + } + + // RESEND ALL OUTSTANDING FRAMES + + L4->FLAGS &= 0x7F; // CLEAR CHOKED + + Msg = L4->L4HOLD_Q; + + while (Msg) + { + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(Copy, Msg, Msg->LENGTH); + + DEST = L4->L4TARGET.DEST; + + C_Q_ADD(&DEST->DEST_Q, Copy); + + L4FRAMESRETRIED++; + + Msg = Msg->CHAIN; + } +} + +VOID AUTOTIMER(TRANSPORTENTRY * L4) +{ + // SEND MESSAGE TO USER TO KEEP CIRCUIT OPEN + + struct DATAMESSAGE * Msg = GetBuff(); + + if (Msg == 0) + return; + + Msg->PID = 0xf0; + Msg->L2DATA[0] = 0; + Msg->L2DATA[1] = 0; + + Msg->LENGTH = MSGHDDRLEN + 3; + + C_Q_ADD(&L4->L4TX_Q, Msg); + + PostDataAvailable(L4); + + L4->L4KILLTIMER = 0; + + if (L4->L4CROSSLINK) + L4->L4CROSSLINK->L4KILLTIMER = 0; +} + +VOID L4CONNECTFAILED(TRANSPORTENTRY * L4) +{ + // CONNECT HAS TIMED OUT - SEND MESSAGE TO OTHER END + + struct DATAMESSAGE * Msg; + TRANSPORTENTRY * Partner; + UCHAR * ptr1; + char Nodename[20]; + struct DEST_LIST * DEST; + + Partner = L4->L4CROSSLINK; + + if (Partner == 0) + return; + + Msg = GetBuff(); + + if (Msg == 0) + return; + + Msg->PID = 0xf0; + + ptr1 = SetupNodeHeader(Msg); + + DEST = L4->L4TARGET.DEST; + Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate + + ptr1 += sprintf(ptr1, "Failure with %s\r", Nodename); + + Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg); + + C_Q_ADD(&Partner->L4TX_Q, Msg); + + PostDataAvailable(Partner); + + Partner->L4CROSSLINK = 0; // Back to command lewel +} + + +VOID ProcessIframe(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer) +{ + // IF UP/DOWN LINK, AND CIRCUIT ESTABLISHED, ADD LEVEL 3/4 HEADERS + // (FRAGMENTING IF NECESSARY), AND PASS TO TRANSPORT CONTROL + // FOR ESTABLISHED ROUTE + + // IF INTERNODE MESSAGE, PASS TO ROUTE CONTROL + + // IF UP/DOWN, AND NO CIRCUIT, PASS TO COMMAND HANDLER + + TRANSPORTENTRY * Session; + + // IT IS POSSIBLE TO MULTIPLEX NETROM AND IP STUFF ON THE SAME LINK + + if (Buffer->PID == 0xCC || Buffer->PID == 0xCD) + { + Q_IP_MSG(Buffer); + return; + } + + if (Buffer->PID == 0xCF) + { + // INTERNODE frame + + // IF LINKTYPE IS NOT 3, MUST CHECK IF WE HAVE ACCIDENTALLY ATTACHED A BBS PORT TO THE NODE + + if (LINK->LINKTYPE != 3) + { + if (LINK->CIRCUITPOINTER) + { + // MUST KILL SESSION + + InformPartner(LINK); // CLOSE IT + LINK->CIRCUITPOINTER = NULL; // AND UNHOOK + } + LINK->LINKTYPE = 3; // NOW WE KNOW ITS A CROSSLINK + } + + NETROMMSG(LINK, (L3MESSAGEBUFFER *)Buffer); + return; + } + + if (LINK->LINKTYPE == 3) + { + // Weve receved a non- netrom frame on an inernode link + + ReleaseBuffer(Buffer); + return; + } + + if (LINK->CIRCUITPOINTER) + { + // Pass to Session + + IFRM150(LINK->CIRCUITPOINTER, Buffer); + return; + } + + // UPLINK MESSAGE WITHOUT LEVEL 4 ENTRY - CREATE ONE + + Session = SetupSessionForL2(LINK); + + if (Session == NULL) + return; + + CommandHandler(Session, Buffer); + return; +} + + +VOID IFRM100(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer) +{ + TRANSPORTENTRY * Session; + + if (LINK->CIRCUITPOINTER) + { + // Pass to Session + + IFRM150(LINK->CIRCUITPOINTER, Buffer); + return; + } + + // UPLINK MESSAGE WITHOUT LEVEL 4 ENTRY - CREATE ONE + + Session = SetupSessionForL2(LINK); + + if (Session == NULL) + return; + + CommandHandler(Session, Buffer); + return; +} + + +VOID IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer) +{ + TRANSPORTENTRY * Partner; + struct _LINKTABLE * LINK; + + Session->L4KILLTIMER = 0; // RESET SESSION TIMEOUT + + if (Session->L4CROSSLINK == NULL) // CONNECTED? + { + // NO, SO PASS TO COMMAND HANDLER + + CommandHandler(Session, Buffer); + return; + } + + Partner = Session->L4CROSSLINK; // TO SESSION PARTNER + + if (Partner->L4STATE == 5) + { + C_Q_ADD(&Partner->L4TX_Q, Buffer); + PostDataAvailable(Partner); + return; + } + + + + // MESSAGE RECEIVED BEFORE SESSION IS UP - CANCEL SESSION + // AND PASS MESSAGE TO COMMAND HANDLER + + if (Partner->L4CIRCUITTYPE & L2LINK) // L2 SESSION? + { + // MUST CANCEL L2 SESSION + + LINK = Partner->L4TARGET.LINK; + LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK + + LINK->L2STATE = 4; // DISCONNECTING + LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC + + LINK->L2RETRIES = LINK->LINKPORT->PORTN2 - 2; //ONLY SEND DISC ONCE + } + + CLEARSESSIONENTRY(Partner); + + Session->L4CROSSLINK = 0; // CLEAR CROSS LINK + CommandHandler(Session, Buffer); + return; +} + + +VOID SENDL4DISC(TRANSPORTENTRY * Session) +{ + L3MESSAGEBUFFER * MSG; + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + if (Session->L4STATE < 4) + { + // CIRCUIT NOT UP OR CLOSING - PROBABLY NOT YET SET UP - JUST ZAP IT + + CLEARSESSIONENTRY(Session); + return; + } + + Session->L4TIMER = Session->SESSIONT1; // START TIMER + Session->L4STATE = 4; // SET DISCONNECTING + Session->L4ACKREQ = 0; // CANCEL ACK NEEDED + + MSG = GetBuff(); + + if (MSG == NULL) + return; + + MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(MSG->L3SRCE, Session->L4MYCALL, 7); + memcpy(MSG->L3DEST, DEST->DEST_CALL, 7); + MSG->L3TTL = L3LIVES; + MSG->L4INDEX = Session->FARINDEX; + MSG->L4ID = Session->FARID; + MSG->L4TXNO = 0; + MSG->L4FLAGS = L4DREQ; + + MSG->LENGTH = (int)(&MSG->L4DATA[0] - (UCHAR *)MSG); + + C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG); +} + + +void WriteL4LogLine(UCHAR * mycall, UCHAR * call, UCHAR * node) +{ + UCHAR FN[MAX_PATH]; + FILE * L4LogHandle; + time_t T; + struct tm * tm; + + char Call1[12], Call2[12], Call3[12]; + + char LogMsg[256]; + int MsgLen; + + Call1[ConvFromAX25(mycall, Call1)] = 0; + Call2[ConvFromAX25(call, Call2)] = 0; + Call3[ConvFromAX25(node, Call3)] = 0; + + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s/L4Log_%02d%02d.txt", BPQDirectory, tm->tm_mon + 1, tm->tm_mday); + + L4LogHandle = fopen(FN, "ab"); + + if (L4LogHandle == NULL) + return; + + MsgLen = sprintf(LogMsg, "%02d:%02d:%02d Call to %s from %s at Node %s\r\n", tm->tm_hour, tm->tm_min, tm->tm_sec, Call1, Call2, Call3); + + fwrite(LogMsg , 1, MsgLen, L4LogHandle); + + fclose(L4LogHandle); +} + +VOID CONNECTREQUEST(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, UINT ApplMask, UCHAR * ApplCall) +{ + // CONNECT REQUEST - SEE IF EXISTING SESSION + // IF NOT, GET AND FORMAT SESSION TABLE ENTRY + // SEND CONNECT ACK + + // EDI = _BUFFER, EBX = LINK + + TRANSPORTENTRY * L4; + int BPQNODE = 0; // NOT ONE OF MINE + char BPQPARAMS[10]; // Extended Connect Params from BPQ Node + int CONERROR; + int Index; + + memcpy(BPQPARAMS, &L4T1, 2); // SET DEFAULT T1 IN CASE NOT FROM ANOTHER BPQ NODE + + BPQPARAMS[2] = 0; // 'SPY' NOT SET + + if (FINDCIRCUIT(L3MSG, &L4, &Index)) + { + // SESSION EXISTS - ASSUME RETRY AND SEND ACK + + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + return; + } + + + if (L4 == 0) + { + SendConNAK(LINK, L3MSG); + return; + } + + L4->CIRCUITINDEX = Index; + + SETUPNEWCIRCUIT(LINK, L3MSG, L4, BPQPARAMS, ApplMask, &BPQNODE); + + if (L4->L4TARGET.DEST == 0) + { + // NODE NOT IN TABLE, AND TABLE FULL - CANCEL IT + + memset(L4, 0, sizeof (TRANSPORTENTRY)); + SendConNAK(LINK, L3MSG); + return; + } + // IF CONNECT TO APPL, ALLOCATE BBS PORT + + if (ApplMask == 0 || BPQPARAMS[2] == 'Z') // Z is "Spy" Connect + { + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + + return; + } + + // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS + + + if (ALIASPTR[0] > ' ') + { + struct DATAMESSAGE * Msg; + + // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS + + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + + Msg = GetBuff(); + + if (Msg) + { + Msg->PID = 0xf0; + memcpy(Msg->L2DATA, APPL->APPLCMD, 12); + Msg->L2DATA[12] = 13; + Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR + + C_Q_ADD(&L4->L4RX_Q, Msg); + return; + } + } + + if (cATTACHTOBBS(L4, ApplMask, PACLEN, &CONERROR)) + { + SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall); + return; + } + + // NO BBS AVAILABLE + + CLEARSESSIONENTRY(L4); + SendConNAK(LINK, L3MSG); + return; +} + +VOID SendConACK(struct _LINKTABLE * LINK, TRANSPORTENTRY * L4, L3MESSAGEBUFFER * L3MSG, BOOL BPQNODE, UINT Applmask, UCHAR * ApplCall) +{ + // SEND CONNECT ACK + + L4CONNECTSIN++; + + L3MSG->L4TXNO = L4->CIRCUITINDEX; + L3MSG->L4RXNO = L4->CIRCUITID; + + L3MSG->L4DATA[0] = L4->L4WINDOW; //WINDOW + + L3MSG->L4FLAGS = L4CACK; + + if (LogL4Connects) + WriteL4LogLine(ApplCall, L4->L4USER, L3MSG->L3SRCE); + + if (LogAllConnects) + { + char From[64]; + char toCall[12], fromCall[12], atCall[12]; + + toCall[ConvFromAX25(ApplCall, toCall)] = 0; + fromCall[ConvFromAX25(L4->L4USER, fromCall)] = 0; + atCall[ConvFromAX25(L3MSG->L3SRCE, atCall)] = 0; + + sprintf(From, "%s at Node %s", fromCall, atCall); + WriteConnectLog(From, toCall, "NETROM"); + } + + + if (CTEXTLEN && (Applmask == 0) && FULL_CTEXT) // Any connect, or call to alias + { + struct DATAMESSAGE * Msg; + int Totallen = CTEXTLEN; + int Paclen= PACLEN; + UCHAR * ptr = CTEXTMSG; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&L4->L4TX_Q, Msg); // SEND MESSAGE TO CALLER + PostDataAvailable(L4); + ptr += Paclen; + Totallen -= Paclen; + } + } + + L3SWAPADDRESSES(L3MSG); + + L3MSG->L3TTL = L3LIVES; + + L3MSG->LENGTH = MSGHDDRLEN + 22; // CTL 20 BYTE Header Window + + if (BPQNODE) + { + L3MSG->L4DATA[1] = L3LIVES; // Our TTL + L3MSG->LENGTH++; + } + + C_Q_ADD(&LINK->TX_Q, L3MSG); +} + +int FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex) +{ + // FIND CIRCUIT FOR AN INCOMING MESSAGE + + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * FIRSTSPARE = NULL; + struct DEST_LIST * DEST; + + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (L4->L4USER[0] == 0) // Spare + { + if (FIRSTSPARE == NULL) + { + FIRSTSPARE = L4; + *NewIndex = Index; + } + + L4++; + Index++; + continue; + } + + DEST = L4->L4TARGET.DEST; + + if (DEST == NULL) + { + // L4 entry without a Dest shouldn't happen. (I don't think!) + + char Call1[12], Call2[12]; + + Call1[ConvFromAX25(L4->L4USER, Call1)] = 0; + Call2[ConvFromAX25(L4->L4MYCALL, Call2)] = 0; + + Debugprintf("L4 entry without Target. Type = %02x Calls %s %s", + L4->L4CIRCUITTYPE, Call1, Call2); + + L4++; + Index++; + continue; + } + + if (CompareCalls(L3MSG->L3SRCE, DEST->DEST_CALL)) + { + if (L4->FARID == L3MSG->L4ID && L4->FARINDEX == L3MSG->L4INDEX) + { + // Found it + + *REQL4 = L4; + return TRUE; + } + } + L4++; + Index++; + } + + // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL + + *REQL4 = FIRSTSPARE; + return FALSE; +} + +VOID L3SWAPADDRESSES(L3MESSAGEBUFFER * L3MSG) +{ + // EXCHANGE ORIGIN AND DEST + + char Temp[7]; + + memcpy(Temp, L3MSG->L3SRCE, 7); + memcpy(L3MSG->L3SRCE, L3MSG->L3DEST, 7); + memcpy(L3MSG->L3DEST, Temp, 7); + + L3MSG->L3DEST[6] &= 0x1E; // Mack EOA and CMD + L3MSG->L3SRCE[6] &= 0x1E; + L3MSG->L3SRCE[6] |= 1; // Set Last Call +} + +VOID SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG) +{ + L3MSG->L4FLAGS = L4CACK | L4BUSY; // REJECT + L3MSG->L4DATA[0] = 0; // WINDOW + + L3SWAPADDRESSES(L3MSG); + L3MSG->L3TTL = L3LIVES; + + C_Q_ADD(&LINK->TX_Q, L3MSG); +} + +VOID SETUPNEWCIRCUIT(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, + TRANSPORTENTRY * L4, char * BPQPARAMS, int ApplMask, int * BPQNODE) +{ + struct DEST_LIST * DEST; + int Maxtries = 2; // Just in case + + L4->FARINDEX = L3MSG->L4INDEX; + L4->FARID = L3MSG->L4ID; + + // Index set by caller + + L4->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + L4->SESSIONT1 = L4T1; + + L4->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + if (L3MSG->L4DATA[0] > L4DEFAULTWINDOW) + L4->L4WINDOW = L3MSG->L4DATA[0]; + + memcpy(L4->L4USER, &L3MSG->L4DATA[1], 7); // Originator's call from Call Request + + if (ApplMask) + { + // Should get APPLCALL if set ( maybe ??????????????? + } + +// MOV ESI,APPLCALLTABLEPTR +// LEA ESI,APPLCALL[ESI] + + memcpy(L4->L4MYCALL, MYCALL, 7); + + // GET BPQ EXTENDED CONNECT PARAMS IF PRESENT + + if (L3MSG->LENGTH == MSGHDDRLEN + 38 || L3MSG->LENGTH == MSGHDDRLEN + 39) + { + *BPQNODE = 1; + memcpy(BPQPARAMS, &L3MSG->L4DATA[15],L3MSG->LENGTH - (MSGHDDRLEN + 36)); + } + + L4->L4CIRCUITTYPE = SESSION | UPLINK; + L4->L4STATE = 5; + +TryAgain: + + DEST = CHECKL3TABLES(LINK, L3MSG); + + L4->L4TARGET.DEST = DEST; + + if (DEST == 0) + { + int WorstQual = 256; + struct DEST_LIST * WorstDest = NULL; + int n = MAXDESTS; + + // Node not it table and table full + + // Replace worst quality node with session counts of zero + + // But could have been excluded, so check + + if (CheckExcludeList(L3MSG->L3SRCE) == 0) + return; + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_COUNT == 0 && DEST->DEST_RTT == 0) // Not used and not INP3 + { + if (DEST->NRROUTE[0].ROUT_QUALITY < WorstQual) + { + WorstQual = DEST->NRROUTE[0].ROUT_QUALITY; + WorstDest = DEST; + } + } + DEST++; + } + + if (WorstDest) + { + REMOVENODE(WorstDest); + if (Maxtries--) + goto TryAgain; // We now have a spare (but protect against loop if something amiss) + } + + // Nothing to delete, so just ignore connect + + return; + } + + if (*BPQNODE) + { + SHORT T1; + + DEST->DEST_STATE |= 0x40; // SET BPQ _NODE BIT + memcpy((char *)&T1, BPQPARAMS, 2); + + if (T1 > 300) + L4->SESSIONT1 = L4T1; + else + L4->SESSIONT1 = T1; + } + else + L4->SESSIONT1 = L4T1; // DEFAULT TIMEOUT + + L4->SESSPACLEN = PACLEN; // DEFAULT +} + + +int CHECKIFBUSYL4(TRANSPORTENTRY * L4) +{ + // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY + + int Count; + + if (L4->L4CROSSLINK) // CONNECTED? + Count = CountFramesQueuedOnSession(L4->L4CROSSLINK); + else + Count = CountFramesQueuedOnSession(L4); + + if (Count < L4->L4WINDOW) + return 0; + else + return L4BUSY; +} + +VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall) +{ + // INTERNODE LINK + + TRANSPORTENTRY * L4; + struct DEST_LIST * DEST; + int Opcode; + char Nodename[20]; + char ReplyText[20]; + struct DATAMESSAGE * Msg; + TRANSPORTENTRY * Partner; + UCHAR * ptr1; + int FramesMissing; + L3MESSAGEBUFFER * Saved; + L3MESSAGEBUFFER ** Prev; + char Call[10]; + + L4FRAMESRX++; + + Opcode = L3MSG->L4FLAGS & 15; + + switch (Opcode) + { + case 0: + + // OPCODE 0 is used for a variety of functions, using L4INDEX and L4ID as qualifiers + // 0c0c is used for IP + + if (L3MSG->L4ID == 0x0C && L3MSG->L4INDEX == 0x0C) + { + Q_IP_MSG((PDATAMESSAGE)L3MSG); + return; + } + + // 00 01 Seesm to be Netrom Record Route + + if (L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0) + { + NRRecordRoute((UCHAR *)L3MSG, L3MSG->LENGTH); + return; + } + + ReleaseBuffer(L3MSG); + return; + + case L4CREQ: + + CONNECTREQUEST(LINK, L3MSG, ApplMask, ApplCall); + return; + } + + // OTHERS NEED A SESSION + + L4 = &L4TABLE[L3MSG->L4INDEX]; + + if (L4->CIRCUITID!= L3MSG->L4ID) + { + ReleaseBuffer(L3MSG); + return; + } + + if ((L4->L4CIRCUITTYPE & SESSION) == 0) + { + // Not an L4 Session - must be an old connection + + ReleaseBuffer(L3MSG); + return; + } + + // HAVE FOUND CORRECT SESSION ENTRY + + switch (Opcode) + { + case L4CACK: + + // CONNECT ACK + + DEST = L4->L4TARGET.DEST; + + // EXTRACT EXTENDED PARAMS IF PRESENT + + if (L3MSG->LENGTH > MSGHDDRLEN + 22) // Standard Msg + { + DEST->DEST_STATE &= 0x80; + DEST->DEST_STATE |= (L3MSG->L4DATA[1] - L3MSG->L3TTL) + 0x41; // Hops to dest + x40 + } + + Partner = L4->L4CROSSLINK; + + if (L3MSG->L4FLAGS & L4BUSY) + { + // Refused + + CLEARSESSIONENTRY(L4); + if (Partner) + Partner->L4CROSSLINK = NULL; // CLEAR CROSSLINK + + strcpy(ReplyText, "Busy from"); + } + else + { + // Connect OK + + if (L4->L4STATE == 5) + { + // MUST BE REPEAT MSG - DISCARD + + ReleaseBuffer(L3MSG); + return; + } + + L4->FARINDEX = L3MSG->L4TXNO; + L4->FARID = L3MSG->L4RXNO; + + L4->L4STATE = 5; // ACTIVE + L4->L4TIMER = 0; + L4->L4RETRIES = 0; + + L4->L4WINDOW = L3MSG->L4DATA[0]; + + strcpy(ReplyText, "Connected to"); + } + + if (Partner == 0) + { + ReleaseBuffer(L3MSG); + return; + } + + Msg = (PDATAMESSAGE)L3MSG; // reuse input buffer + + Msg->PID = 0xf0; + ptr1 = SetupNodeHeader(Msg); + + Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate + + ptr1 += sprintf(ptr1, "%s %s\r", ReplyText, Nodename); + + Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg); + + C_Q_ADD(&Partner->L4TX_Q, Msg); + + PostDataAvailable(Partner); + return; + + case L4DREQ: + + // DISCONNECT REQUEST + + L3MSG->L4INDEX = L4->FARINDEX; + L3MSG->L4ID = L4->FARID; + + L3MSG->L4FLAGS = L4DACK; + + L3SWAPADDRESSES(L3MSG); // EXCHANGE SOURCE AND DEST + L3MSG->L3TTL = L3LIVES; + + C_Q_ADD(&LINK->TX_Q, L3MSG); + + CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT) + return; + + case L4DACK: + + CLEARSESSIONENTRY(L4); + ReleaseBuffer(L3MSG); + return; + + case L4INFO: + + //MAKE SURE SESSION IS UP - FIRST I FRAME COULD ARRIVE BEFORE CONNECT ACK + + if (L4->L4STATE == 2) + { + ReleaseBuffer(L3MSG); // SHOULD SAVE - WILL AVOID NEED TO RETRANSMIT + return; + } + + ACKFRAMES(L3MSG, L4, L3MSG->L4RXNO); + + // If DISCPENDING or STATE IS 4, THEN SESSION IS CLOSING - IGNORE ANY I FRAMES + + if ((L4->FLAGS & DISCPENDING) || L4->L4STATE == 4) + { + ReleaseBuffer(L3MSG); + return; + } + + // CHECK RECEIVED SEQUENCE + + FramesMissing = L3MSG->L4TXNO - L4->RXSEQNO; // WHAT WE GOT - WHAT WE WANT + + if (FramesMissing > 128) + FramesMissing -= 256; + + // if NUMBER OF FRAMES MISSING is -VE, THEN IN FACT IT INDICATES A REPEAT + + if (FramesMissing < 0) + { + // FRAME IS A REPEAT + + Call[ConvFromAX25(L3MSG->L3SRCE, Call)] = 0; + Debugprintf("Discarding repeated frame seq %d from %s", L3MSG->L4TXNO, Call); + + L4->L4ACKREQ = 1; + ReleaseBuffer(L3MSG); + return; + } + + if (FramesMissing > 0) + { + // EXPECTED FRAME HAS BEEN MISSED - ASK FOR IT AGAIN, + // AND KEEP THIS FRAME UNTIL MISSING ONE ARRIVES + + L4->NAKBITS |= L4NAK; // SET NAK REQUIRED + + SENDL4IACK(L4); // SEND DATA ACK COMMAND TO ACK OUTSTANDING FRAMES + + // SEE IF WE ALREADY HAVE A COPY OF THIS ONE +/* + Saved = L4->L4RESEQ_Q; + + Call[ConvFromAX25(L3MSG->L3SRCE, Call)] = 0; + Debugprintf("saving seq %d from %s", L3MSG->L4TXNO, Call); + + while (Saved) + { + if (Saved->L4TXNO == L3MSG->L4TXNO) + { + // ALREADY HAVE A COPY - DISCARD IT + + Debugprintf("Already have seq %d - discarding", L3MSG->L4TXNO); + ReleaseBuffer(L3MSG); + return; + } + + Saved = Saved->Next; + } + + C_Q_ADD(&L4->L4RESEQ_Q, L3MSG); // ADD TO CHAIN + return; +*/ + } + + // Frame is OK + +L4INFO_OK: + + if (L3MSG == 0) + { + Debugprintf("Trying to Process NULL L3 Message"); + return; + } + + L4->NAKBITS &= ~L4NAK; // CLEAR MESSAGE LOST STATE + + L4->RXSEQNO++; + + // REMOVE HEADERS, AND QUEUE INFO + + L3MSG->LENGTH -= 20; // L3/L4 Header + + if (L3MSG->LENGTH < (4 + sizeof(void *))) // No PID + { + ReleaseBuffer(L3MSG); + return; + } + + L3MSG->L3PID = 0xF0; // Normal Data PID + + memmove(L3MSG->L3SRCE, L3MSG->L4DATA, L3MSG->LENGTH - (4 + sizeof(void *))); + + REFRESHROUTE(L4); + + L4->L4ACKREQ = L4DELAY; // SEND INFO ACK AFTER L4DELAY (UNLESS I FRAME SENT) + + IFRM150(L4, (PDATAMESSAGE)L3MSG); // CHECK IF SETTING UP AND PASS ON + + // See if anything on reseq Q to process + + if (L4->L4RESEQ_Q == 0) + return; + + Prev = &L4->L4RESEQ_Q; + Saved = L4->L4RESEQ_Q; + + while (Saved) + { + if (Saved->L4TXNO == L4->RXSEQNO) // The one we want + { + // REMOVE IT FROM QUEUE,AND PROCESS IT + + *Prev = Saved->Next; // CHAIN NEXT IN CHAIN TO PREVIOUS + + OLDFRAMES++; // COUNT FOR STATS + + L3MSG = Saved; + Debugprintf("Processing Saved Message %d Address %x", L4->RXSEQNO, L3MSG); + goto L4INFO_OK; + } + + Debugprintf("Message %d %x still on Reseq Queue", Saved->L4TXNO, Saved); + + Prev = &Saved; + Saved = Saved->Next; + } + + return; + + case L4IACK: + + ACKFRAMES(L3MSG, L4, L3MSG->L4RXNO); + REFRESHROUTE(L4); + + // Drop Through + } + + // Unrecognised - Ignore + + ReleaseBuffer(L3MSG); + return; +} + + +VOID ACKFRAMES(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY * L4, int NR) +{ + // SEE HOW MANY FRAMES ARE ACKED - IF NEGATIVE, THAN THIS MUST BE A + // DELAYED REPEAT OF AN ACK ALREADY PROCESSED + + int Count = NR - L4->L4WS; + L3MESSAGEBUFFER * Saved; + struct DEST_LIST * DEST; + struct DATAMESSAGE * Msg; + struct DATAMESSAGE * Copy; + int RTT; + + + if (Count < -128) + Count += 256; + + if (Count < 0) + { + // THIS MAY BE A DELAYED REPEAT OF AN ACK ALREADY PROCESSED + + return; // IGNORE COMPLETELY + } + + while (Count > 0) + { + // new ACK + + // FRAME L4WS HAS BEED ACKED - IT SHOULD BE FIRST ON HOLD QUEUE + + Saved = Q_REM((void *)&L4->L4HOLD_Q); + + if (Saved) + ReleaseBuffer(Saved); + + // CHECK RTT SEQUENCE + + if (L4->L4WS == L4->RTT_SEQ) + { + if (L4->RTT_TIMER) + { + // FRAME BEING TIMED HAS BEEN ACKED - UPDATE DEST RTT TIMER + + DEST = L4->L4TARGET.DEST; + + RTT = GetTickCount() - L4->RTT_TIMER; + + if (DEST->DEST_RTT == 0) + DEST->DEST_RTT = RTT; + else + DEST->DEST_RTT = ((DEST->DEST_RTT * 9) + RTT) /10; // 90% Old + New + } + } + + L4->L4WS++; + Count--; + } + + L4->L4TIMER = 0; + L4->L4RETRIES = 0; + + if (NR != L4->TXSEQNO) + { + // Not all Acked + + L4->L4TIMER = L4->SESSIONT1; // RESTART TIMER + } + else + { + if ((L4->FLAGS & DISCPENDING) && L4->L4TX_Q == 0) + { + // All Acked and DISC Pending, so send it + + SENDL4DISC(L4); + return; + } + } + + // SEE IF CHOKE SET + + L4->FLAGS &= ~L4BUSY; + + if (L3MSG->L4FLAGS & L4BUSY) + { + L4->FLAGS |= L3MSG->L4FLAGS & L4BUSY; // Get Busy flag from message + + if ((L3MSG->L4FLAGS & L4NAK) == 0) + return; // Dont send while biust unless NAC received + } + + if (L3MSG->L4FLAGS & L4NAK) + { + // RETRANSMIT REQUESTED MESSAGE - WILL BE FIRST ON HOLD QUEUE + + Msg = L4->L4HOLD_Q; + + if (Msg == 0) + return; + + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(Copy, Msg, Msg->LENGTH); + + DEST = L4->L4TARGET.DEST; + + C_Q_ADD(&DEST->DEST_Q, Copy); + } +} + + + + + + + + + + + + + + + +VOID SENDL4IACK(TRANSPORTENTRY * Session) +{ + // SEND INFO ACK + + PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)GetBuff(); + struct DEST_LIST * DEST = Session->L4TARGET.DEST; + + if (MSG == NULL) + return; + + MSG->L3PID = 0xCF; // NET MESSAGE + + memcpy(MSG->L3SRCE, Session->L4MYCALL, 7); + memcpy(MSG->L3DEST, DEST->DEST_CALL, 7); + + MSG->L3TTL = L3LIVES; + + MSG->L4INDEX = Session->FARINDEX; + MSG->L4ID = Session->FARID; + + MSG->L4TXNO = 0; + + + MSG->L4RXNO = Session->RXSEQNO; + Session->L4LASTACKED = Session->RXSEQNO; // SAVE LAST NUMBER ACKED + + MSG->L4FLAGS = L4IACK | GETBUSYBIT(Session) | Session->NAKBITS; + + MSG->LENGTH = MSGHDDRLEN + 22; + + C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG); +} + + + + +/* + PUBLIC KILLSESSION +KILLSESSION: + + pushad + push ebx + CALL _CLEARSESSIONENTRY + pop ebx + popad + + JMP L4CONN90 ; REJECT + + PUBLIC CONNECTACK +CONNECTACK: +; +; EXTRACT EXTENDED PARAMS IF PRESENT +; + + CMP BYTE PTR MSGLENGTH[EDI],L4DATA+1 + JE SHORT NOTBPQ + + MOV AL,L4DATA+1[EDI] + SUB AL,L3MONR[EDI] + ADD AL,41H ; HOPS TO DEST + 40H + + MOV ESI,L4TARGET[EBX] + AND DEST_STATE[ESI],80H + OR DEST_STATE[ESI],AL ; SAVE + + PUBLIC NOTBPQ +NOTBPQ: +; +; SEE IF SUCCESS OR FAIL +; + PUSH EDI + + MOV ESI,L4TARGET[EBX] ; ADDR OF LINK/DEST ENTRY + LEA ESI,DEST_CALL[ESI] + + CALL DECODENODENAME ; CONVERT TO ALIAS:CALL + + MOV EDI,OFFSET32 CONACKCALL + MOV ECX,17 + REP MOVSB + + + POP EDI + + TEST L4FLAGS[EDI],L4BUSY + JNZ SHORT L4CONNFAILED + + CMP L4STATE[EBX],5 + JE SHORT CONNACK05 ; MUST BE REPEAT MSG - DISCARD + + MOV AX,WORD PTR L4TXNO[EDI] ; HIS INDEX + MOV WORD PTR FARINDEX[EBX],AX + + MOV L4STATE[EBX],5 ; ACTIVE + MOV L4TIMER[EBX],0 ; CANCEL TIMER + MOV L4RETRIES[EBX],0 ; CLEAR RETRY COUNT + + MOV AL,L4DATA[EDI] ; WINDOW + MOV L4WINDOW[EBX],AL ; SET WINDOW + + MOV EDX,L4CROSSLINK[EBX] ; POINT TO PARTNER +; + MOV ESI,OFFSET32 CONNECTEDMSG + MOV ECX,LCONNECTEDMSG + + JMP SHORT L4CONNCOMM + + PUBLIC L4CONNFAILED +L4CONNFAILED: +; + MOV EDX,L4CROSSLINK[EBX] ; SAVE PARTNER + pushad + push ebx + CALL _CLEARSESSIONENTRY + pop ebx + popad + + PUSH EBX + + MOV EBX,EDX + MOV L4CROSSLINK[EBX],0 ; CLEAR CROSSLINK + POP EBX + + MOV ESI,OFFSET32 BUSYMSG ; ?? BUSY + MOV ECX,LBUSYMSG + + PUBLIC L4CONNCOMM +L4CONNCOMM: + + OR EDX,EDX + JNZ SHORT L4CONNOK10 +; +; CROSSLINK HAS GONE?? - JUST CHUCK MESSAGE +; + PUBLIC CONNACK05 +CONNACK05: + + JMP L4DISCARD + + PUBLIC L4CONNOK10 +L4CONNOK10: + + PUSH EBX + PUSH ESI + PUSH ECX + + MOV EDI,_BUFFER + + ADD EDI,7 + MOV AL,0F0H + STOSB ; PID + + CALL _SETUPNODEHEADER ; PUT IN _NODE ID + + + POP ECX + POP ESI + REP MOVSB + + MOV ESI,OFFSET32 CONACKCALL + MOV ECX,17 ; MAX LENGTH ALIAS:CALL + REP MOVSB + + MOV AL,0DH + STOSB + + MOV ECX,EDI + MOV EDI,_BUFFER + SUB ECX,EDI + + MOV MSGLENGTH[EDI],CX + + MOV EBX,EDX ; CALLER'S SESSION + + LEA ESI,L4TX_Q[EBX] + CALL _Q_ADD ; SEND MESSAGE TO CALLER + + CALL _POSTDATAAVAIL + + POP EBX ; ORIGINAL CIRCUIT TABLE + RET + + + PUBLIC SENDCONNECTREPLY +SENDCONNECTREPLY: +; +; LINK SETUP COMPLETE - EBX = LINK, EDI = _BUFFER +; + CMP LINKTYPE[EBX],3 + JNE SHORT CONNECTED00 +; +; _NODE - _NODE SESSION SET UP - DONT NEED TO DO ANYTHING (I THINK!) +; + CALL RELBUFF + RET + +; +; UP/DOWN LINK +; + PUBLIC CONNECTED00 +CONNECTED00: + CMP CIRCUITPOINTER[EBX],0 + JNE SHORT CONNECTED01 + + CALL RELBUFF ; UP/DOWN WITH NO SESSION - NOONE TO TELL + RET ; NO CROSS LINK + PUBLIC CONNECTED01 +CONNECTED01: + MOV _BUFFER,EDI + PUSH EBX + PUSH ESI + PUSH ECX + + ADD EDI,7 + MOV AL,0F0H + STOSB ; PID + + CALL _SETUPNODEHEADER ; PUT IN _NODE ID + + LEA ESI,LINKCALL[EBX] + + PUSH EDI + CALL CONVFROMAX25 ; ADDR OF CALLED STATION + POP EDI + + MOV EBX,CIRCUITPOINTER[EBX] + + MOV L4STATE[EBX],5 ; SET LINK UP + + MOV EBX,L4CROSSLINK[EBX] ; TO INCOMING LINK + cmp ebx,0 + jne xxx +; +; NO LINK ??? +; + MOV EDI,_BUFFER + CALL RELBUFF + + POP ECX + POP ESI + POP EBX + + RET + + PUBLIC xxx +xxx: + + POP ECX + POP ESI + REP MOVSB + + MOV ESI,OFFSET32 _NORMCALL + MOVZX ECX,_NORMLEN + REP MOVSB + + MOV AL,0DH + STOSB + + MOV ECX,EDI + MOV EDI,_BUFFER + SUB ECX,EDI + + MOV MSGLENGTH[EDI],CX + + LEA ESI,L4TX_Q[EBX] + CALL _Q_ADD ; SEND MESSAGE TO CALLER + + CALL _POSTDATAAVAIL + + POP EBX + RET +*/ \ No newline at end of file diff --git a/LinBPQ.c b/LinBPQ.c new file mode 100644 index 0000000..e5d6f6e --- /dev/null +++ b/LinBPQ.c @@ -0,0 +1,1766 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Control Routine for LinBPQ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include "bpqmail.h" +#ifdef WIN32 +#include +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#ifndef MACBPQ +#ifndef FREEBSD +#include +#endif +#endif +#endif + +#include "time.h" + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +BOOL APIENTRY Rig_Init(); + +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); +VOID CopyConfigFile(char * ConfigName); +VOID SendMailForThread(VOID * Param); +VOID GetUIConfig(); +Dll BOOL APIENTRY Init_IP(); +VOID OpenReportingSockets(); +VOID SetupNTSAliases(char * FN); +int DeleteRedundantMessages(); +BOOL InitializeTNCEmulator(); +VOID FindLostBuffers(); +VOID IPClose(); +DllExport BOOL APIENTRY Rig_Close(); +Dll BOOL APIENTRY Poll_IP(); +BOOL Rig_Poll(); +BOOL Rig_Poll(); +VOID CheckWL2KReportTimer(); +VOID TNCTimer(); +VOID SendLocation(); +int ChatPollStreams(); +void ChatTrytoSend(); +VOID BBSSlowTimer(); +int GetHTMLForms(); +char * AddUser(char * Call, char * password, BOOL BBSFlag); +VOID SaveChatConfigFile(char * ConfigName); +VOID SaveMH(); +int upnpClose(); +void SaveAIS(); +void initAIS(); +void DRATSPoll(); + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; + +BOOL RunMail = FALSE; +BOOL RunChat = FALSE; +BOOL needAIS= FALSE; +BOOL needADSB = FALSE; + +int CloseOnError = 0; + +VOID Poll_AGW(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +BOOL AGWActive = FALSE; + +extern int AGWPort; + +BOOL RigActive = FALSE; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern int NumberofStreams; +extern int MaxStreams; +extern ULONG BBSApplMask; +extern int BBSApplNum; +extern int ChatApplNum; +extern int MaxChatStreams; + +extern int NUMBEROFTNCPORTS; + +extern int EnableUI; + +extern BOOL AUTOSAVEMH; + +extern FILE * LogHandle[4]; + +#define MaxSockets 64 + +extern ConnectionInfo Connections[MaxSockets+1]; + +time_t LastTrafficTime; +extern int MaintTime; + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + +int _MYTIMEZONE = 0; + +// flags equates + +#define F_Excluded 0x0001 +#define F_LOC 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_PAG 0x0020 +#define F_GST 0x0040 +#define F_MOD 0x0080 +#define F_PRV 0x0100 +#define F_UNP 0x0200 +#define F_NEW 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 + +/* #define F_PWD 0x1000 */ + + +UCHAR BPQDirectory[260]; +UCHAR LogDirectory[260]; + +BOOL GetConfig(char * ConfigName); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +int EncryptPass(char * Pass, char * Encrypt); +int APIENTRY FindFreeStream(); +int PollStreams(); +int APIENTRY SetAppl(int stream, int flags, int mask); +int APIENTRY SessionState(int stream, int * state, int * change); +int APIENTRY SessionControl(int stream, int command, int Mask); + +BOOL ChatInit(); +VOID CloseChat(); +VOID CloseTNCEmulator(); + +config_t cfg; +config_setting_t * group; + +BOOL MonBBS = TRUE; +BOOL MonCHAT = TRUE; +BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + +extern BOOL LogAPRSIS; + +BOOL UIEnabled[33]; +BOOL UINull[33]; +char * UIDigi[33]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + +extern char MailDir[MAX_PATH]; + +extern time_t MaintClock; // Time to run housekeeping + +#ifdef WIN32 +BOOL KEEPGOING = 30; // 5 secs to shut down +#else +BOOL KEEPGOING = 50; // 5 secs to shut down +#endif +BOOL Restarting = FALSE; +BOOL CLOSING = FALSE; + +int ProgramErrors; +int Slowtimer = 0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + + +VOID CheckProgramErrors() +{ + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + +/* + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); +*/ + exit(0); + } +} + +#ifdef WIN32 + +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch( fdwCtrlType ) + { + // Handle the CTRL-C signal. + case CTRL_C_EVENT: + printf( "Ctrl-C event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return( TRUE ); + + // CTRL-CLOSE: confirm that the user wants to exit. + case CTRL_CLOSE_EVENT: + + CLOSING = TRUE; + printf( "Ctrl-Close event\n\n" ); + Sleep(20000); + Beep( 750, 300 ); + return( TRUE ); + + // Pass other signals to the next handler. + case CTRL_BREAK_EVENT: + Beep( 900, 200 ); + printf( "Ctrl-Break event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + case CTRL_LOGOFF_EVENT: + Beep( 1000, 200 ); + printf( "Ctrl-Logoff event\n\n" ); + return FALSE; + + case CTRL_SHUTDOWN_EVENT: + Beep( 750, 500 ); + printf( "Ctrl-Shutdown event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + default: + return FALSE; + } +} + +#else + +// Linux Signal Handlers + +static void sigterm_handler(int sig) +{ + syslog(LOG_INFO, "terminating on SIGTERM\n"); + CLOSING = TRUE; +} + +static void sigint_handler(int sig) +{ + printf("terminating on SIGINT\n"); + CLOSING = TRUE; +} + + +static void sigusr1_handler(int sig) +{ + signal(SIGUSR1, sigusr1_handler); +} + +#endif + + +#ifndef WIN32 + +BOOL CopyFile(char * In, char * Out, BOOL Failifexists) +{ + FILE * Handle; + DWORD FileSize; + char * Buffer; + struct stat STAT; + + if (stat(In, &STAT) == -1) + return FALSE; + + FileSize = STAT.st_size; + + Handle = fopen(In, "rb"); + + if (Handle == NULL) + return FALSE; + + Buffer = malloc(FileSize+1); + + FileSize = fread(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + + if (FileSize != STAT.st_size) + { + free(Buffer); + return FALSE; + } + + Handle = fopen(Out, "wb"); + + if (Handle == NULL) + { + free(Buffer); + return FALSE; + } + + FileSize = fwrite(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + free(Buffer); + + return TRUE; +} +#endif + +int RefreshMainWindow() +{ + return 0; +} + +int LastSemGets = 0; + +extern int SemHeldByAPI; + +VOID MonitorThread(void * x) +{ + // Thread to detect stuck semaphore + + do + { + if ((Semaphore.Gets == LastSemGets) && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d", + Semaphore.SemProcessID, SemHeldByAPI); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); +// Debugprintf("Monitor Thread Still going %d %d %d %x %d", LastSemGets, Semaphore.Gets, Semaphore.Flag, Semaphore.SemThreadID, SemHeldByAPI); + + } + while (TRUE); +} + + + + +VOID TIMERINTERRUPT(); + +BOOL Start(); +VOID INITIALISEPORTS(); +Dll BOOL APIENTRY Init_APRS(); +VOID APRSClose(); +Dll VOID APIENTRY Poll_APRS(); +VOID HTTPTimer(); + + +#define CKernel +#include "Versions.h" + +extern struct SEM Semaphore; + +int SemHeldByAPI = 0; +BOOL IGateEnabled = TRUE; +BOOL APRSActive = FALSE; +BOOL ReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; + +BOOL IPActive = FALSE; +extern BOOL IPRequired; + +extern struct WL2KInfo * WL2KReports; + +int InitDone; +char pgm[256] = "LINBPQ"; + +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MUIONLY; +UCHAR MTX; +unsigned long long MMASK; + + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +int SAVEPORT = 0; + +VOID SetApplPorts(); + +char VersionString[50] = Verstring; +char VersionStringWithBuild[50]=Verstring; +int Ver[4] = {Vers}; +char TextVerstring[50] = Verstring; + +extern UCHAR PWLen; +extern char PWTEXT[]; +extern int ISPort; + +extern char ChatConfigName[250]; + +UCHAR * GetBPQDirectory() +{ + return BPQDirectory; +} +UCHAR * GetLogDirectory() +{ + return LogDirectory; +} +extern int POP3Timer; + +int main(int argc, char * argv[]) +{ + int i; + struct UserInfo * user = NULL; + ConnectionInfo * conn; + struct stat STAT; + PEXTPORTDATA PORTVEC; + UCHAR LogDir[260]; + +#ifdef WIN32 + + WSADATA WsaData; // receives data from WSAStartup + HWND hWnd = GetForegroundWindow(); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + + // disable the [x] button. + + if (hWnd != NULL) + { + HMENU hMenu = GetSystemMenu(hWnd, 0); + if (hMenu != NULL) + { + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + DrawMenuBar(hWnd); + } + } + +#else + setlinebuf(stdout); + struct sigaction act; + openlog("LINBPQ", LOG_PID, LOG_DAEMON); +#ifndef MACBPQ +#ifndef FREEBSD + prctl(PR_SET_DUMPABLE, 1); // Enable Core Dumps even with setcap +#endif +#endif +#endif + + printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); + printf("%s\n", VerCopyright); + + if (argc > 1 && _stricmp(argv[1], "-v") == 0) + return 0; + + sprintf(RlineVer, "LinBPQ%d.%d.%d", Ver[0], Ver[1], Ver[2]); + + + Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + +#ifndef MACBPQ + _MYTIMEZONE = _timezone; +#endif + + if (_MYTIMEZONE < -86400 || _MYTIMEZONE > 86400) + _MYTIMEZONE = 0; + +#ifdef WIN32 + GetCurrentDirectory(256, BPQDirectory); + GetCurrentDirectory(256, LogDirectory); +#else + getcwd(BPQDirectory, 256); + getcwd(LogDirectory, 256); +#endif + Consoleprintf("Current Directory is %s\n", BPQDirectory); + + for (i = 1; i < argc; i++) + { + if (_memicmp(argv[i], "logdir=", 7) == 0) + { + strcpy(LogDirectory, &argv[i][7]); + break; + } + } + + + // Make sure logs directory exists + + sprintf(LogDir, "%s/logs", LogDirectory); + +#ifdef WIN32 + CreateDirectory(LogDir, NULL); +#else + mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + if (!ProcessConfig()) + { + WritetoConsoleLocal("Configuration File Error\n"); + return (0); + } + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Linux (", TextVerstring); + +#ifdef MACBPQ + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for MAC (", TextVerstring); +#endif +#ifdef FREEBSD + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for FreeBSD (", TextVerstring); +#endif + + + GetSemaphore(&Semaphore, 0); + + if (Start() != 0) + { + FreeSemaphore(&Semaphore); + return (0); + } + + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + + PWLen=i; + + SetApplPorts(); + + GetUIConfig(); + + INITIALISEPORTS(); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + if (needAIS) + initAIS(); + + RigActive = Rig_Init(); + + FreeSemaphore(&Semaphore); + + OpenReportingSockets(); + + initUTF8(); + + InitDone = TRUE; + + Debugprintf("Monitor Thread ID %x", _beginthread(MonitorThread, 0, 0)); + + +#ifdef WIN32 +#else + openlog("LINBPQ", LOG_PID, LOG_DAEMON); + + memset (&act, '\0', sizeof(act)); + + act.sa_handler = &sigint_handler; + if (sigaction(SIGINT, &act, NULL) < 0) + perror ("SIGINT"); + + act.sa_handler = &sigterm_handler; + if (sigaction(SIGTERM, &act, NULL) < 0) + perror ("sigaction"); + + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, NULL) < 0) + perror ("SIGHUP"); + + if (sigaction(SIGPIPE, &act, NULL) < 0) + perror ("SIGPIPE"); + +#endif + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "chat") == 0) + IncludesChat = TRUE; + } + + if (IncludesChat) + { + RunChat = TRUE; + + printf("Starting Chat\n"); + + sprintf (ChatConfigName, "%s/chatconfig.cfg", BPQDirectory); + printf("Config File is %s\n", ChatConfigName); + + if (stat(ChatConfigName, &STAT) == -1) + { + printf("Chat Config File not found - creating a default config\n"); + ChatApplNum = 2; + MaxChatStreams = 10; + SaveChatConfigFile(ChatConfigName); + } + + if (GetChatConfig(ChatConfigName) == EXIT_FAILURE) + { + printf("Chat Config File seems corrupt - check before continuing\n"); + return -1; + } + + if (ChatApplNum) + { + if (ChatInit() == 0) + { + printf("Chat Init Failed\n"); + RunChat = 0; + } + else + { + printf("Chat Started\n"); + } + } + else + { + printf("Chat APPLNUM not defined\n"); + RunChat = 0; + } + CopyConfigFile(ChatConfigName); + } + + // Start Mail if requested by command line or config + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "mail") == 0) + IncludesMail = TRUE; + } + + + if (IncludesMail) + { + RunMail = TRUE; + + printf("Starting Mail\n"); + + sprintf (ConfigName, "%s/linmail.cfg", BPQDirectory); + printf("Config File is %s\n", ConfigName); + + if (stat(ConfigName, &STAT) == -1) + { + printf("Config File not found - creating a default config\n"); + strcpy(BBSName, MYNODECALL); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + BBSApplNum = 1; + MaxStreams = 10; + SaveConfig(ConfigName); + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + printf("BBS Config File seems corrupt - check before continuing\n"); + return -1; + } + + printf("Config Processed\n"); + + BBSApplMask = 1<<(BBSApplNum-1); + + // See if we need to warn of possible problem with BaseDir moved by installer + + sprintf(BaseDir, "%s", BPQDirectory); + + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "/"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "/"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "/"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "/"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "/"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "/"); + strcat(MailDir, "Mail"); + +#ifdef WIN32 + CreateDirectory(MailDir, NULL); // Just in case +#else + mkdir(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + // Make backup copies of Databases + +// CopyConfigFile(ConfigName); + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if(SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // See if just want to add user (mainly for setup scripts) + + if (argc == 5 && _stricmp(argv[1], "--adduser") == 0) + { + BOOL isBBS = FALSE; + char * response; + + if (_stricmp(argv[4], "TRUE") == 0) + isBBS = TRUE; + + printf("Adding User %s\r\n", argv[2]); + response = AddUser(argv[2], argv[3], isBBS); + printf("%s", response); + exit(0); + } + // Allocate Streams + + strcpy(pgm, "BBS"); + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + +// BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + Debugprintf("POP3 Debug Before Init TCP Timer = %d", POP3Timer); + + InitialiseTCP(); + Debugprintf("POP3 Debug Before Init NNTP Timer = %d", POP3Timer); + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (EnableUI || MailForInterval) + SetupUIInterface(); + + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = mktime(tm) - (time_t)_MYTIMEZONE; + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "tidymail") == 0) + DeleteRedundantMessages(); + + if (_stricmp(argv[i], "nohomebbs") == 0) + DontNeedHomeBBS = TRUE; + } + + printf("Mail Started\n"); + Logprintf(LOG_BBS, NULL, '!', "Mail Starting"); + + } + } + + Debugprintf("POP3 Debug After Mail Init Timer = %d", POP3Timer); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + +#ifndef WIN32 + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "daemon") == 0) + { + + // Convert to daemon + + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + + if (pid < 0) + exit(EXIT_FAILURE); + + if (pid > 0) + exit(EXIT_SUCCESS); + + /* Change the file mode mask */ + + umask(0); + + /* Create a new SID for the child process */ + + sid = setsid(); + + if (sid < 0) + exit(EXIT_FAILURE); + + /* Change the current working directory */ + + if ((chdir("/")) < 0) + exit(EXIT_FAILURE); + + /* Close out the standard file descriptors */ + + printf("Entering daemon mode\n"); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + break; + } + } +#endif + + while (KEEPGOING) + { + Sleep(100); + GetSemaphore(&Semaphore, 2); + + if (QCOUNT < 10) + { + if (CLOSING == FALSE) + FindLostBuffers(); + CLOSING = TRUE; + } + + if (CLOSING) + { + if (RunChat) + { + CloseChat(); + RunChat = FALSE; + } + + if (RunMail) + { + int BPQStream, n; + + RunMail = FALSE; + + for (n = 0; n < NumberofStreams; n++) + { + BPQStream = Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + SaveConfig(ConfigName); + } + + KEEPGOING--; // Give time for links to close + setbuf(stdout, NULL); + printf("Closing... %d \r", KEEPGOING); + } + + + if (RigReconfigFlag) + { + RigReconfigFlag = FALSE; + Rig_Close(); + Sleep(2000); // Allow CATPTT threads to close + RigActive = Rig_Init(); + + Consoleprintf("Rigcontrol Reconfiguration Complete"); + } + + if (APRSReconfigFlag) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + Consoleprintf("APRS Reconfiguration Complete"); + } + + if (ReconfigFlag) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + ReconfigFlag = FALSE; + +// SetupBPQDirectory(); + + WritetoConsoleLocal("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { +// SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + WL2KReports = NULL; + +// Sleep(2000); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); + + SetApplPorts(); + + GetUIConfig(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS, IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + +// PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + OpenReportingSockets(); + + WritetoConsoleLocal("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 2); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 2); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + + if (IPActive) Poll_IP(); + if (RigActive) Rig_Poll(); + if (APRSActive) Poll_APRS(); + CheckWL2KReportTimer(); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + + HTTPTimer(); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + + Slowtimer++; + + if (RunChat) + { + ChatPollStreams(); + ChatTrytoSend(); + + if (Slowtimer > 100) // 10 secs + { + ChatTimer(); + } + } + + if (RunMail) + { + PollStreams(); + + if (Slowtimer > 100) // 10 secs + { + time_t NOW = time(NULL); + struct tm * tm; + + TCPTimer(); + FWDTimerProc(); + BBSSlowTimer(); + + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + LastTrafficTime = NOW; + CreateBBSTrafficReport(); + } + } + } + TCPFastTimer(); + TrytoSend(); + } + + if (Slowtimer > 100) + Slowtimer = 0; + } + + printf("Closing Ports\n"); + + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + if (needAIS) + SaveAIS(); + + + // Close Ports + + PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5, PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + if (AUTOSAVE) + SaveNodes(); + + if (AUTOSAVEMH) + SaveMH(); + + if (IPActive) + IPClose(); + + if (RunMail) + FreeWebMailMallocs(); + + upnpClose(); + + // Close any open logs + + for (i = 0; i < 4; i++) + { + if (LogHandle[i]) + fclose(LogHandle[i]); + } + + return 0; +} + +int APIENTRY WritetoConsole(char * buff) +{ + return WritetoConsoleLocal(buff); +} + +int WritetoConsoleLocal(char * buff) +{ + return printf("%s", buff); +} + +#ifdef WIN32 +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +#endif +//UINT SoundModemExtInit(EXTPORTDATA * PortEntry); +//UINT BaycomExtInit(EXTPORTDATA * PortEntry); + +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); + +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); + +void * TelnetExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); + +void * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + // Only works with built in drivers + + UCHAR Value[20]; + + strcpy(Value,PORTVEC->PORT_DLL_NAME); + + _strupr(Value); + +#ifndef FREEBSD + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; +#endif + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + +#ifdef WIN32 + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + +#endif +/* + if (strstr(Value, "SOUNDMODEM")) + return (UINT) SoundModemExtInit; + + if (strstr(Value, "BAYCOM")) + return (UINT) BaycomExtInit; +*/ + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + return(0); +} + +int APIENTRY Restart() +{ + CLOSING = TRUE; + return TRUE; +} + +int APIENTRY Reboot() +{ + // Run sudo shutdown -r -f +#ifdef WIN32 + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); + return 0; +#else + + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[0] = "sudo"; + arg_list[1] = "shutdown"; + arg_list[2] = "now"; + arg_list[3] = "-r"; + + // Fork and Exec shutdown + + // Duplicate this process. + + child_pid = fork(); + + if (child_pid == -1) + { + printf ("Reboot fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to run shutdown\n"); + exit(0); // Kill the new process + } + return TRUE; +#endif + +} + +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsoleLocal("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsoleLocal("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} + +int APRSWriteLog(char * msg); + +VOID MonitorAPRSIS(char * Msg, size_t MsgLen, BOOL TX) +{ + char Line[300]; + char Copy[300]; + int Len; + struct tm * TM; + time_t NOW; + + if (LogAPRSIS == 0) + return; + + if (MsgLen > 250) + return; + + // Mustn't change Msg + + memcpy(Copy, Msg, MsgLen); + Copy[MsgLen] = 0; + + NOW = time(NULL); + TM = gmtime(&NOW); + + Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); + + APRSWriteLog(Line); + +} + +struct TNCINFO * TNC; + +#ifndef WIN32 + +#include +#include + +#ifdef __MACH__ + +#include + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 0 + +int clock_gettime(int clk_id, struct timespec *t){ + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t time; + time = mach_absolute_time(); + double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); + double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); + t->tv_sec = seconds; + t->tv_nsec = nseconds; + return 0; +} +#endif + +int GetTickCount() +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + + + +void SetWindowText(HWND hWnd, char * lpString) +{ + return; +}; + +BOOL SetDlgItemText(HWND hWnd, int item, char * lpString) +{ + return 0; +}; + +#endif + +int GetListeningPortsPID(int Port) +{ +#ifdef WIN32 + + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + unsigned int n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } +#endif + return 0; // Not found +} + + + +VOID Check_Timer() +{ +} + +VOID POSTDATAAVAIL(){}; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(6425,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) +}; + + +//VOID SendRPBeacon(struct TNCINFO * TNC) +//{ +//} + +int PollStreams() +{ + int state,change; + ConnectionInfo * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + DoReceivedData(conn->BPQStream); + DoBBSMonitorData(conn->BPQStream); + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + Connected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + Disconnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + } + } + + return 0; +} + + +VOID CloseConsole(int Stream) +{ +} + +#ifndef WIN32 + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + return 0; +} +#endif + +#ifdef FREEBSD + +char * gcvt(double _Val, int _NumOfDigits, char * _DstBuf) +{ + sprintf(_DstBuf, "%f", _Val); + return _DstBuf; +} + +#endif + + + + + diff --git a/LzFind.c b/LzFind.c new file mode 100644 index 0000000..4fa7e25 --- /dev/null +++ b/LzFind.c @@ -0,0 +1,751 @@ +/* LzFind.c -- Match finder for LZ algorithms + 2008-10-04 : Igor Pavlov : Public domain */ + +#include + +#include "LzFind.h" +#include "LzHash.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((LZ_UInt32)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((LZ_UInt32)3 << 30) + +#define kStartMaxLen 3 + +static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + if (!p->directInput) + { + alloc->Free(alloc, p->bufferBase); + p->bufferBase = 0; + } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +static int LzInWindow_Create(CMatchFinder *p, LZ_UInt32 keepSizeReserv, ISzAlloc *alloc) +{ + LZ_UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; + if (p->directInput) + { + p->blockSize = blockSize; + return 1; + } + if (p->bufferBase == 0 || p->blockSize != blockSize) + { + LzInWindow_Free(p, alloc); + p->blockSize = blockSize; + p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); + } + return (p->bufferBase != 0); +} + +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +Byte MatchFinder_GetIndexByte(CMatchFinder *p, LZ_Int32 index) { return p->buffer[index]; } + +LZ_UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +void MatchFinder_ReduceOffsets(CMatchFinder *p, LZ_UInt32 subValue) +{ + p->posLimit -= subValue; + p->pos -= subValue; + p->streamPos -= subValue; +} + +static void MatchFinder_ReadBlock(CMatchFinder *p) +{ + if (p->streamEndWasReached || p->result != SZ_OK) + return; + for (;;) + { + Byte *dest = p->buffer + (p->streamPos - p->pos); + size_t size = (p->bufferBase + p->blockSize - dest); + if (size == 0) + return; + p->result = p->stream->Read(p->stream, dest, &size); + if (p->result != SZ_OK) + return; + if (size == 0) + { + p->streamEndWasReached = 1; + return; + } + p->streamPos += (LZ_UInt32)size; + if (p->streamPos - p->pos > p->keepSizeAfter) + return; + } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ + memmove(p->bufferBase, + p->buffer - p->keepSizeBefore, + (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); + p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ + if (p->streamEndWasReached) + return; + if (p->keepSizeAfter >= p->streamPos - p->pos) + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ + p->cutValue = 32; + p->btMode = 1; + p->numHashBytes = 4; + /* p->skipModeBits = 0; */ + p->directInput = 0; + p->bigHash = 0; +} + +#define kCrcPoly 0xEDB88320 + +void MatchFinder_Construct(CMatchFinder *p) +{ + LZ_UInt32 i; + p->bufferBase = 0; + p->directInput = 0; + p->hash = 0; + MatchFinder_SetDefaultSettings(p); + + for (i = 0; i < 256; i++) + { + LZ_UInt32 r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + p->crc[i] = r; + } +} + +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->hash); + p->hash = 0; +} + +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +{ + MatchFinder_FreeThisClassMemory(p, alloc); + LzInWindow_Free(p, alloc); +} + +static CLzRef* AllocRefs(LZ_UInt32 num, ISzAlloc *alloc) +{ + size_t sizeInBytes = (size_t)num * sizeof(CLzRef); + if (sizeInBytes / sizeof(CLzRef) != num) + return 0; + return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, LZ_UInt32 historySize, + LZ_UInt32 keepAddBufferBefore, LZ_UInt32 matchMaxLen, LZ_UInt32 keepAddBufferAfter, + ISzAlloc *alloc) +{ + LZ_UInt32 sizeReserv; + if (historySize > kMaxHistorySize) + { + MatchFinder_Free(p, alloc); + return 0; + } + sizeReserv = historySize >> 1; + if (historySize > ((LZ_UInt32)2 << 30)) + sizeReserv = historySize >> 2; + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + + p->keepSizeBefore = historySize + keepAddBufferBefore + 1; + p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ + if (LzInWindow_Create(p, sizeReserv, alloc)) + { + LZ_UInt32 newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1; + LZ_UInt32 hs; + p->matchMaxLen = matchMaxLen; + { + p->fixedHashSize = 0; + if (p->numHashBytes == 2) + hs = (1 << 16) - 1; + else + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + /* hs >>= p->skipModeBits; */ + hs |= 0xFFFF; /* don't change it! It's required for Deflate */ + if (hs > (1 << 24)) + { + if (p->numHashBytes == 3) + hs = (1 << 24) - 1; + else + hs >>= 1; + } + } + p->hashMask = hs; + hs++; + if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; + if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; + if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; + hs += p->fixedHashSize; + } + + { + LZ_UInt32 prevSize = p->hashSizeSum + p->numSons; + LZ_UInt32 newSize; + p->historySize = historySize; + p->hashSizeSum = hs; + p->cyclicBufferSize = newCyclicBufferSize; + p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); + newSize = p->hashSizeSum + p->numSons; + if (p->hash != 0 && prevSize == newSize) + return 1; + MatchFinder_FreeThisClassMemory(p, alloc); + p->hash = AllocRefs(newSize, alloc); + if (p->hash != 0) + { + p->son = p->hash + p->hashSizeSum; + return 1; + } + } + } + MatchFinder_Free(p, alloc); + return 0; +} + +static void MatchFinder_SetLimits(CMatchFinder *p) +{ + LZ_UInt32 limit = kMaxValForNormalize - p->pos; + LZ_UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + if (limit2 < limit) + limit = limit2; + limit2 = p->streamPos - p->pos; + if (limit2 <= p->keepSizeAfter) + { + if (limit2 > 0) + limit2 = 1; + } + else + limit2 -= p->keepSizeAfter; + if (limit2 < limit) + limit = limit2; + { + LZ_UInt32 lenLimit = p->streamPos - p->pos; + if (lenLimit > p->matchMaxLen) + lenLimit = p->matchMaxLen; + p->lenLimit = lenLimit; + } + p->posLimit = p->pos + limit; +} + +void MatchFinder_Init(CMatchFinder *p) +{ + LZ_UInt32 i; + for (i = 0; i < p->hashSizeSum; i++) + p->hash[i] = kEmptyHashValue; + p->cyclicBufferPos = 0; + p->buffer = p->bufferBase; + p->pos = p->streamPos = p->cyclicBufferSize; + p->result = SZ_OK; + p->streamEndWasReached = 0; + MatchFinder_ReadBlock(p); + MatchFinder_SetLimits(p); +} + +static LZ_UInt32 MatchFinder_GetSubValue(CMatchFinder *p) +{ + return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(LZ_UInt32 subValue, CLzRef *items, LZ_UInt32 numItems) +{ + LZ_UInt32 i; + for (i = 0; i < numItems; i++) + { + LZ_UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } +} + +static void MatchFinder_Normalize(CMatchFinder *p) +{ + LZ_UInt32 subValue = MatchFinder_GetSubValue(p); + MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); + MatchFinder_ReduceOffsets(p, subValue); +} + +static void MatchFinder_CheckLimits(CMatchFinder *p) +{ + if (p->pos == kMaxValForNormalize) + MatchFinder_Normalize(p); + if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) + MatchFinder_CheckAndMoveAndRead(p); + if (p->cyclicBufferPos == p->cyclicBufferSize) + p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); +} + +static LZ_UInt32 * Hc_GetMatchesSpec(LZ_UInt32 lenLimit, LZ_UInt32 curMatch, LZ_UInt32 pos, const Byte *cur, CLzRef *son, + LZ_UInt32 _cyclicBufferPos, LZ_UInt32 _cyclicBufferSize, LZ_UInt32 cutValue, + LZ_UInt32 *distances, LZ_UInt32 maxLen) +{ + son[_cyclicBufferPos] = curMatch; + for (;;) + { + LZ_UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + return distances; + { + const Byte *pb = cur - delta; + curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + if (pb[maxLen] == cur[maxLen] && *pb == *cur) + { + LZ_UInt32 len = 0; + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + return distances; + } + } + } + } +} + +LZ_UInt32 * GetMatchesSpec1(LZ_UInt32 lenLimit, LZ_UInt32 curMatch, LZ_UInt32 pos, const Byte *cur, CLzRef *son, + LZ_UInt32 _cyclicBufferPos, LZ_UInt32 _cyclicBufferSize, LZ_UInt32 cutValue, + LZ_UInt32 *distances, LZ_UInt32 maxLen) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + LZ_UInt32 len0 = 0, len1 = 0; + for (;;) + { + LZ_UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return distances; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + LZ_UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return distances; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +static void SkipMatchesSpec(LZ_UInt32 lenLimit, LZ_UInt32 curMatch, LZ_UInt32 pos, const Byte *cur, CLzRef *son, + LZ_UInt32 _cyclicBufferPos, LZ_UInt32 _cyclicBufferSize, LZ_UInt32 cutValue) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + LZ_UInt32 len0 = 0, len1 = 0; + for (;;) + { + LZ_UInt32 delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); + const Byte *pb = cur - delta; + LZ_UInt32 len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + { + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +#define MOVE_POS \ +++p->cyclicBufferPos; \ +p->buffer++; \ +if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ +LZ_UInt32 lenLimit; LZ_UInt32 hashValue; const Byte *cur; LZ_UInt32 curMatch; \ +lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ +cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ +offset = (LZ_UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ + distances + offset, maxLen) - distances); MOVE_POS_RET; + +#define SKIP_FOOTER \ +SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; + +static LZ_UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances) +{ + LZ_UInt32 offset; + GET_MATCHES_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 1) +} + +LZ_UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances) +{ + LZ_UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 2) +} + +static LZ_UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances) +{ + LZ_UInt32 hash2Value, delta2, maxLen, offset; + GET_MATCHES_HEADER(3) + + HASH3_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + curMatch = p->hash[kFix3HashSize + hashValue]; + + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + + + maxLen = 2; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[0] = maxLen; + distances[1] = delta2 - 1; + offset = 2; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + GET_MATCHES_FOOTER(offset, maxLen) +} + +static LZ_UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances) +{ + LZ_UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + GET_MATCHES_FOOTER(offset, maxLen) +} + +static LZ_UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances) +{ + LZ_UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[ hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + offset = (LZ_UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} + +LZ_UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances) +{ + LZ_UInt32 offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = (LZ_UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances, 2) - (distances)); + MOVE_POS_RET +} + +static void Bt2_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num) +{ + do + { + SKIP_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt3_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num) +{ + do + { + LZ_UInt32 hash2Value; + SKIP_HEADER(3) + HASH3_CALC; + curMatch = p->hash[kFix3HashSize + hashValue]; + p->hash[hash2Value] = + p->hash[kFix3HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Bt4_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num) +{ + do + { + LZ_UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = p->pos; + p->hash[kFix4HashSize + hashValue] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} + +static void Hc4_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num) +{ + do + { + LZ_UInt32 hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[ hash2Value] = + p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; + vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; + if (!p->btMode) + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + else if (p->numHashBytes == 2) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; + } + else if (p->numHashBytes == 3) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; + } + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } +} diff --git a/LzFind.h b/LzFind.h new file mode 100644 index 0000000..9857b69 --- /dev/null +++ b/LzFind.h @@ -0,0 +1,107 @@ +/* LzFind.h -- Match finder for LZ algorithms + 2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZFIND_H +#define __LZFIND_H + +#include "types.h" + +typedef LZ_UInt32 CLzRef; + +typedef struct _CMatchFinder +{ + Byte *buffer; + LZ_UInt32 pos; + LZ_UInt32 posLimit; + LZ_UInt32 streamPos; + LZ_UInt32 lenLimit; + + LZ_UInt32 cyclicBufferPos; + LZ_UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + + LZ_UInt32 matchMaxLen; + CLzRef *hash; + CLzRef *son; + LZ_UInt32 hashMask; + LZ_UInt32 cutValue; + + Byte *bufferBase; + ISeqInStream *stream; + int streamEndWasReached; + + LZ_UInt32 blockSize; + LZ_UInt32 keepSizeBefore; + LZ_UInt32 keepSizeAfter; + + LZ_UInt32 numHashBytes; + int directInput; + int btMode; + /* int skipModeBits; */ + int bigHash; + LZ_UInt32 historySize; + LZ_UInt32 fixedHashSize; + LZ_UInt32 hashSizeSum; + LZ_UInt32 numSons; + SRes result; + LZ_UInt32 crc[256]; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(LZ_Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB + */ +int MatchFinder_Create(CMatchFinder *p, LZ_UInt32 historySize, + LZ_UInt32 keepAddBufferBefore, LZ_UInt32 matchMaxLen, LZ_UInt32 keepAddBufferAfter, + ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(LZ_UInt32 subValue, CLzRef *items, LZ_UInt32 numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, LZ_UInt32 subValue); + +LZ_UInt32 * GetMatchesSpec1(LZ_UInt32 lenLimit, LZ_UInt32 curMatch, LZ_UInt32 pos, const Byte *buffer, CLzRef *son, + LZ_UInt32 _cyclicBufferPos, LZ_UInt32 _cyclicBufferSize, LZ_UInt32 _cutValue, + LZ_UInt32 *distances, LZ_UInt32 maxLen); + +/* + Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function + */ + +typedef void (*Mf_Init_Func)(void *object); +typedef Byte (*Mf_GetIndexByte_Func)(void *object, LZ_Int32 index); +typedef LZ_UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef LZ_UInt32 (*Mf_GetMatches_Func)(void *object, LZ_UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, LZ_UInt32); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetIndexByte_Func GetIndexByte; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +LZ_UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances); +LZ_UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num); + +#endif diff --git a/LzHash.h b/LzHash.h new file mode 100644 index 0000000..884808f --- /dev/null +++ b/LzHash.h @@ -0,0 +1,54 @@ +/* LzHash.h -- HASH functions for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZHASH_H +#define __LZHASH_H + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hashValue = cur[0] | ((LZ_UInt32)cur[1] << 8); + +#define HASH3_CALC { \ + LZ_UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hashValue = (temp ^ ((LZ_UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ + LZ_UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((LZ_UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hashValue = (temp ^ ((LZ_UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } + +#define HASH5_CALC { \ + LZ_UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((LZ_UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((LZ_UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ + hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ + hash4Value &= (kHash4Size - 1); } + +/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((LZ_UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hashValue = ((cur[2] | ((LZ_UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + + +#define MT_HASH2_CALC \ + hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC { \ + LZ_UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((LZ_UInt32)cur[2] << 8)) & (kHash3Size - 1); } + +#define MT_HASH4_CALC { \ + LZ_UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((LZ_UInt32)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((LZ_UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } + +#endif diff --git a/LzmaDec.c b/LzmaDec.c new file mode 100644 index 0000000..855036e --- /dev/null +++ b/LzmaDec.c @@ -0,0 +1,1025 @@ +/* LzmaDec.c -- LZMA Decoder + 2008-11-06 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include + +#define kNumTopBits 24 +#define kTopValue ((LZ_UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ +{ UPDATE_0(p); i = (i + i); A0; } else \ +{ UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ +{ i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ +{ i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ +{ UPDATE_0_CHECK; i = (i + i); A0; } else \ +{ UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ +{ i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((LZ_UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +const char *LZMA_ERRORS[] = { + "SZ_OK", + "SZ_ERROR_DATA", + "SZ_ERROR_MEM", + "SZ_ERROR_CRC", + "SZ_ERROR_UNSUPPORTED", + "SZ_ERROR_PARAM", + "SZ_ERROR_INPUT_EOF", + "SZ_ERROR_OUTPUT_EOF", + "SZ_ERROR_READ", + "SZ_ERROR_WRITE", + "SZ_ERROR_PROGRESS", + "SZ_ERROR_FAIL", + "SZ_ERROR_THREAD", + "n/a", "n/a", "n/a", + "SZ_ERROR_ARCHIVE", + "SZ_ERROR_NO_ARCHIVE"}; + +static const Byte kLiteralNextStates[kNumStates * 2] = +{ + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5, + 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 +}; + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. + And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization + Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker + */ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + LZ_UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + LZ_UInt32 processedPos = p->processedPos; + LZ_UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + LZ_UInt32 range = p->range; + LZ_UInt32 code = p->code; + + do + { + CLzmaProb *prob; + LZ_UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + symbol = 1; + do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + dic[dicPos++] = (Byte)symbol; + processedPos++; + + state = kLiteralNextStates[state]; + /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */ + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + LZ_UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + LZ_UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + LZ_UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + LZ_UInt32 t; + code -= range; + t = (0 - ((LZ_UInt32)code >> 31)); /* (LZ_UInt32)((LZ_Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (LZ_UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + /* state = kLiteralNextStates[state]; */ + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + SizeT rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + LZ_UInt32 rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + LZ_UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + LZ_UInt32 range = p->range; + LZ_UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + LZ_UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ + p->code = ((LZ_UInt32)data[1] << 24) | ((LZ_UInt32)data[2] << 16) | ((LZ_UInt32)data[3] << 8) | ((LZ_UInt32)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, int initDic, int initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + LZ_UInt32 numProbs = Literal + ((LZ_UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + LZ_UInt32 i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT outSize = *destLen; + SizeT inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + SizeT inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ + LzmaDec_FreeProbs(p, alloc); + LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + LZ_UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((LZ_UInt32)data[2] << 8) | ((LZ_UInt32)data[3] << 16) | ((LZ_UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + LZ_UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + SizeT dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p, alloc); + p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p, alloc); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc) +{ + CLzmaDec p; + SRes res; + SizeT inSize = *srcLen; + SizeT outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p, alloc); + return res; +} diff --git a/LzmaDec.h b/LzmaDec.h new file mode 100644 index 0000000..da78480 --- /dev/null +++ b/LzmaDec.h @@ -0,0 +1,223 @@ +/* LzmaDec.h -- LZMA Decoder + 2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMADEC_H +#define __LZMADEC_H + +#include "types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb LZ_UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + LZ_UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties + Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties + */ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + LZ_UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + LZ_UInt32 processedPos; + LZ_UInt32 checkDicSize; + unsigned state; + LZ_UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + LZ_UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + + LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + */ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() + */ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + + finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + + Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + */ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + + finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + */ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + + finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + + Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). + */ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#endif diff --git a/LzmaEnc.c b/LzmaEnc.c new file mode 100644 index 0000000..eb6b2d7 --- /dev/null +++ b/LzmaEnc.c @@ -0,0 +1,2281 @@ +/* LzmaEnc.c -- LZMA Encoder + 2009-02-02 : Igor Pavlov : Public domain */ + +#include + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifdef COMPRESS_MF_MT +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((LZ_UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ + p->level = 5; + p->dictSize = p->mc = 0; + p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; + p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ + int level = p->level; + if (level < 0) level = 5; + p->level = level; + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->lc < 0) p->lc = 3; + if (p->lp < 0) p->lp = 0; + if (p->pb < 0) p->pb = 2; + if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); + if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); + if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); + if (p->numHashBytes < 0) p->numHashBytes = 4; + if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numThreads < 0) + p->numThreads = +#ifdef COMPRESS_MF_MT + ((p->btMode && p->algo) ? 2 : 1); +#else + 1; +#endif +} + +LZ_UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + return props.dictSize; +} + +/* #define LZMA_LOG_BSR */ +/* Define it for Intel's CPU */ + + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 30 + +#define BSR2_RET(pos, res) { uint32_t i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } + +LZ_UInt32 GetPosSlot1(LZ_UInt32 pos) +{ + LZ_UInt32 res; + BSR2_RET(pos, res); + return res; +} +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } + +#else + +#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +void LzmaEnc_FastPosInit(Byte *g_FastPos) +{ + int c = 2, slotFast; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + + for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) + { + LZ_UInt32 k = (1 << ((slotFast >> 1) - 1)); + LZ_UInt32 j; + for (j = 0; j < k; j++, c++) + g_FastPos[c] = (Byte)slotFast; + } +} + +#define BSR2_RET(pos, res) { LZ_UInt32 i = 6 + ((kNumLogBits - 1) & \ +(0 - (((((LZ_UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ +res = p->g_FastPos[pos >> i] + (i * 2); } +/* + #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ + p->g_FastPos[pos >> 6] + 12 : \ + p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } + */ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } + +#endif + + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct _COptimal +{ + LZ_UInt32 price; + + CState state; + int prev1IsChar; + int prev2; + + LZ_UInt32 posPrev2; + LZ_UInt32 backPrev2; + + LZ_UInt32 posPrev; + LZ_UInt32 backPrev; + LZ_UInt32 backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex / 2)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb LZ_UInt32 +#else +#define CLzmaProb UInt16 +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + +typedef struct +{ + CLzmaProb choice; + CLzmaProb choice2; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; + CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + +typedef struct +{ + CLenEnc p; + LZ_UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; + LZ_UInt32 tableSize; + LZ_UInt32 counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + +typedef struct _CRangeEnc +{ + LZ_UInt32 range; + Byte cache; + LZ_UInt64 low; + LZ_UInt64 cacheSize; + Byte *buf; + Byte *bufLim; + Byte *bufBase; + ISeqOutStream *outStream; + LZ_UInt64 processed; + SRes res; +} CRangeEnc; + +typedef struct _CSeqInStreamBuf +{ + ISeqInStream funcTable; + const Byte *data; + SizeT rem; +} CSeqInStreamBuf; + +static SRes MyRead(void *pp, void *data, size_t *size) +{ + size_t curSize = *size; + CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp; + if (p->rem < curSize) + curSize = p->rem; + memcpy(data, p->data, curSize); + p->rem -= curSize; + p->data += curSize; + *size = curSize; + return SZ_OK; +} + +typedef struct +{ + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + LZ_UInt32 reps[LZMA_NUM_REPS]; + LZ_UInt32 state; +} CSaveState; + +typedef struct _CLzmaEnc +{ + IMatchFinder matchFinder; + void *matchFinderObj; + +#ifdef COMPRESS_MF_MT + int mtMode; + CMatchFinderMt matchFinderMt; +#endif + + CMatchFinder matchFinderBase; + +#ifdef COMPRESS_MF_MT + Byte pad[128]; +#endif + + LZ_UInt32 optimumEndIndex; + LZ_UInt32 optimumCurrentIndex; + + LZ_UInt32 longestMatchLength; + LZ_UInt32 numPairs; + LZ_UInt32 numAvail; + COptimal opt[kNumOpts]; + +#ifndef LZMA_LOG_BSR + Byte g_FastPos[1 << kNumLogBits]; +#endif + + LZ_UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + LZ_UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + LZ_UInt32 numFastBytes; + LZ_UInt32 additionalOffset; + LZ_UInt32 reps[LZMA_NUM_REPS]; + LZ_UInt32 state; + + LZ_UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + LZ_UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; + LZ_UInt32 alignPrices[kAlignTableSize]; + LZ_UInt32 alignPriceCount; + + LZ_UInt32 distTableSize; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + unsigned lclp; + + int fastMode; + + CRangeEnc rc; + + int writeEndMark; + LZ_UInt64 nowPos64; + LZ_UInt32 matchPriceCount; + int finished; + int multiThread; + + SRes result; + LZ_UInt32 dictSize; + LZ_UInt32 matchFinderCycles; + + ISeqInStream *inStream; + CSeqInStreamBuf seqBufInStream; + + CSaveState saveState; +} CLzmaEnc; + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CSaveState *dest = &p->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ + CLzmaEnc *dest = (CLzmaEnc *)pp; + const CSaveState *p = &dest->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + + if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || + props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) + return SZ_ERROR_PARAM; + p->dictSize = props.dictSize; + p->matchFinderCycles = props.mc; + { + unsigned fb = props.fb; + if (fb < 5) + fb = 5; + if (fb > LZMA_MATCH_LEN_MAX) + fb = LZMA_MATCH_LEN_MAX; + p->numFastBytes = fb; + } + p->lc = props.lc; + p->lp = props.lp; + p->pb = props.pb; + p->fastMode = (props.algo == 0); + p->matchFinderBase.btMode = props.btMode; + { + LZ_UInt32 numHashBytes = 4; + if (props.btMode) + { + if (props.numHashBytes < 2) + numHashBytes = 2; + else if (props.numHashBytes < 4) + numHashBytes = props.numHashBytes; + } + p->matchFinderBase.numHashBytes = numHashBytes; + } + + p->matchFinderBase.cutValue = props.mc; + + p->writeEndMark = props.writeEndMark; + +#ifdef COMPRESS_MF_MT + /* + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + */ + p->multiThread = (props.numThreads > 1); +#endif + + return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ + p->outStream = 0; + p->bufBase = 0; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) +{ + if (p->bufBase == 0) + { + p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); + if (p->bufBase == 0) + return 0; + p->bufLim = p->bufBase + RC_BUF_SIZE; + } + return 1; +} + +static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->bufBase); + p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ + /* Stream.Init(); */ + p->low = 0; + p->range = 0xFFFFFFFF; + p->cacheSize = 1; + p->cache = 0; + + p->buf = p->bufBase; + + p->processed = 0; + p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ + size_t num; + if (p->res != SZ_OK) + return; + num = p->buf - p->bufBase; + if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + p->processed += num; + p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ + if ((LZ_UInt32)p->low < (LZ_UInt32)0xFF000000 || (int)(p->low >> 32) != 0) + { + Byte temp = p->cache; + do + { + Byte *buf = p->buf; + *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + temp = 0xFF; + } + while (--p->cacheSize != 0); + p->cache = (Byte)((LZ_UInt32)p->low >> 24); + } + p->cacheSize++; + p->low = (LZ_UInt32)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ + int i; + for (i = 0; i < 5; i++) + RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, LZ_UInt32 value, int numBits) +{ + do + { + p->range >>= 1; + p->low += p->range & (0 - ((value >> --numBits) & 1)); + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } + } + while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, LZ_UInt32 symbol) +{ + LZ_UInt32 ttt = *prob; + LZ_UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; + if (symbol == 0) + { + p->range = newBound; + ttt += (kBitModelTotal - ttt) >> kNumMoveBits; + } + else + { + p->low += newBound; + p->range -= newBound; + ttt -= ttt >> kNumMoveBits; + } + *prob = (CLzmaProb)ttt; + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, LZ_UInt32 symbol) +{ + symbol |= 0x100; + do + { + RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, LZ_UInt32 symbol, LZ_UInt32 matchByte) +{ + LZ_UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); +} + +void LzmaEnc_InitPriceTables(LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) + { + const int kCyclesBits = kNumBitPriceShiftBits; + LZ_UInt32 w = i; + LZ_UInt32 bitCount = 0; + int j; + for (j = 0; j < kCyclesBits; j++) + { + w = w * w; + bitCount <<= 1; + while (w >= ((LZ_UInt32)1 << 16)) + { + w >>= 1; + bitCount++; + } + } + ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + } +} + + +#define GET_PRICE(prob, symbol) \ +p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ +ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static LZ_UInt32 LitEnc_GetPrice(const CLzmaProb *probs, LZ_UInt32 symbol, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + symbol |= 0x100; + do + { + price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); + return price; +} + +static LZ_UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, LZ_UInt32 symbol, LZ_UInt32 matchByte, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + LZ_UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); + return price; +} + + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol) +{ + LZ_UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0;) + { + LZ_UInt32 bit; + i--; + bit = (symbol >> i) & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol) +{ + LZ_UInt32 m = 1; + int i; + for (i = 0; i < numBitLevels; i++) + { + LZ_UInt32 bit = symbol & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } +} + +static LZ_UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + symbol |= (1 << numBitLevels); + while (symbol != 1) + { + price += GET_PRICEa(probs[symbol >> 1], symbol & 1); + symbol >>= 1; + } + return price; +} + +static LZ_UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + LZ_UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0; i--) + { + LZ_UInt32 bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) | bit; + } + return price; +} + + +static void LenEnc_Init(CLenEnc *p) +{ + unsigned i; + p->choice = p->choice2 = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + p->low[i] = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) + p->mid[i] = kProbInitValue; + for (i = 0; i < kLenNumHighSymbols; i++) + p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, LZ_UInt32 symbol, LZ_UInt32 posState) +{ + if (symbol < kLenNumLowSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice, 0); + RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice, 1); + if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice2, 0); + RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice2, 1); + RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); + } + } +} + +static void LenEnc_SetPrices(CLenEnc *p, LZ_UInt32 posState, LZ_UInt32 numSymbols, LZ_UInt32 *prices, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 a0 = GET_PRICE_0a(p->choice); + LZ_UInt32 a1 = GET_PRICE_1a(p->choice); + LZ_UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); + LZ_UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); + LZ_UInt32 i = 0; + for (i = 0; i < kLenNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); + } + for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); + } + for (; i < numSymbols; i++) + prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, LZ_UInt32 posState, LZ_UInt32 *ProbPrices) +{ + LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); + p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, LZ_UInt32 numPosStates, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, LZ_UInt32 symbol, LZ_UInt32 posState, int updatePrice, LZ_UInt32 *ProbPrices) +{ + LenEnc_Encode(&p->p, rc, symbol, posState); + if (updatePrice) + if (--p->counters[posState] == 0) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + + + + +static void MovePos(CLzmaEnc *p, LZ_UInt32 num) +{ +#ifdef SHOW_STAT + ttt += num; + printf("\n MovePos %d", num); +#endif + if (num != 0) + { + p->additionalOffset += num; + p->matchFinder.Skip(p->matchFinderObj, num); + } +} + +static LZ_UInt32 ReadMatchDistances(CLzmaEnc *p, LZ_UInt32 *numDistancePairsRes) +{ + LZ_UInt32 lenRes = 0, numPairs; + p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); + numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); +#ifdef SHOW_STAT + printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); + ttt++; + { + LZ_UInt32 i; + for (i = 0; i < numPairs; i += 2) + printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); + } +#endif + if (numPairs > 0) + { + lenRes = p->matches[numPairs - 2]; + if (lenRes == p->numFastBytes) + { + const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + LZ_UInt32 distance = p->matches[numPairs - 1] + 1; + LZ_UInt32 numAvail = p->numAvail; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + { + const Byte *pby2 = pby - distance; + for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); + } + } + } + p->additionalOffset++; + *numDistancePairsRes = numPairs; + return lenRes; +} + + +#define MakeAsChar(p) (p)->backPrev = (LZ_UInt32)(-1); (p)->prev1IsChar = False; +#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static LZ_UInt32 GetRepLen1Price(CLzmaEnc *p, LZ_UInt32 state, LZ_UInt32 posState) +{ + return + GET_PRICE_0(p->isRepG0[state]) + + GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static LZ_UInt32 GetPureRepPrice(CLzmaEnc *p, LZ_UInt32 repIndex, LZ_UInt32 state, LZ_UInt32 posState) +{ + LZ_UInt32 price; + if (repIndex == 0) + { + price = GET_PRICE_0(p->isRepG0[state]); + price += GET_PRICE_1(p->isRep0Long[state][posState]); + } + else + { + price = GET_PRICE_1(p->isRepG0[state]); + if (repIndex == 1) + price += GET_PRICE_0(p->isRepG1[state]); + else + { + price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE(p->isRepG2[state], repIndex - 2); + } + } + return price; +} + +static LZ_UInt32 GetRepPrice(CLzmaEnc *p, LZ_UInt32 repIndex, LZ_UInt32 len, LZ_UInt32 state, LZ_UInt32 posState) +{ + return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + + GetPureRepPrice(p, repIndex, state, posState); +} + +static LZ_UInt32 Backward(CLzmaEnc *p, LZ_UInt32 *backRes, LZ_UInt32 cur) +{ + LZ_UInt32 posMem = p->opt[cur].posPrev; + LZ_UInt32 backMem = p->opt[cur].backPrev; + p->optimumEndIndex = cur; + do + { + if (p->opt[cur].prev1IsChar) + { + MakeAsChar(&p->opt[posMem]) + p->opt[posMem].posPrev = posMem - 1; + if (p->opt[cur].prev2) + { + p->opt[posMem - 1].prev1IsChar = False; + p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; + p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + } + } + { + LZ_UInt32 posPrev = posMem; + LZ_UInt32 backCur = backMem; + + backMem = p->opt[posPrev].backPrev; + posMem = p->opt[posPrev].posPrev; + + p->opt[posPrev].backPrev = backCur; + p->opt[posPrev].posPrev = cur; + cur = posPrev; + } + } + while (cur != 0); + *backRes = p->opt[0].backPrev; + p->optimumCurrentIndex = p->opt[0].posPrev; + return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) + +static LZ_UInt32 GetOptimum(CLzmaEnc *p, LZ_UInt32 position, LZ_UInt32 *backRes) +{ + LZ_UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; + LZ_UInt32 matchPrice, repMatchPrice, normalMatchPrice; + LZ_UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + LZ_UInt32 *matches; + const Byte *data; + Byte curByte, matchByte; + if (p->optimumEndIndex != p->optimumCurrentIndex) + { + const COptimal *opt = &p->opt[p->optimumCurrentIndex]; + LZ_UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; + *backRes = opt->backPrev; + p->optimumCurrentIndex = opt->posPrev; + return lenRes; + } + p->optimumCurrentIndex = p->optimumEndIndex = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + if (numAvail < 2) + { + *backRes = (LZ_UInt32)(-1); + return 1; + } + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 lenTest; + const Byte *data2; + reps[i] = p->reps[i]; + data2 = data - (reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= p->numFastBytes) + { + LZ_UInt32 lenRes; + *backRes = repMaxIndex; + lenRes = repLens[repMaxIndex]; + MovePos(p, lenRes - 1); + return lenRes; + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + *backRes = (LZ_UInt32)-1; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsCharState(p->state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAsChar(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + LZ_UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAsShortRep(&p->opt[1]); + } + } + lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + *backRes = p->opt[1].backPrev; + return 1; + } + + p->opt[1].posPrev = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->opt[0].backs[i] = reps[i]; + + len = lenEnd; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 repLen = repLens[i]; + LZ_UInt32 price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); + do + { + LZ_UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = i; + opt->prev1IsChar = False; + } + } + while (--repLen >= 2); + } + + normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) + { + LZ_UInt32 offs = 0; + while (len > matches[offs]) + offs += 2; + for (; ; len++) + { + COptimal *opt; + LZ_UInt32 distance = matches[offs + 1]; + + LZ_UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; + LZ_UInt32 lenToPosState = GetLenToPosState(len); + if (distance < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][distance]; + else + { + LZ_UInt32 slot; + GetPosSlot2(distance, slot); + curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; + } + opt = &p->opt[len]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = distance + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } + } + + cur = 0; + +#ifdef SHOW_STAT2 + if (position >= 0) + { + unsigned i; + printf("\n pos = %4X", position); + for (i = cur; i <= lenEnd; i++) + printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); + } +#endif + + for (;;) + { + LZ_UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; + LZ_UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; + int nextIsChar; + Byte curByte, matchByte; + const Byte *data; + COptimal *curOpt; + COptimal *nextOpt; + + cur++; + if (cur == lenEnd) + return Backward(p, backRes, cur); + + newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) + { + p->numPairs = numPairs; + p->longestMatchLength = newLen; + return Backward(p, backRes, cur); + } + position++; + curOpt = &p->opt[cur]; + posPrev = curOpt->posPrev; + if (curOpt->prev1IsChar) + { + posPrev--; + if (curOpt->prev2) + { + state = p->opt[curOpt->posPrev2].state; + if (curOpt->backPrev2 < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + else + state = p->opt[posPrev].state; + state = kLiteralNextStates[state]; + } + else + state = p->opt[posPrev].state; + if (posPrev == cur - 1) + { + if (IsShortRep(curOpt)) + state = kShortRepNextStates[state]; + else + state = kLiteralNextStates[state]; + } + else + { + LZ_UInt32 pos; + const COptimal *prevOpt; + if (curOpt->prev1IsChar && curOpt->prev2) + { + posPrev = curOpt->posPrev2; + pos = curOpt->backPrev2; + state = kRepNextStates[state]; + } + else + { + pos = curOpt->backPrev; + if (pos < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + prevOpt = &p->opt[posPrev]; + if (pos < LZMA_NUM_REPS) + { + LZ_UInt32 i; + reps[0] = prevOpt->backs[pos]; + for (i = 1; i <= pos; i++) + reps[i] = prevOpt->backs[i - 1]; + for (; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i]; + } + else + { + LZ_UInt32 i; + reps[0] = (pos - LZMA_NUM_REPS); + for (i = 1; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i - 1]; + } + } + curOpt->state = (CState)state; + + curOpt->backs[0] = reps[0]; + curOpt->backs[1] = reps[1]; + curOpt->backs[2] = reps[2]; + curOpt->backs[3] = reps[3]; + + curPrice = curOpt->price; + nextIsChar = False; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + posState = (position & p->pbMask); + + curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + curAnd1Price += + (!IsCharState(state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + nextOpt = &p->opt[cur + 1]; + + if (curAnd1Price < nextOpt->price) + { + nextOpt->price = curAnd1Price; + nextOpt->posPrev = cur; + MakeAsChar(nextOpt); + nextIsChar = True; + } + + matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); + + if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) + { + LZ_UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); + if (shortRepPrice <= nextOpt->price) + { + nextOpt->price = shortRepPrice; + nextOpt->posPrev = cur; + MakeAsShortRep(nextOpt); + nextIsChar = True; + } + } + numAvailFull = p->numAvail; + { + LZ_UInt32 temp = kNumOpts - 1 - cur; + if (temp < numAvailFull) + numAvailFull = temp; + } + + if (numAvailFull < 2) + continue; + numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + + if (!nextIsChar && matchByte != curByte) /* speed optimization */ + { + /* try Literal + rep0 */ + LZ_UInt32 temp; + LZ_UInt32 lenTest2; + const Byte *data2 = data - (reps[0] + 1); + LZ_UInt32 limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); + lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + LZ_UInt32 state2 = kLiteralNextStates[state]; + LZ_UInt32 posStateNext = (position + 1) & p->pbMask; + LZ_UInt32 nextRepMatchPrice = curAnd1Price + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + /* for (; lenTest2 >= 2; lenTest2--) */ + { + LZ_UInt32 curAndLenPrice; + COptimal *opt; + LZ_UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = False; + } + } + } + } + + startLen = 2; /* speed optimization */ + { + LZ_UInt32 repIndex; + for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) + { + LZ_UInt32 lenTest; + LZ_UInt32 lenTestTemp; + LZ_UInt32 price; + const Byte *data2 = data - (reps[repIndex] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + while (lenEnd < cur + lenTest) + p->opt[++lenEnd].price = kInfinityPrice; + lenTestTemp = lenTest; + price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); + do + { + LZ_UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; + COptimal *opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = repIndex; + opt->prev1IsChar = False; + } + } + while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + /* if (_maxMode) */ + { + LZ_UInt32 lenTest2 = lenTest + 1; + LZ_UInt32 limit = lenTest2 + p->numFastBytes; + LZ_UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + LZ_UInt32 state2 = kRepNextStates[state]; + LZ_UInt32 posStateNext = (position + lenTest) & p->pbMask; + LZ_UInt32 curAndLenCharPrice = + price + p->repLenEnc.prices[posState][lenTest - 2] + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (position + lenTest + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + LZ_UInt32 curAndLenPrice; + COptimal *opt; + LZ_UInt32 offset = cur + lenTest + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = repIndex; + } + } + } + } + } + } + /* for (LZ_UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ + if (newLen > numAvail) + { + newLen = numAvail; + for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); + matches[numPairs] = newLen; + numPairs += 2; + } + if (newLen >= startLen) + { + LZ_UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); + LZ_UInt32 offs, curBack, posSlot; + LZ_UInt32 lenTest; + while (lenEnd < cur + newLen) + p->opt[++lenEnd].price = kInfinityPrice; + + offs = 0; + while (startLen > matches[offs]) + offs += 2; + curBack = matches[offs + 1]; + GetPosSlot2(curBack, posSlot); + for (lenTest = /*2*/ startLen; ; lenTest++) + { + LZ_UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; + LZ_UInt32 lenToPosState = GetLenToPosState(lenTest); + COptimal *opt; + if (curBack < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; + + opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = curBack + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + + if (/*_maxMode && */lenTest == matches[offs]) + { + /* Try Match + Literal + Rep0 */ + const Byte *data2 = data - (curBack + 1); + LZ_UInt32 lenTest2 = lenTest + 1; + LZ_UInt32 limit = lenTest2 + p->numFastBytes; + LZ_UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + LZ_UInt32 state2 = kMatchNextStates[state]; + LZ_UInt32 posStateNext = (position + lenTest) & p->pbMask; + LZ_UInt32 curAndLenCharPrice = curAndLenPrice + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (posStateNext + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + LZ_UInt32 offset = cur + lenTest + 1 + lenTest2; + LZ_UInt32 curAndLenPrice; + COptimal *opt; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = curBack + LZMA_NUM_REPS; + } + } + } + offs += 2; + if (offs == numPairs) + break; + curBack = matches[offs + 1]; + if (curBack >= kNumFullDistances) + GetPosSlot2(curBack, posSlot); + } + } + } + } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static LZ_UInt32 GetOptimumFast(CLzmaEnc *p, LZ_UInt32 *backRes) +{ + LZ_UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + const Byte *data; + const LZ_UInt32 *matches; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + *backRes = (LZ_UInt32)-1; + if (numAvail < 2) + return 1; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + + repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 len; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + if (len >= p->numFastBytes) + { + *backRes = i; + MovePos(p, len - 1); + return len; + } + if (len > repLen) + { + repIndex = i; + repLen = len; + } + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + + mainDist = 0; /* for GCC */ + if (mainLen >= 2) + { + mainDist = matches[numPairs - 1]; + while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + { + if (!ChangePair(matches[numPairs - 3], mainDist)) + break; + numPairs -= 2; + mainLen = matches[numPairs - 2]; + mainDist = matches[numPairs - 1]; + } + if (mainLen == 2 && mainDist >= 0x80) + mainLen = 1; + } + + if (repLen >= 2 && ( + (repLen + 1 >= mainLen) || + (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || + (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + { + *backRes = repIndex; + MovePos(p, repLen - 1); + return repLen; + } + + if (mainLen < 2 || numAvail <= 2) + return 1; + + p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); + if (p->longestMatchLength >= 2) + { + LZ_UInt32 newDistance = matches[p->numPairs - 1]; + if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || + (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || + (p->longestMatchLength > mainLen + 1) || + (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) + return 1; + } + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 len, limit; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + limit = mainLen - 1; + for (len = 2; len < limit && data[len] == data2[len]; len++); + if (len >= limit) + return 1; + } + *backRes = mainDist + LZMA_NUM_REPS; + MovePos(p, mainLen - 2); + return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, LZ_UInt32 posState) +{ + LZ_UInt32 len; + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + len = LZMA_MATCH_LEN_MIN; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); + RangeEnc_EncodeDirectBits(&p->rc, (((LZ_UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ + if (p->result != SZ_OK) + return p->result; + if (p->rc.res != SZ_OK) + p->result = SZ_ERROR_WRITE; + if (p->matchFinderBase.result != SZ_OK) + p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) + p->finished = True; + return p->result; +} + +static SRes Flush(CLzmaEnc *p, LZ_UInt32 nowPos) +{ + /* ReleaseMFStream(); */ + p->finished = True; + if (p->writeEndMark) + WriteEndMarker(p, nowPos & p->pbMask); + RangeEnc_FlushData(&p->rc); + RangeEnc_FlushStream(&p->rc); + return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ + LZ_UInt32 i; + for (i = 0; i < kAlignTableSize; i++) + p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ + LZ_UInt32 tempPrices[kNumFullDistances]; + LZ_UInt32 i, lenToPosState; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + LZ_UInt32 posSlot = GetPosSlot1(i); + LZ_UInt32 footerBits = ((posSlot >> 1) - 1); + LZ_UInt32 base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); + } + + for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + LZ_UInt32 posSlot; + const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; + LZ_UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + + { + LZ_UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; + LZ_UInt32 i; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + } + } + p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); +#ifdef COMPRESS_MF_MT + MatchFinderMt_Construct(&p->matchFinderMt); + p->matchFinderMt.MatchFinder = &p->matchFinderBase; +#endif + + { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + LzmaEnc_SetProps(p, &props); + } + +#ifndef LZMA_LOG_BSR + LzmaEnc_FastPosInit(p->g_FastPos); +#endif + + LzmaEnc_InitPriceTables(p->ProbPrices); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +{ + void *p; + p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); + if (p != 0) + LzmaEnc_Construct((CLzmaEnc *)p); + return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->litProbs); + alloc->Free(alloc, p->saveState.litProbs); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +#ifdef COMPRESS_MF_MT + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); +#endif + MatchFinder_Free(&p->matchFinderBase, allocBig); + LzmaEnc_FreeLits(p, alloc); + RangeEnc_Free(&p->rc, alloc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); + alloc->Free(alloc, p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, int useLimits, LZ_UInt32 maxPackSize, LZ_UInt32 maxUnpackSize) +{ + LZ_UInt32 nowPos32, startPos32; + if (p->inStream != 0) + { + p->matchFinderBase.stream = p->inStream; + p->matchFinder.Init(p->matchFinderObj); + p->inStream = 0; + } + + if (p->finished) + return p->result; + RINOK(CheckErrors(p)); + + nowPos32 = (LZ_UInt32)p->nowPos64; + startPos32 = nowPos32; + + if (p->nowPos64 == 0) + { + LZ_UInt32 numPairs; + Byte curByte; + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + return Flush(p, nowPos32); + ReadMatchDistances(p, &numPairs); + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); + p->state = kLiteralNextStates[p->state]; + curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); + LitEnc_Encode(&p->rc, p->litProbs, curByte); + p->additionalOffset--; + nowPos32++; + } + + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) + { + LZ_UInt32 pos, len, posState; + + if (p->fastMode) + len = GetOptimumFast(p, &pos); + else + len = GetOptimum(p, nowPos32, &pos); + +#ifdef SHOW_STAT2 + printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); +#endif + + posState = nowPos32 & p->pbMask; + if (len == 1 && pos == (LZ_UInt32)-1) + { + Byte curByte; + CLzmaProb *probs; + const Byte *data; + + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; + curByte = *data; + probs = LIT_PROBS(nowPos32, *(data - 1)); + if (IsCharState(p->state)) + LitEnc_Encode(&p->rc, probs, curByte); + else + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); + p->state = kLiteralNextStates[p->state]; + } + else + { + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + if (pos < LZMA_NUM_REPS) + { + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); + if (pos == 0) + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); + RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); + } + else + { + LZ_UInt32 distance = p->reps[pos]; + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); + if (pos == 1) + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + else + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); + if (pos == 3) + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + } + p->reps[1] = p->reps[0]; + p->reps[0] = distance; + } + if (len == 1) + p->state = kShortRepNextStates[p->state]; + else + { + LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + p->state = kRepNextStates[p->state]; + } + } + else + { + LZ_UInt32 posSlot; + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + pos -= LZMA_NUM_REPS; + GetPosSlot(pos, posSlot); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + LZ_UInt32 footerBits = ((posSlot >> 1) - 1); + LZ_UInt32 base = ((2 | (posSlot & 1)) << footerBits); + LZ_UInt32 posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); + else + { + RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); + p->alignPriceCount++; + } + } + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = pos; + p->matchPriceCount++; + } + } + p->additionalOffset -= len; + nowPos32 += len; + if (p->additionalOffset == 0) + { + LZ_UInt32 processed; + if (!p->fastMode) + { + if (p->matchPriceCount >= (1 << 7)) + FillDistancesPrices(p); + if (p->alignPriceCount >= kAlignTableSize) + FillAlignPrices(p); + } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + break; + processed = nowPos32 - startPos32; + if (useLimits) + { + if (processed + kNumOpts + 300 >= maxUnpackSize || + RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + break; + } + else if (processed >= (1 << 15)) + { + p->nowPos64 += nowPos32 - startPos32; + return CheckErrors(p); + } + } + } + p->nowPos64 += nowPos32 - startPos32; + return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((LZ_UInt32)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, LZ_UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LZ_UInt32 beforeSize = kNumOpts; + //int btMode; + if (!RangeEnc_Alloc(&p->rc, alloc)) + return SZ_ERROR_MEM; + //btMode = (p->matchFinderBase.btMode != 0); +#ifdef COMPRESS_MF_MT + p->mtMode = (p->multiThread && !p->fastMode && btMode); +#endif + + { + unsigned lclp = p->lc + p->lp; + if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) + { + LzmaEnc_FreeLits(p, alloc); + p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + if (p->litProbs == 0 || p->saveState.litProbs == 0) + { + LzmaEnc_FreeLits(p, alloc); + return SZ_ERROR_MEM; + } + p->lclp = lclp; + } + } + + p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + + if (beforeSize + p->dictSize < keepWindowSize) + beforeSize = keepWindowSize - p->dictSize; + +#ifdef COMPRESS_MF_MT + if (p->mtMode) + { + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); + p->matchFinderObj = &p->matchFinderMt; + MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); + } + else +#endif + { + if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) + return SZ_ERROR_MEM; + p->matchFinderObj = &p->matchFinderBase; + MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + } + return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ + LZ_UInt32 i; + p->state = 0; + for (i = 0 ; i < LZMA_NUM_REPS; i++) + p->reps[i] = 0; + + RangeEnc_Init(&p->rc); + + + for (i = 0; i < kNumStates; i++) + { + LZ_UInt32 j; + for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) + { + p->isMatch[i][j] = kProbInitValue; + p->isRep0Long[i][j] = kProbInitValue; + } + p->isRep[i] = kProbInitValue; + p->isRepG0[i] = kProbInitValue; + p->isRepG1[i] = kProbInitValue; + p->isRepG2[i] = kProbInitValue; + } + + { + LZ_UInt32 num = 0x300 << (p->lp + p->lc); + for (i = 0; i < num; i++) + p->litProbs[i] = kProbInitValue; + } + + { + for (i = 0; i < kNumLenToPosStates; i++) + { + CLzmaProb *probs = p->posSlotEncoder[i]; + LZ_UInt32 j; + for (j = 0; j < (1 << kNumPosSlotBits); j++) + probs[j] = kProbInitValue; + } + } + { + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + p->posEncoders[i] = kProbInitValue; + } + + LenEnc_Init(&p->lenEnc.p); + LenEnc_Init(&p->repLenEnc.p); + + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; + + p->optimumEndIndex = 0; + p->optimumCurrentIndex = 0; + p->additionalOffset = 0; + + p->pbMask = (1 << p->pb) - 1; + p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ + if (!p->fastMode) + { + FillDistancesPrices(p); + FillAlignPrices(p); + } + + p->lenEnc.tableSize = + p->repLenEnc.tableSize = + p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, LZ_UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LZ_UInt32 i; + for (i = 0; i < (LZ_UInt32)kDicLogSizeMaxCompress; i++) + if (p->dictSize <= ((LZ_UInt32)1 << i)) + break; + p->distTableSize = i * 2; + + p->finished = False; + p->result = SZ_OK; + RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + p->nowPos64 = 0; + return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + p->rc.outStream = outStream; + return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, + ISeqInStream *inStream, LZ_UInt32 keepWindowSize, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) +{ + p->seqBufInStream.funcTable.Read = MyRead; + p->seqBufInStream.data = src; + p->seqBufInStream.rem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + LZ_UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LzmaEnc_SetInputBuf(p, src, srcLen); + p->inStream = &p->seqBufInStream.funcTable; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ +#ifdef COMPRESS_MF_MT + CLzmaEnc *p = (CLzmaEnc *)pp; + if (p->mtMode) + MatchFinderMt_ReleaseStream(&p->matchFinderMt); +#else + return; //pp = pp; +#endif +} + +typedef struct _CSeqOutStreamBuf +{ + ISeqOutStream funcTable; + Byte *data; + SizeT rem; + int overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ + CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + if (p->rem < size) + { + size = p->rem; + p->overflow = True; + } + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + return size; +} + + +LZ_UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, int reInit, + Byte *dest, size_t *destLen, LZ_UInt32 desiredPackSize, LZ_UInt32 *unpackSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LZ_UInt64 nowPos64; + SRes res; + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = False; + p->finished = False; + p->result = SZ_OK; + + if (reInit) + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; + RangeEnc_Init(&p->rc); + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + + *unpackSize = (LZ_UInt32)(p->nowPos64 - nowPos64); + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + + return res; +} + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + SRes res = SZ_OK; + +#ifdef COMPRESS_MF_MT + Byte allocaDummy[0x300]; + int i = 0; + for (i = 0; i < 16; i++) + allocaDummy[i] = (Byte)i; +#endif + + RINOK(LzmaEnc_Prepare(pp, inStream, outStream, alloc, allocBig)); + + for (;;) + { + res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + if (res != SZ_OK || p->finished != 0) + break; + if (progress != 0) + { + res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + if (res != SZ_OK) + { + res = SZ_ERROR_PROGRESS; + break; + } + } + } + LzmaEnc_Finish(pp); + return res; +} + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + int i; + LZ_UInt32 dictSize = p->dictSize; + if (*size < LZMA_PROPS_SIZE) + return SZ_ERROR_PARAM; + *size = LZMA_PROPS_SIZE; + props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + + for (i = 11; i <= 30; i++) + { + if (dictSize <= ((LZ_UInt32)2 << i)) + { + dictSize = (2 << i); + break; + } + if (dictSize <= ((LZ_UInt32)3 << i)) + { + dictSize = (3 << i); + break; + } + } + + for (i = 0; i < 4; i++) + props[1 + i] = (Byte)(dictSize >> (8 * i)); + return SZ_OK; +} + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + SRes res; + CLzmaEnc *p = (CLzmaEnc *)pp; + + CSeqOutStreamBuf outStream; + + LzmaEnc_SetInputBuf(p, src, srcLen); + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = writeEndMark; + res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable, + progress, alloc, allocBig); + + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + return res; +} + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); + SRes res; + if (p == 0) + return SZ_ERROR_MEM; + + res = LzmaEnc_SetProps(p, props); + if (res == SZ_OK) + { + res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); + if (res == SZ_OK) + res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, + writeEndMark, progress, alloc, allocBig); + } + + LzmaEnc_Destroy(p, alloc, allocBig); + return res; +} diff --git a/LzmaEnc.h b/LzmaEnc.h new file mode 100644 index 0000000..57e3012 --- /dev/null +++ b/LzmaEnc.h @@ -0,0 +1,72 @@ +/* LzmaEnc.h -- LZMA Encoder + 2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMAENC_H +#define __LZMAENC_H + +#include "types.h" + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ + int level; /* 0 <= level <= 9 */ + LZ_UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + (1 << 12) <= dictSize <= (1 << 30) for 64-bit version + default = (1 << 24) */ + int lc; /* 0 <= lc <= 8, default = 3 */ + int lp; /* 0 <= lp <= 4, default = 0 */ + int pb; /* 0 <= pb <= 4, default = 2 */ + int algo; /* 0 - fast, 1 - normal, default = 1 */ + int fb; /* 5 <= fb <= 273, default = 32 */ + int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ + int numHashBytes; /* 2, 3 or 4, default = 4 */ + LZ_UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ + unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ + int numThreads; /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +LZ_UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: + Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater in props + SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_PROGRESS - some break from progress callback + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + */ + +typedef void * CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode + Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + */ + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +#endif diff --git a/LzmaLib.c b/LzmaLib.c new file mode 100644 index 0000000..a9ebd71 --- /dev/null +++ b/LzmaLib.c @@ -0,0 +1,46 @@ +/* LzmaLib.c -- LZMA library wrapper + 2008-08-05 + Igor Pavlov + Public domain */ + +#include "LzmaEnc.h" +#include "LzmaDec.h" +#include "Alloc.h" +#include "LzmaLib.h" + +static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } +static void SzFree(void *p, void *address) { p = p; MyFree(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ +) +{ + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = level; + props.dictSize = dictSize; + props.lc = lc; + props.lp = lp; + props.pb = pb; + props.fb = fb; + props.numThreads = numThreads; + + return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 0, + NULL, &g_Alloc, &g_Alloc); +} + + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t *srcLen, + const unsigned char *props, size_t propsSize) +{ + ELzmaStatus status; + return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, &status, &g_Alloc); +} diff --git a/LzmaLib.h b/LzmaLib.h new file mode 100644 index 0000000..4272c12 --- /dev/null +++ b/LzmaLib.h @@ -0,0 +1,135 @@ +/* LzmaLib.h -- LZMA library interface +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __LZMALIB_H +#define __LZMALIB_H + +#include "types.h" + +#ifdef __cplusplus + #define MY_EXTERN_C extern "C" +#else + #define MY_EXTERN_C extern +#endif + +#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL + +#define LZMA_PROPS_SIZE 5 + +/* +RAM requirements for LZMA: + for compression: (dictSize * 11.5 + 6 MB) + state_size + for decompression: dictSize + state_size + state_size = (4 + (1.5 << (lc + lp))) KB + by default (lc=3, lp=0), state_size = 16 KB. + +LZMA properties (5 bytes) format + Offset Size Description + 0 1 lc, lp and pb in encoded form. + 1 4 dictSize (little endian). +*/ + +/* +LzmaCompress +------------ + +outPropsSize - + In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + + LZMA Encoder will use defult values for any parameter, if it is + -1 for any from: level, loc, lp, pb, fb, numThreads + 0 for dictSize + +level - compression level: 0 <= level <= 9; + + level dictSize algo fb + 0: 16 KB 0 32 + 1: 64 KB 0 32 + 2: 256 KB 0 32 + 3: 1 MB 0 32 + 4: 4 MB 0 32 + 5: 16 MB 1 32 + 6: 32 MB 1 32 + 7+: 64 MB 1 64 + + The default value for "level" is 5. + + algo = 0 means fast method + algo = 1 means normal method + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + +lc - The number of literal context bits (high bits of previous literal). + It can be in the range from 0 to 8. The default value is 3. + Sometimes lc=4 gives the gain for big files. + +lp - The number of literal pos bits (low bits of current position for literals). + It can be in the range from 0 to 4. The default value is 0. + The lp switch is intended for periodical data when the period is equal to 2^lp. + For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's + better to set lc=0, if you change lp switch. + +pb - The number of pos bits (low bits of current position). + It can be in the range from 0 to 4. The default value is 2. + The pb switch is intended for periodical data when the period is equal 2^pb. + +fb - Word size (the number of fast bytes). + It can be in the range from 5 to 273. The default value is 32. + Usually, a big number gives a little bit better compression ratio and + slower compression process. + +numThreads - The number of thereads. 1 or 2. The default value is 2. + Fast mode (algo = 0) can use only 1 thread. + +Out: + destLen - processed output size +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* default = (1 << 24) */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ + ); + +/* +LzmaUncompress +-------------- +In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size +Out: + destLen - processed output size + srcLen - processed input size +Returns: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation arror + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) +*/ + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, + const unsigned char *props, size_t propsSize); + +#endif diff --git a/MBLRoutines.c b/MBLRoutines.c new file mode 100644 index 0000000..b279729 --- /dev/null +++ b/MBLRoutines.c @@ -0,0 +1,411 @@ +/* +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 +// +// MBL-Style Forwarding Routines + +#include "bpqmail.h" + +VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len) +{ + Buffer[len] = 0; + + // Winpack can send a second SID to switch to upload mode + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + if (user->flags & (F_PMS)) + { + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + conn->FBBChecksum = 0; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + } + else + FBBputs(conn, ">\r"); + } + + return; + } + + if (Buffer[0] == 6 && Buffer[1] == 5) + { + // ?? Sally send these after a failed tranfer + + memmove(Buffer, &Buffer[2], len); + len-=2; + } + + + if (_memicmp(Buffer, "F< ", 3) == 0) + { + // FBB Compressed request from system using UI Messages + + int Number = atoi(&Buffer[3]); + struct MsgInfo * Msg = FindMessageByNumber(Number); + char ErrMsg[80]; + int ErrLen; + + if (Msg == 0) + { + ErrLen = sprintf(&ErrMsg[2], "Msg $%d does not exist!\r>", Number); + ErrMsg[0] = 0x18; + ErrMsg[1] = ErrLen; + + BBSputs(conn, ErrMsg); + FBBputs(conn, ">\r"); + return; + } + + Msg = FindMessage(user->Call, Number, conn->sysop); + + if (Msg) + { + conn->BBSFlags |= FBBCompressed; // Needs compression + SendCompressed(conn, Msg); + FBBputs(conn, ">\r"); + Msg->status = 'Y'; // Mark as read + } + else + { + ErrLen = sprintf(&ErrMsg[2], "Msg $%d not available to you!\r>", Number); + ErrMsg[0] = 0x18; + ErrMsg[1] = ErrLen; + + BBSputs(conn, ErrMsg); + FBBputs(conn, ">\r"); + } + return; + } + + if (Buffer[0] == 'S') //Send + { + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + char * Cmd; + char * To = NULL; + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char * ptr, * Context; + char seps[] = " \t\r"; + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd[1] == 0) Cmd[1] = 'P'; + + if (RefuseBulls && Cmd[1] == 'B') + { + nodeprintfEx(conn, "NO - BULLS NOT ACCEPTED\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintfEx(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintfEx(conn, ">\r"); + + return; + } + + To = strtok_s(NULL, seps, &Context); + + ptr = strtok_s(NULL, seps, &Context); + + while (ptr) + { + if (strcmp(ptr, "@") == 0) + { + ATBBS = _strupr(strtok_s(NULL, seps, &Context)); + } + else if(strcmp(ptr, "<") == 0) + { + From = strtok_s(NULL, seps, &Context); + } + else if (ptr[0] == '$') + BID = &ptr[1]; + else + { + nodeprintfEx(conn, "*** Error: Invalid Format\r"); + return; + } + + ptr = strtok_s(NULL, seps, &Context); + } + + if (!From) + { + nodeprintfEx(conn, "*** Error: Invalid Format\r"); + return; + } + + if (BID && LookupTempBID(BID)) + { + // Being received from another BBS + + nodeprintfEx(conn, "NO - BID\r"); + + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintfEx(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintfEx(conn, ">\r"); + + return; + } + + CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); + return; + } + + + if (Buffer[0] == 'N') // Not wanted + { + if (conn->FwdMsg) + { + // Zap the entry + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + + return; + } + + if (Buffer[0] == 'O') // Need it (OK) + { + struct tm * tm; + time_t now; + char * MsgBytes; + char * MsgPtr; // In case updated for B2 + int MsgLen; + + if (!conn->FwdMsg) + return; + + nodeprintfEx(conn, "%s\r", conn->FwdMsg->title); + + MsgBytes = ReadMessageFile(conn->FwdMsg->number); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + MsgLen = RemoveLF(MsgPtr, MsgLen); + + now = time(NULL); + + tm = gmtime(&now); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message + BBSputs(conn, "\r"); + + QueueMsg(conn, MsgPtr, MsgLen); + + if (user->ForwardingInfo->SendCTRLZ) + nodeprintf(conn, "\rx1a"); + else + nodeprintf(conn, "\r/ex\r"); + + free(MsgBytes); + + conn->FBBMsgsSent = TRUE; + + return; + } + + if (_stricmp(Buffer, "F>\r") == 0) + { + // Reverse forward request + + // If we have just sent a nessage, Flag it as sent + + if (conn->FBBMsgsSent) + { + conn->FBBMsgsSent = FALSE; + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + // Send Message or Disconnect + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + if (conn->BBSFlags & MFJMODE) + { + // No @ BBS to MFJ TNC + + nodeprintfEx(conn, "S%c %s < %s $%s\r", Msg->type, Msg->to, Msg->from, Msg->bid); + } + else + nodeprintfEx(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + + conn->BBSFlags |= MBLFORWARDING; + return; + } + + BBSputs(conn, "*** DONE\r"); + Flush(conn); + Sleep(400); + Disconnect(conn->BPQStream); + return; + } + + if (Buffer[len-2] == '>') + { + // If we have just sent a message, Flag it as sent + + if (conn->FBBMsgsSent) + { + conn->FBBMsgsSent = FALSE; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + // Send Message or request reverse using MBL-style forwarding + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + nodeprintfEx(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + + return; + + } + else + { + FBBputs(conn, "F>\r"); + return; + } + } + + // Winpack after doing ocmpressed downloads sends KM or B + + if (_stricmp(Buffer, "*** DONE\r") == 0 || _stricmp(Buffer, "*** What?\r") == 0 + || _stricmp(Buffer, "B\r") == 0) + { + Disconnect(conn->BPQStream); + return; + } + + if (_stricmp(Buffer, "KM\r") == 0) + { + int i; + struct MsgInfo * Msg; + + for (i = NumberofMessages; i > 0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0)) + { + if (Msg->type == 'P' && Msg->status == 'Y') + { + FlagAsKilled(Msg, TRUE); + nodeprintfEx(conn, "Message #%d Killed\r", Msg->number); + } + } + } + + SendPrompt(conn, user); + return; + } +} + diff --git a/MHSave.txt b/MHSave.txt new file mode 100644 index 0000000..e69de29 diff --git a/MULTIPSK.c b/MULTIPSK.c new file mode 100644 index 0000000..eee3269 --- /dev/null +++ b/MULTIPSK.c @@ -0,0 +1,1543 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use MultoPSK ALE400 Mode +// +// Uses BPQ EXTERNAL interface +// + + +#define _CRT_SECURE_NO_DEPRECATE + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + + + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static void ConnecttoMPSKThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoMPSK(); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); + +char * strlop(char * buf, char delim); + +extern UCHAR BPQDirectory[]; + +#define MAXBPQPORTS 32 +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port + +static int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK host + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[200]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + char FN[MAX_PATH] = ""; + + if (TNC->ProgramPath == NULL) + return FALSE; + + GetWindowText(hwnd, wtext, 199); + + if (strstr(wtext,"* MULTIPSK")) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + TNC->PID = ProcessId; + return FALSE; + } + + return (TRUE); +} + +#endif + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + int len; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Cmd); + + len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, len, 0); + + } + } + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + // Only on first port using a host + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoMPSK(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPSock,&readfs); + + + FD_ZERO(&writefs); + + if (TNC->CONNECTING) FD_SET(TNC->TCPSock,&writefs); // Need notification of Connect + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING ||TNC->CONNECTED) FD_SET(TNC->TCPSock,&errorfs); + + if (select((int)TNC->TCPSock+ 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // If required, send signon + + send(TNC->TCPSock,"\x1a", 1, 0); + send(TNC->TCPSock,"DIGITAL MODE ?", 14, 0); + send(TNC->TCPSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPSock,&errorfs)) + { + + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // Have to time out connects, as TNC doesn't report failure + + if (STREAM->Connecting) + { + STREAM->Connecting--; + + if (STREAM->Connecting == 0) + { + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->DiscWhenAllSent = 10; + + // Send Disc to TNC + + TidyClose(TNC, Stream); + } + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + + SOCKET Sock; + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + Sock = TNCInfo[MasterPort[port]]->TCPSock; + + ReleaseBuffer((UINT *)buffptr); + } + + + return (0); + + + + case 2: // send + + + if (!TNCInfo[MasterPort[port]]->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + SendData(TNC, buff->L2DATA, txlen); + } + else + { + char Command[80]; + int len; + + buff->L2DATA[txlen] = 0; + + _strupr(buff->L2DATA); + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TidyClose(TNC, buff->PORT); + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0) + { + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(buff->L2DATA, "MODE", 4) == 0) + { + buff->L2DATA[txlen - 1] = 0; // Remove CR + + len = sprintf(Command,"%cDIGITAL MODE %s\x1b", '\x1a', &buff->L2DATA[5]); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + + if (_memicmp(buff->L2DATA, "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + PMSGWITHLEN buffptr = GetBuff(); + int s = 0; + + while(s <= TNC->MPSKInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr->Len = sprintf(buffptr->Data, "MPSK} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + strcpy(STREAM->RemoteCall, ptr); + + len = sprintf(Command,"%cCALLSIGN_TO_CALL_ARQ_FAE %s%c%cSELECTIVE_CALL_ARQ_FAE\x1b", + '\x1a', STREAM->RemoteCall, '\x1b', '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + STREAM->Connecting = TNC->MPSKInfo->ConnTimeOut; // It doesn't report failure + +// sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); +// SetDlgItemText(TNC->hDlg, IDC_TNCSTATE, Status); + + return 0; + } + + // Send any other command to Multipsk + + buff->L2DATA[txlen - 1] = 0; + _strupr(buff->L2DATA); + + len = sprintf(Command,"%c%s\x1b", '\x1a', buff->L2DATA); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + + } + + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNCInfo[MasterPort[port]]->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesOutstanding > 8) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + break; + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->PID = 0; // So we don't try again + + return 0; +} + +static RestartTNC(struct TNCINFO * TNC) +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath && TNC->DontRestart == 0) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + ret = CreateProcess(TNC->ProgramPath, "MultiPSK TCP_IP_ON", NULL, NULL, FALSE,0 ,NULL ,HomeDir, &SInfo, &PInfo); + + if (ret) + TNC->PID = PInfo.dwProcessId; + + return ret; + } + return 0; +} +#endif + +void * MPSKExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // Will be called once for each MPSK port to be mapped to a BPQ Port + // The MPSK port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_MPSK; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"MPSK Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + + // See if we already have a port for this host + + MasterPort[port] = port; + + for (i = 1; i < port; i++) + { + if (i == port) continue; + + if (TNCInfo[i] && TNCInfo[i]->TCPPort == TNC->TCPPort && + _stricmp(TNCInfo[i]->HostName, TNC->HostName) == 0) + { + MasterPort[port] = i; + break; + } + } + + BPQPort[PortEntry->PORTCONTROL.CHANNELNUM-65][MasterPort[port]] = port; + +#ifndef LINBPQ + if (MasterPort[port] == port) + { + if (EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC)) + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + ConnecttoMPSK(port); + } +#endif + time(&lasttime[port]); // Get initial time value + +// SendMessage(0x40eaa, WM_COMMAND, 0x03000eaa, 0x40eaa); + + return ExtProc; +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct MPSKINFO * AGW; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + AGW = TNC->MPSKInfo = zalloc(sizeof(struct MPSKINFO)); // AGW Sream Mode Specific Data + + AGW->MaxSessions = 10; + AGW->ConnTimeOut = CONTIMEOUT; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort); + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CONTIMEOUT", 10) == 0) + AGW->ConnTimeOut = atoi(&buf[11]) * 10; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "ALEBEACON", 9) == 0) // Send Beacon after each session + TNC->MPSKInfo->Beacon = TRUE; + else + if (_memicmp(buf, "DEFAULTMODE", 11) == 0) // Send Beacon after each session + strcpy(TNC->MPSKInfo->DefaultMode, &buf[12]); + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoMPSK(int port) +{ + _beginthread(ConnecttoMPSKThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoMPSKThread(void * portptr) +{ + + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for MPSK socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + + TNC->CONNECTED=TRUE; + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for MPSK socket - error code = %d\n", err); + WritetoConsole(Msg); + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connection to TNC failed"); + + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connected to MPSK TNC"); + + return; + +} + +static int ProcessReceivedData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "MPSK Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessMPSKPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC); +VOID ProcessMSPKComment(struct TNCINFO * TNC); +VOID ProcessMSPKData(struct TNCINFO * TNC); + +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + +/* +3) each text character transmitted by the client to the server (for the Multipsk TX text editor) must be preceded by the character CHR(25) or CHR(22) in the case of a special link (KISS in Packet or Pax, for example). + +4) each command string transmitted by the client to the server must be preceded by the character CHR(26) and finished by CHR(27), + +5) each character effectively transmitted by Multipsk to the transceiver and transmitted to the client is preceded by the character CHR(28), + +6) each character received by Multipsk and transmitted to the client is preceded by the character CHR(29), + +7) each command string transmitted by the server to the client must be preceded by the character CHR(30) and finished by CHR(31), + +8) all commands (written in readable text ) will have an answer (see further for details), + +9) each server comment (Call ID or RS ID reception, switch to RX or to TX) string transmitted by the server to the client must be preceded by a string: "CHR(23)RX CALL ID=", "CHR(23)RX RS ID=", "CHR(23)SWITCH=RX", "CHR(23) SWITCH=TX", and finished by CHR(24). + +10) each server command, for the transceiver control, transmitted by the server to the client must be preceded by the string "CHR(23) XCVR=" and finished by CHR(24). + +Data + +End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] call "THIS I[End of TX] end of link to GM8BPQ[End of TX] sounding "THIS WAS"[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQFAE BEACON OH5RM Kouvola KP30JR +[End of TX] ARQ FAE selective callGM8BPQ DE OH5RM + +[Connection made with OH5RM] + + +18103 but I have to go out to change antenna + +[End of connection with OH5RM]FAE BEACON OH5RM Kouvola KP30JR +S" to GM8BPQ + +10:23:55 AM Comment: SWITCH=RX +10:24:00 AM Comment: RX RS ID=10:24:00 UTC ALE400 1609 Hz 0 MHz +10:24:19 AM Comment: RX RS ID=10:24:19 UTC ALE400 1604 Hz 0 MHz +10:25:04 AM Comment: SWITCH=TX +10:25:07 AM Comment: SWITCH=RX +10:25:15 AM Comment: SWITCH=TX +:30:22 AM Comment: SWITCH=RX +10:30:25 AM Comment: SWITCH=TX +10:30:27 AM Comment: SWITCH=RX +10:30:35 AM Comment: RX RS ID=10:30:35 UTC ALE400 1598 Hz 0 MHz + + +*/ + + // Reuse the HAL CMD and Data Buffers to build messages from TCP stream + + // See if sequence split over a packet boundary + + if (TNC->CmdEsc == 23) + { + TNC->CmdEsc = 0; + goto CommentEsc; + } + + if (TNC->CmdEsc == 29) + { + TNC->CmdEsc = 0; + goto DataEsc; + } + + if (TNC->CmdEsc == 30) + { + TNC->CmdEsc = 0; + goto CmdEsc; + } + + // No Split + + while(Len) + { + switch (*(MPTR++)) + { + case 29: // Data Char + + Len--; + DataEsc: + if (Len) + { + TNC->DataBuffer[TNC->DataLen++] = *MPTR; + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdEsc = 29; + + if (TNC->DataLen) + ProcessMSPKData(TNC); + + + return; // Nothing left + + case 30: + + Len --; + CmdEsc: + while (Len) + { + if (*MPTR == 31) // End of String + { + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 30; + return; // Nothing left + + case 23: // Server Comment + + Len --; + CommentEsc: + while (Len) + { + if (*MPTR == 24) // End of String + { + // Process Comment + + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 23; + return; // Nothing left + + default: + + Len--; + + } +OuterLoop:; + } + + if (TNC->DataLen) + ProcessMSPKData(TNC); +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + + if (strcmp(TNC->CmdBuffer, "SWITCH=TX") == 0) + TNC->MPSKInfo->TX = TRUE; + else + { + if (strcmp(TNC->CmdBuffer, "SWITCH=RX") == 0) + { + TNC->MPSKInfo->TX = FALSE; + + // See if a command was queued while busy + + if (TNC->CmdSet) + { + send(TNC->TCPSock, TNC->CmdSet, strlen(TNC->CmdSet), 0); + free (TNC->CmdSet); + TNC->CmdSet = NULL; + } + } + else + { + Debugprintf("MPSK CMD %s", TNC->CmdBuffer); + + if (TNC->InternalCmd) + { + PMSGWITHLEN buffptr = GetBuff(); + char * ptr = strstr(TNC->CmdBuffer, "OK"); + + if (ptr) + *(ptr+2) = 0; // Convert OKn to OK for BBS Connect Script + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr->Len= sprintf(buffptr->Data, "MPSK} %s\r", TNC->CmdBuffer); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + if (strstr(TNC->CmdBuffer, "STOP_SELECTIVE_CALL_ARQ_FAE OK")) + TNC->Streams[0].Connecting = FALSE; + + } + } + } +} + +VOID ProcessMSPKComment(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + Debugprintf("MPSK Comment %s", TNC->CmdBuffer); +} + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i,txptr=0; + UCHAR c; + UCHAR * outbuff = inbuff; + + for (i = 0; i < len; i++) + { + c = inbuff[i]; + + if (c == 0xc0) + c = inbuff[++i] - 0x20; + + outbuff[txptr++]=c; + } + + return txptr; +} + +VOID ProcessMSPKData(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char * ptr; + int Len = TNC->DataLen; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // Process Data + + if (STREAM->Connected) + { + ptr = strstr(TNC->DataBuffer, "[End of connection"); + + if (ptr) + { + // Disconnect + + TNC->DataLen = 0; + + if (STREAM->DiscWhenAllSent) + return; // Already notified + + if (STREAM->Connecting) + { + // Report Connect Failed, and drop back to command mode + + STREAM->Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->DiscWhenAllSent = 10; + + return; + } + + // Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + STREAM->DiscWhenAllSent = 10; + STREAM->FramesOutstanding = 0; + + return; + } + + // Pass to Application. Remove any transparency (hex 0xc0 used as an escape) + + buffptr = GetBuff(); + + if (TNC->DataBuffer[TNC->DataLen - 1] == 0xc0) + return; // Last char is an escape, so wait for the escaped char to arrive + + if (buffptr) + { + if (memchr(TNC->DataBuffer, 0xc0, TNC->DataLen)) + TNC->DataLen = UnStuff(TNC->DataBuffer, TNC->DataLen); + + buffptr->Len = TNC->DataLen; + memcpy(buffptr->Data, TNC->DataBuffer, TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->BytesRXed += TNC->DataLen; + } + + TNC->DataLen = 0; + return; + } + + // Not Connected. We get various status messages, including Connection made, + // but they may be split across packets, or have more that one to a packet. + // I think they are all CR/LF terminated . No they aren't! + + // Look for [] this seems to be what is important + +DataLoop: + + if (memcmp(TNC->DataBuffer, "[End of TX] ARQ FAE CQ", 22) == 0) + { + // Remove string from buffer + + if (Len == 22) // Most Likely + { + TNC->DataLen = 0; + return; + } + + TNC->DataLen -= 22; + memmove(TNC->DataBuffer, &TNC->DataBuffer[22], Len - 21); //Copy Null + Len -= 22; + goto DataLoop; + + } + + ptr = strchr(TNC->DataBuffer, '['); + + if (ptr) + { + // Start of a significant Message + + char * eptr = strchr(TNC->DataBuffer, ']'); + char CallFrom[20]; + char * cptr ; + + if (eptr == 0) + return; // wait for matching [] + + cptr = strstr(TNC->DataBuffer, "[Connection made with "); + + // TNC->DataLen -= LineLen; + // memmove(TNC->DataBuffer, &TNC->DataBuffer[LineLen], 1 + Len - LineLen); //Copy Null + // Len -= LineLen; + // goto DataLoop; + + + if (cptr) // Have a connection + { + + // Connected + + memcpy(CallFrom, &cptr[22], 18); + cptr = strchr(CallFrom, ']'); + if (cptr) + *cptr = 0; + + if (STREAM->Connecting) + { + // Connect Complete + + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + buffptr = GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", CallFrom); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + else + { + // Incoming. Look for a free Stream + + STREAM->Connected = TRUE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + UpdateMH(TNC, CallFrom, '+', 'I'); + + ProcessIncommingConnect(TNC, CallFrom, Stream, FALSE); + + if (HFCTEXTLEN) + { + if (HFCTEXTLEN > 1) + SendData(TNC, HFCTEXT, HFCTEXTLEN); + } + else + { + if (FULL_CTEXT) + { + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + SendData(TNC, &CTEXTMSG[Next], CTPaclen); + Next += CTPaclen; + Len -= CTPaclen; + } + SendData(TNC, &CTEXTMSG[Next], Len); + } + } + } + } + + } + + // Doesnt contain [ - just discard + + TNC->DataLen = 0; + Debugprintf(TNC->DataBuffer); + return; + +} + + + +/* + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = RXHeader->DataLength; + memcpy(&buffptr[2], Message, RXHeader->DataLength); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + return; + + + case 'd': // Disconnected + + + + case 'C': + + // Connect. Can be Incoming or Outgoing + + // "*** CONNECTED To Station [CALLSIGN]" When the other station starts the connection + // "*** CONNECTED With [CALLSIGN]" When we started the connection + + */ + + +VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen) +{ + // Preceed each data byte with 25 (decimal) + + char * NewMsg = malloc (MsgLen * 4); + int n; + UCHAR c; + int ExtraLen = 0; + char * ptr = NewMsg; + char * inptr = Msg; + SOCKET sock = TNCInfo[MasterPort[TNC->Port]]->TCPSock; + + TNC->Streams[0].BytesTXed += MsgLen; + + for (n = 0; n < MsgLen; n++) + { + *(ptr++) = 25; + c = *inptr++; + + if (c < 0x20 || c == 0xc0) + { + if (c != 0x0d) + { + *ptr++ = 0x0c0; + *(ptr++) = 25; + *ptr++ = c + 0x20; + ExtraLen += 2; + continue; + } + } + + *ptr++ = c; + } + + send(sock, NewMsg, MsgLen * 2 + ExtraLen, 0); + + free(NewMsg); +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Command[80]; + int len; + + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Savde till not transmitting + else + send(TNC->TCPSock, Command, len, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Cmd[80]; + int Len; + + sprintf(Cmd, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Cmd); + + Cmd[0] = 0; + + if (TNC->MPSKInfo->DefaultMode[0]) + sprintf(Cmd, "%cDIGITAL MODE %s\x1b", '\x1a', TNC->MPSKInfo->DefaultMode); + + if (TNC->MPSKInfo->Beacon) + sprintf(Cmd, "%s%cBEACON_ARQ_FAE\x1b", Cmd, '\x1a'); + + Len = strlen(Cmd); + + if(Len) + { + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, Len, 0); + } +} + diff --git a/MULTIPSK64.c b/MULTIPSK64.c new file mode 100644 index 0000000..8fe6a84 --- /dev/null +++ b/MULTIPSK64.c @@ -0,0 +1,1543 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use MultoPSK ALE400 Mode +// +// Uses BPQ EXTERNAL interface +// + + +#define _CRT_SECURE_NO_DEPRECATE + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + + + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +static void ConnecttoMPSKThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoMPSK(); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); + +char * strlop(char * buf, char delim); + +extern UCHAR BPQDirectory[]; + +#define MAXBPQPORTS 32 +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port + +static int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK host + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[200]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + char FN[MAX_PATH] = ""; + + if (TNC->ProgramPath == NULL) + return FALSE; + + GetWindowText(hwnd, wtext, 199); + + if (strstr(wtext,"* MULTIPSK")) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + TNC->PID = ProcessId; + return FALSE; + } + + return (TRUE); +} + +#endif + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + int len; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Cmd); + + len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, len, 0); + + } + } + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + // Only on first port using a host + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoMPSK(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPSock,&readfs); + + + FD_ZERO(&writefs); + + if (TNC->CONNECTING) FD_SET(TNC->TCPSock,&writefs); // Need notification of Connect + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING ||TNC->CONNECTED) FD_SET(TNC->TCPSock,&errorfs); + + if (select((int)TNC->TCPSock+ 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // If required, send signon + + send(TNC->TCPSock,"\x1a", 1, 0); + send(TNC->TCPSock,"DIGITAL MODE ?", 14, 0); + send(TNC->TCPSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPSock,&errorfs)) + { + + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // Have to time out connects, as TNC doesn't report failure + + if (STREAM->Connecting) + { + STREAM->Connecting--; + + if (STREAM->Connecting == 0) + { + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->DiscWhenAllSent = 10; + + // Send Disc to TNC + + TidyClose(TNC, Stream); + } + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = 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((PDATAMESSAGE)buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + + SOCKET Sock; + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + Sock = TNCInfo[MasterPort[port]]->TCPSock; + + ReleaseBuffer((UINT *)buffptr); + } + + + return (0); + + + + case 2: // send + + + if (!TNCInfo[MasterPort[port]]->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + SendData(TNC, buff->L2DATA, txlen); + } + else + { + char Command[80]; + int len; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + if (_memicmp(&buff[8], "D\r", 2) == 0) + { + TidyClose(TNC, buff->PORT); + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len= sprintf((UCHAR *)&buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (STREAM->Connecting && _memicmp(&buff[8], "ABORT", 5) == 0) + { + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(&buff[8], "MODE", 4) == 0) + { + buff->L2DATA[txlen - 1] = 0; // Remove CR + + len = sprintf(Command,"%cDIGITAL MODE %s\x1b", '\x1a', &buff->L2DATA[5]); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + + if (_memicmp(&buff[8], "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + UINT * buffptr = GetBuff(); + int s = 0; + + while(s <= TNC->MPSKInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + strcpy(STREAM->RemoteCall, ptr); + + len = sprintf(Command,"%cCALLSIGN_TO_CALL_ARQ_FAE %s%c%cSELECTIVE_CALL_ARQ_FAE\x1b", + '\x1a', STREAM->RemoteCall, '\x1b', '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + STREAM->Connecting = TNC->MPSKInfo->ConnTimeOut; // It doesn't report failure + +// sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); +// SetDlgItemText(TNC->hDlg, IDC_TNCSTATE, Status); + + return 0; + } + + // Send any other command to Multipsk + + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + len = sprintf(Command,"%c%s\x1b", '\x1a', buff->L2DATA); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + + } + + return (0); + + case 3: + + Stream = buff->PORT; + + TNCOK = TNCInfo[MasterPort[port]]->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesOutstanding > 8) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + break; + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->PID = 0; // So we don't try again + + return 0; +} + +static RestartTNC(struct TNCINFO * TNC) +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + ret = CreateProcess(TNC->ProgramPath, "MultiPSK TCP_IP_ON", NULL, NULL, FALSE,0 ,NULL ,HomeDir, &SInfo, &PInfo); + + if (ret) + TNC->PID = PInfo.dwProcessId; + + return ret; + } + return 0; +} +#endif + +UINT MPSKExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // Will be called once for each MPSK port to be mapped to a BPQ Port + // The MPSK port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_MPSK; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"MPSK Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + + // See if we already have a port for this host + + MasterPort[port] = port; + + for (i = 1; i < port; i++) + { + if (i == port) continue; + + if (TNCInfo[i] && TNCInfo[i]->TCPPort == TNC->TCPPort && + _stricmp(TNCInfo[i]->HostName, TNC->HostName) == 0) + { + MasterPort[port] = i; + break; + } + } + + BPQPort[PortEntry->PORTCONTROL.CHANNELNUM-65][MasterPort[port]] = port; + +#ifndef LINBPQ + if (MasterPort[port] == port) + { + if (EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC)) + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + ConnecttoMPSK(port); + } +#endif + time(&lasttime[port]); // Get initial time value + +// SendMessage(0x40eaa, WM_COMMAND, 0x03000eaa, 0x40eaa); + + return ExtProc; + +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct MPSKINFO * AGW; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + AGW = TNC->MPSKInfo = zalloc(sizeof(struct MPSKINFO)); // AGW Sream Mode Specific Data + + AGW->MaxSessions = 10; + AGW->ConnTimeOut = CONTIMEOUT; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort); + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CONTIMEOUT", 10) == 0) + AGW->ConnTimeOut = atoi(&buf[11]) * 10; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "ALEBEACON", 9) == 0) // Send Beacon after each session + TNC->MPSKInfo->Beacon = TRUE; + else + if (_memicmp(buf, "DEFAULTMODE", 11) == 0) // Send Beacon after each session + strcpy(TNC->MPSKInfo->DefaultMode, &buf[12]); + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoMPSK(int port) +{ + _beginthread(ConnecttoMPSKThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoMPSKThread(void * portptr) +{ + + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for MPSK socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + + TNC->CONNECTED=TRUE; + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for MPSK socket - error code = %d\n", err); + WritetoConsole(Msg); + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connection to TNC failed"); + + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connected to MPSK TNC"); + + return; + +} + +static int ProcessReceivedData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "MPSK Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessMPSKPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC); +VOID ProcessMSPKComment(struct TNCINFO * TNC); +VOID ProcessMSPKData(struct TNCINFO * TNC); + +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + +/* +3) each text character transmitted by the client to the server (for the Multipsk TX text editor) must be preceded by the character CHR(25) or CHR(22) in the case of a special link (KISS in Packet or Pax, for example). + +4) each command string transmitted by the client to the server must be preceded by the character CHR(26) and finished by CHR(27), + +5) each character effectively transmitted by Multipsk to the transceiver and transmitted to the client is preceded by the character CHR(28), + +6) each character received by Multipsk and transmitted to the client is preceded by the character CHR(29), + +7) each command string transmitted by the server to the client must be preceded by the character CHR(30) and finished by CHR(31), + +8) all commands (written in readable text ) will have an answer (see further for details), + +9) each server comment (Call ID or RS ID reception, switch to RX or to TX) string transmitted by the server to the client must be preceded by a string: "CHR(23)RX CALL ID=", "CHR(23)RX RS ID=", "CHR(23)SWITCH=RX", "CHR(23) SWITCH=TX", and finished by CHR(24). + +10) each server command, for the transceiver control, transmitted by the server to the client must be preceded by the string "CHR(23) XCVR=" and finished by CHR(24). + +Data + +End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] call "THIS I[End of TX] end of link to GM8BPQ[End of TX] sounding "THIS WAS"[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQFAE BEACON OH5RM Kouvola KP30JR +[End of TX] ARQ FAE selective callGM8BPQ DE OH5RM + +[Connection made with OH5RM] + + +18103 but I have to go out to change antenna + +[End of connection with OH5RM]FAE BEACON OH5RM Kouvola KP30JR +S" to GM8BPQ + +10:23:55 AM Comment: SWITCH=RX +10:24:00 AM Comment: RX RS ID=10:24:00 UTC ALE400 1609 Hz 0 MHz +10:24:19 AM Comment: RX RS ID=10:24:19 UTC ALE400 1604 Hz 0 MHz +10:25:04 AM Comment: SWITCH=TX +10:25:07 AM Comment: SWITCH=RX +10:25:15 AM Comment: SWITCH=TX +:30:22 AM Comment: SWITCH=RX +10:30:25 AM Comment: SWITCH=TX +10:30:27 AM Comment: SWITCH=RX +10:30:35 AM Comment: RX RS ID=10:30:35 UTC ALE400 1598 Hz 0 MHz + + +*/ + + // Reuse the HAL CMD and Data Buffers to build messages from TCP stream + + // See if sequence split over a packet boundary + + if (TNC->CmdEsc == 23) + { + TNC->CmdEsc = 0; + goto CommentEsc; + } + + if (TNC->CmdEsc == 29) + { + TNC->CmdEsc = 0; + goto DataEsc; + } + + if (TNC->CmdEsc == 30) + { + TNC->CmdEsc = 0; + goto CmdEsc; + } + + // No Split + + while(Len) + { + switch (*(MPTR++)) + { + case 29: // Data Char + + Len--; + DataEsc: + if (Len) + { + TNC->DataBuffer[TNC->DataLen++] = *MPTR; + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdEsc = 29; + + if (TNC->DataLen) + ProcessMSPKData(TNC); + + + return; // Nothing left + + case 30: + + Len --; + CmdEsc: + while (Len) + { + if (*MPTR == 31) // End of String + { + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 30; + return; // Nothing left + + case 23: // Server Comment + + Len --; + CommentEsc: + while (Len) + { + if (*MPTR == 24) // End of String + { + // Process Comment + + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 23; + return; // Nothing left + + default: + + Len--; + + } +OuterLoop:; + } + + if (TNC->DataLen) + ProcessMSPKData(TNC); +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + + if (strcmp(TNC->CmdBuffer, "SWITCH=TX") == 0) + TNC->MPSKInfo->TX = TRUE; + else + { + if (strcmp(TNC->CmdBuffer, "SWITCH=RX") == 0) + { + TNC->MPSKInfo->TX = FALSE; + + // See if a command was queued while busy + + if (TNC->CmdSet) + { + send(TNC->TCPSock, TNC->CmdSet, (int)strlen(TNC->CmdSet), 0); + free (TNC->CmdSet); + TNC->CmdSet = NULL; + } + } + else + { + Debugprintf("MPSK CMD %s", TNC->CmdBuffer); + + if (TNC->InternalCmd) + { + ULONG * buffptr = GetBuff(); + char * ptr = strstr(TNC->CmdBuffer, "OK"); + + if (ptr) + *(ptr+2) = 0; // Convert OKn to OK for BBS Connect Script + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} %s\r", TNC->CmdBuffer); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + if (strstr(TNC->CmdBuffer, "STOP_SELECTIVE_CALL_ARQ_FAE OK")) + TNC->Streams[0].Connecting = FALSE; + + } + } + } +} + +VOID ProcessMSPKComment(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + Debugprintf("MPSK Comment %s", TNC->CmdBuffer); +} + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i,txptr=0; + UCHAR c; + UCHAR * outbuff = inbuff; + + for (i = 0; i < len; i++) + { + c = inbuff[i]; + + if (c == 0xc0) + c = inbuff[++i] - 0x20; + + outbuff[txptr++]=c; + } + + return txptr; +} + +VOID ProcessMSPKData(struct TNCINFO * TNC) +{ + UINT * buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char * ptr; + int Len = TNC->DataLen; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // Process Data + + if (STREAM->Connected) + { + ptr = strstr(TNC->DataBuffer, "[End of connection"); + + if (ptr) + { + // Disconnect + + TNC->DataLen = 0; + + if (STREAM->DiscWhenAllSent) + return; // Already notified + + if (STREAM->Connecting) + { + // Report Connect Failed, and drop back to command mode + + STREAM->Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "MPSK} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->DiscWhenAllSent = 10; + + return; + } + + // Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + STREAM->DiscWhenAllSent = 10; + STREAM->FramesOutstanding = 0; + + return; + } + + // Pass to Application. Remove any transparency (hex 0xc0 used as an escape) + + buffptr = GetBuff(); + + if (TNC->DataBuffer[TNC->DataLen - 1] == 0xc0) + return; // Last char is an escape, so wait for the escaped char to arrive + + if (buffptr) + { + if (memchr(TNC->DataBuffer, 0xc0, TNC->DataLen)) + TNC->DataLen = UnStuff(TNC->DataBuffer, TNC->DataLen); + + buffptr[1] = TNC->DataLen; + memcpy(&buffptr[2], TNC->DataBuffer, TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->BytesRXed += TNC->DataLen; + } + + TNC->DataLen = 0; + return; + } + + // Not Connected. We get various status messages, including Connection made, + // but they may be split across packets, or have more that one to a packet. + // I think they are all CR/LF terminated . No they aren't! + + // Look for [] this seems to be what is important + +DataLoop: + + if (memcmp(TNC->DataBuffer, "[End of TX] ARQ FAE CQ", 22) == 0) + { + // Remove string from buffer + + if (Len == 22) // Most Likely + { + TNC->DataLen = 0; + return; + } + + TNC->DataLen -= 22; + memmove(TNC->DataBuffer, &TNC->DataBuffer[22], Len - 21); //Copy Null + Len -= 22; + goto DataLoop; + + } + + ptr = strchr(TNC->DataBuffer, '['); + + if (ptr) + { + // Start of a significant Message + + char * eptr = strchr(TNC->DataBuffer, ']'); + char CallFrom[20]; + char * cptr ; + + if (eptr == 0) + return; // wait for matching [] + + cptr = strstr(TNC->DataBuffer, "[Connection made with "); + + // TNC->DataLen -= LineLen; + // memmove(TNC->DataBuffer, &TNC->DataBuffer[LineLen], 1 + Len - LineLen); //Copy Null + // Len -= LineLen; + // goto DataLoop; + + + if (cptr) // Have a connection + { + + // Connected + + memcpy(CallFrom, &cptr[22], 18); + cptr = strchr(CallFrom, ']'); + if (cptr) + *cptr = 0; + + if (STREAM->Connecting) + { + // Connect Complete + + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", CallFrom); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + else + { + // Incoming. Look for a free Stream + + STREAM->Connected = TRUE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + UpdateMH(TNC, CallFrom, '+', 'I'); + + ProcessIncommingConnect(TNC, CallFrom, Stream, FALSE); + + if (HFCTEXTLEN) + { + if (HFCTEXTLEN > 1) + SendData(TNC, HFCTEXT, HFCTEXTLEN); + } + else + { + if (FULL_CTEXT) + { + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + SendData(TNC, &CTEXTMSG[Next], CTPaclen); + Next += CTPaclen; + Len -= CTPaclen; + } + SendData(TNC, &CTEXTMSG[Next], Len); + } + } + } + } + + } + + // Doesnt contain [ - just discard + + TNC->DataLen = 0; + Debugprintf(TNC->DataBuffer); + return; + +} + + + +/* + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = RXHeader->DataLength; + memcpy(&buffptr[2], Message, RXHeader->DataLength); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + return; + + + case 'd': // Disconnected + + + + case 'C': + + // Connect. Can be Incoming or Outgoing + + // "*** CONNECTED To Station [CALLSIGN]" When the other station starts the connection + // "*** CONNECTED With [CALLSIGN]" When we started the connection + + */ + + +VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen) +{ + // Preceed each data byte with 25 (decimal) + + char * NewMsg = malloc (MsgLen * 4); + int n; + UCHAR c; + int ExtraLen = 0; + char * ptr = NewMsg; + char * inptr = Msg; + SOCKET sock = TNCInfo[MasterPort[TNC->Port]]->TCPSock; + + TNC->Streams[0].BytesTXed += MsgLen; + + for (n = 0; n < MsgLen; n++) + { + *(ptr++) = 25; + c = *inptr++; + + if (c < 0x20 || c == 0xc0) + { + if (c != 0x0d) + { + *ptr++ = 0x0c0; + *(ptr++) = 25; + *ptr++ = c + 0x20; + ExtraLen += 2; + continue; + } + } + + *ptr++ = c; + } + + send(sock, NewMsg, MsgLen * 2 + ExtraLen, 0); + + free(NewMsg); +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Command[80]; + int len; + + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Savde till not transmitting + else + send(TNC->TCPSock, Command, len, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Cmd[80]; + int Len; + + sprintf(Cmd, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Cmd); + + Cmd[0] = 0; + + if (TNC->MPSKInfo->DefaultMode[0]) + sprintf(Cmd, "%cDIGITAL MODE %s\x1b", '\x1a', TNC->MPSKInfo->DefaultMode); + + if (TNC->MPSKInfo->Beacon) + sprintf(Cmd, "%s%cBEACON_ARQ_FAE\x1b", Cmd, '\x1a'); + + Len = (int)strlen(Cmd); + + if(Len) + { + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, Len, 0); + } +} + diff --git a/MailCommands.c b/MailCommands.c new file mode 100644 index 0000000..8550439 --- /dev/null +++ b/MailCommands.c @@ -0,0 +1,696 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +#include "bpqmail.h" + +int APIENTRY ChangeSessionIdletime(int Stream, int idletime); +struct MsgInfo * GetMsgFromNumber(int msgno); +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle); +struct UserInfo * FindBBS(char * Name); +int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user); + +static char seps[] = " \t\r"; + +VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + int AuthInt = 0; + + if (!(user->flags & F_SYSOP)) + { + nodeprintf(conn, "AUTH can only be used by SYSOPs\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1) + AuthInt = atoi(Arg1); + else + { + nodeprintf(conn, "AUTH Code missing\r"); + SendPrompt(conn, user); + return; + } + + if (user->Temp->LastAuthCode == AuthInt) + { + nodeprintf(conn, "AUTH Code already used\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1 && CheckOneTimePassword(Arg1, user->pass)) + { + conn->sysop = TRUE; + nodeprintf(conn, "Ok\r"); + user->Temp->LastAuthCode = atoi(Arg1); + } + else + nodeprintf(conn, "AUTH Failed\r"); + + SendPrompt(conn, user); + return; +} + + +VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + char Line[200] = "User Flags:"; + struct UserInfo * EUser = user; + + if (conn->sysop == 0) + { + nodeprintf(conn, "Edit User needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0) + { + nodeprintf(conn, "EDITUSER CALLSIGN to Display\r"); + nodeprintf(conn, "EDITUSER CALLSIGN FLAG1 FLAG2 ... to set, -FLAG1 -FLAG2 ... to clear\r"); + nodeprintf(conn, "EDITUSER: Flags are: EXC(luded) EXP(ert) SYSOP BBS PMS EMAIL HOLD RMS(Express User) APRS(Mail For)\r"); + + SendPrompt(conn, user); + return; + } + + EUser = LookupCall(Arg1); + + if (EUser == 0) + { + nodeprintf(conn, "User %s not found\r", Arg1); + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + goto UDisplay; + + // A set of flags to change +Flag or -Flag + + while(Arg1 && strlen(Arg1) > 2) + { + _strupr(Arg1); + + if (strstr(Arg1, "EXC")) + if (Arg1[0] != '-') EUser->flags |= F_Excluded; else EUser->flags &= ~F_Excluded; + if (strstr(Arg1, "EXP")) + if (Arg1[0] != '-') EUser->flags |= F_Expert; else EUser->flags &= ~F_Expert; + if (strstr(Arg1, "SYS")) + if (Arg1[0] != '-') EUser->flags |= F_SYSOP; else EUser->flags &= ~F_SYSOP; + if (strstr(Arg1, "BBS")) + if (Arg1[0] != '-') EUser->flags |= F_BBS; else EUser->flags &= ~F_BBS; + if (strstr(Arg1, "PMS")) + if (Arg1[0] != '-') EUser->flags |= F_PMS; else EUser->flags &= ~F_PMS; + if (strstr(Arg1, "EMAIL")) + if (Arg1[0] != '-') EUser->flags |= F_EMAIL; else EUser->flags &= ~F_EMAIL; + if (strstr(Arg1, "HOLD")) + if (Arg1[0] != '-') EUser->flags |= F_HOLDMAIL; else EUser->flags &= ~F_HOLDMAIL; + if (strstr(Arg1, "RMS")) + if (Arg1[0] != '-') EUser->flags |= F_Temp_B2_BBS; else EUser->flags &= ~F_Temp_B2_BBS; + if (strstr(Arg1, "APRS")) + if (Arg1[0] != '-') EUser->flags |= F_APRSMFOR; else EUser->flags &= ~F_APRSMFOR; + + Arg1 = strtok_s(NULL, seps, &Context); + } + + SaveUserDatabase(); + + // Drop through to display +UDisplay: + + if (EUser->flags & F_Excluded) + strcat(Line, " EXC"); + + if (EUser->flags & F_Expert) + strcat(Line, " EXP"); + + if (EUser->flags & F_SYSOP) + strcat(Line, " SYSOP"); + + if (EUser->flags & F_BBS) + strcat(Line, " BBS"); + + if (EUser->flags & F_PMS) + strcat(Line, " PMS"); + + if (EUser->flags & F_EMAIL) + strcat(Line, " EMAIL"); + + if (EUser->flags & F_HOLDMAIL) + strcat(Line, " HOLD"); + + if (EUser->flags & F_Temp_B2_BBS) + strcat(Line, " RMS"); + + if (EUser->flags & F_APRSMFOR) + strcat(Line, " APRS"); + + strcat(Line, "\r"); + nodeprintf(conn, Line); + + SendPrompt(conn, user); + return; +} + +VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * inuser, char * Arg1, char * Context) +{ + int i, s; + char FWLine[10000] = ""; + struct UserInfo * user; + char RMSCall[20]; + + if (conn->sysop == 0) + { + nodeprintf(conn, "Command needs SYSOP status\r"); + SendPrompt(conn, inuser); + return; + } + + for (i = 0; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; + + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + strcat(FWLine, " "); + if (s) + { + sprintf(RMSCall, "%s-%d", user->Call, s); + strcat(FWLine, RMSCall); + } + else + strcat(FWLine, user->Call); + + } + } + } + } + + strcat(FWLine, "\r"); + + nodeprintf(conn, FWLine); +} + + + +VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + char RMSLine[200]; + char RMSCall[10]; + struct UserInfo * RMSUser = user; + int s; +Loop: + if (Arg1) + { + // Update + if (_stricmp(Arg1, "Enable") == 0) + { + RMSUser->flags |= F_POLLRMS; + Arg1 = strtok_s(NULL, seps, &Context); + goto Display; + } + else if (_stricmp(Arg1, "Disable") == 0) + { + RMSUser->flags &= ~F_POLLRMS; + Arg1 = strtok_s(NULL, seps, &Context); + goto Display; + } + else if (strlen(Arg1) > 2) + { + // Callsign - if SYSOP, following commands apply to selected user + + if (conn->sysop == 0) + { + nodeprintf(conn, "Changing RMS Poll params for another user needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + RMSUser = LookupCall(Arg1); + + if (RMSUser == NULL) + { + nodeprintf(conn, "User %s not found\r", Arg1); + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + goto Display; + + if (_stricmp(Arg1, "Enable") == 0 || _stricmp(Arg1, "Disable") == 0 || (strlen(Arg1) < 3)) + goto Loop; + + goto Display; + } + + // A list of SSID's to poll + + RMSUser->RMSSSIDBits = 0; + + while(Arg1 && strlen(Arg1) < 3) + { + s = atoi(Arg1); + if (s < 16) + RMSUser->RMSSSIDBits |= (1 << (s)); + + Arg1 = strtok_s(NULL, seps, &Context); + } + } + + // Drop through to display + +Display: + strcpy(RMSLine, "Polling for calls"); + + if (RMSUser->flags & F_POLLRMS) + { + if (RMSUser->RMSSSIDBits == 0) RMSUser->RMSSSIDBits = 1; + { + for (s = 0; s < 16; s++) + { + if (RMSUser->RMSSSIDBits & (1 << s)) + { + strcat(RMSLine, " "); + if (s) + { + sprintf(RMSCall, "%s-%d", RMSUser->Call, s); + strcat(RMSLine, RMSCall); + } + else + strcat(RMSLine, RMSUser->Call); + + } + } + } + strcat(RMSLine, "\r"); + nodeprintf(conn, RMSLine); + } + else + nodeprintf(conn, "RMS Polling for %s disabled\r", RMSUser->Call); + + if (Arg1) + goto Loop; + + SaveUserDatabase(); + SendPrompt(conn, user); +} + +VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + int IdleTime; + + if (Arg1) + IdleTime = atoi(Arg1); + else + { + nodeprintf(conn, "Format is IDLETIME nnn\r"); + SendPrompt(conn, user); + return; + } + + if (IdleTime < 60 || IdleTime > 900) + { + nodeprintf(conn, "Time out of range (60 to 900 seconds)\r"); + SendPrompt(conn, user); + return; + } + + if (conn->BPQStream >= 0) + ChangeSessionIdletime(conn->BPQStream, IdleTime); + else + { + nodeprintf(conn, "Can't set Idle Time on Console)\r"); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "Idle Tine set to %d\r", IdleTime); + SendPrompt(conn, user); + return; +} + +VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + int newNumber; + + if (conn->sysop == 0) + { + nodeprintf(conn, "Command needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1) + newNumber = atoi(Arg1); + else + { + nodeprintf(conn, "New Number not specified\r"); + SendPrompt(conn, user); + return; + } + + if (newNumber < LatestMsg) + { + nodeprintf(conn, "New Number less tham current (%d)\r", LatestMsg); + SendPrompt(conn, user); + return; + } + + if (newNumber > (MaxMsgno - 1000)) + { + nodeprintf(conn, "New Number too close to Max Message Number\r"); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "Next message number was %d, now %d\r", LatestMsg, newNumber); + SendPrompt(conn, user); + LatestMsg = newNumber; + SaveMessageDatabase(); +} + + +VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + int msgno; + char * FN; + FILE * Handle = NULL; + struct MsgInfo * Msg; + + + if (conn->sysop == 0) + { + nodeprintf(conn, "EXPORT command needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0) + { + nodeprintf(conn, "EXPORT nnn FILENAME - Export Message nnn to file FILENAME\r"); + SendPrompt(conn, user); + return; + } + + msgno = atoi(Arg1); + + FN = strtok_s(NULL, " \r", &Context); + + if (FN == NULL) + { + nodeprintf(conn, "Missong Filename"); + SendPrompt(conn, user); + return; + } + + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + SendPrompt(conn, user); + return; + } + + conn->BBSFlags |= BBS; + + Handle = fopen(FN, "ab"); + + if (Handle == NULL) + { + nodeprintf(conn, "File %s could not be opened\r", FN); + SendPrompt(conn, user); + return; + } + +// SetFilePointer(Handle, 0, 0, FILE_END); + + ForwardMessagetoFile(Msg, Handle); + + fclose(Handle); + + nodeprintf(conn, "%Message %d Exported\r", msgno); + SendPrompt(conn, user); + + return; + +} + + +VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + int count; + + if (conn->sysop == 0) + { + nodeprintf(conn, "IMPORT command needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0) + { + nodeprintf(conn, "IMPORT FILENAME - Import Messages from file FILENAME\r"); + SendPrompt(conn, user); + return; + } + + conn->BBSFlags |= BBS; + + count = ImportMessages(conn, Arg1, TRUE); + + conn->BBSFlags &= ~BBS; + conn->Flags = 0; + + nodeprintf(conn, "%d Messages Processed\r", count); + SendPrompt(conn, user); + + return; + +} + + + + + +VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + char Line[200]; + struct UserInfo * FwdBBS; + struct BBSForwardingInfo * ForwardingInfo; + + if (conn->sysop == 0) + { + nodeprintf(conn, "FWD command needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + + if (Arg1 == 0 || _stricmp(Arg1, "HELP") == 0) + { + nodeprintf(conn, "FWD BBSCALL - Display settings\r"); + nodeprintf(conn, "FWD BBSCALL interval - Set forwarding interval\r"); + nodeprintf(conn, "FWD BBSCALL REV interval - Set reverse forwarding interval\r"); + nodeprintf(conn, "FWD BBSCALL +- Flags (Flags are EN(able) RE(verse Poll) SE(Send Immediately)\r"); + nodeprintf(conn, "FWD BBSCALL NOW - Start a forwarding cycle now\r"); + nodeprintf(conn, "FWD QUEUE - List BBS's with queued messages\r"); + nodeprintf(conn, "FWD NOW can specify a Connect Script to use, overriding the configured script.\r"); + nodeprintf(conn, "Elements are separated by | chars. eg FWD RMS NOW ATT 7|C GM8BPQ-10\r"); + + SendPrompt(conn, user); + return; + } + + + if (_stricmp(Arg1, "QUEUE") == 0) + { + struct UserInfo * xuser; + int Msgs; + + if (Context && Context[0]) + { + // a bbs name - list all messages queued to it + + strlop(Context, '\r'); + + xuser = FindBBS(_strupr(Context)); + + if (xuser) + { + ListMessagestoForward(conn, xuser); + SendPrompt(conn, user); + return; + } + } + + for (xuser = BBSChain; xuser; xuser = xuser->BBSNext) + { + Msgs = CountMessagestoForward(xuser); + + if (Msgs) + nodeprintf(conn, "%s %d Msgs\r", xuser->Call, Msgs); + + } + SendPrompt(conn, user); + return; + } + + FwdBBS = LookupCall(Arg1); + + if (FwdBBS == 0 || (FwdBBS->flags & F_BBS) == 0) + { + nodeprintf(conn, "BBS %s not found\r", Arg1); + SendPrompt(conn, user); + return; + } + + ForwardingInfo = FwdBBS->ForwardingInfo; + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + goto FDisplay; + + if (_stricmp(Arg1, "NOW") == 0) + { + char ** Value = NULL; + + if (ForwardingInfo->Forwarding) + { + BBSputs(conn, "Already Connected\r"); + SendPrompt(conn, user); + return; + } + + while (Context[0] == ' ') + Context++; + + if (Context) + strlop(Context, 13); + + if (Context && Context[0]) + { + // Temp Connect Script to use + + char * ptr1; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + ptr = Context; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + Value = realloc(Value, (Count+2) * sizeof(void *)); + + Value[Count++] = _strdup(ptr); + + ptr = ptr1; + } + + Value[Count] = NULL; + } + + StartForwarding(FwdBBS->BBSNumber, Value); + + if (ForwardingInfo->Forwarding) + nodeprintf(conn, "Forwarding Started\r"); + else + nodeprintf(conn, "Start Forwarding failed\r"); + + SendPrompt(conn, user); + return; + } + + if (_stricmp(Arg1, "REV") == 0) + { + Arg1 = strtok_s(NULL, seps, &Context); + ForwardingInfo->RevFwdInterval = atoi(Arg1); + } + else + { + while(Arg1) + { + _strupr(Arg1); + + if (isdigits(Arg1)) + ForwardingInfo->FwdInterval = atoi(Arg1); + else if (strstr(Arg1, "EN")) + if (Arg1[0] == '-') ForwardingInfo->Enabled = FALSE; else ForwardingInfo->Enabled = TRUE; + else if (strstr(Arg1, "RE")) + if (Arg1[0] == '-') ForwardingInfo->ReverseFlag = FALSE; else ForwardingInfo->ReverseFlag = TRUE; + else if (strstr(Arg1, "SE")) + if (Arg1[0] == '-') ForwardingInfo->SendNew = FALSE; else ForwardingInfo->SendNew = TRUE; + + Arg1 = strtok_s(NULL, seps, &Context); + } + } + + SaveConfig(ConfigName); + GetConfig(ConfigName); + +FDisplay: + + sprintf(Line, "%s Fwd Interval %d Rev Interval %d Fwd %s, Reverse Poll %s, Send Immediately %s\r", + FwdBBS->Call, ForwardingInfo->FwdInterval, ForwardingInfo->RevFwdInterval, + (ForwardingInfo->Enabled)? "Enabled" : "Disabled", (ForwardingInfo->ReverseFlag) ? "Enabled": "Disabled", + (ForwardingInfo->SendNew) ? "Enabled": "Disabled"); + + nodeprintf(conn, Line); + SendPrompt(conn, user); + + return; +} + +void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context) +{ + if (conn->sysop == 0) + { + nodeprintf(conn, "DOHOUSEKEEPING command needs SYSOP status\r"); + SendPrompt(conn, user); + return; + } + + DoHouseKeeping(FALSE); + + nodeprintf(conn, "Ok\r", Arg1); + SendPrompt(conn, user); +} + + diff --git a/MailDataDefs.c b/MailDataDefs.c new file mode 100644 index 0000000..ea3825d --- /dev/null +++ b/MailDataDefs.c @@ -0,0 +1,221 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +#include "bpqmail.h" + +int LastVer[4] = {0, 0, 0, 0}; // In case we need to do somthing the first time a version is run + +HWND MainWnd; +HWND hWndSess; +RECT MainRect; +HMENU hActionMenu; +static HMENU hMenu; +HMENU hDisMenu; // Disconnect Menu Handle +HMENU hFWDMenu; // Forward Menu Handle + +int SessX, SessY, SessWidth; // Params for Session Window + +char szBuff[80]; + +#define MaxSockets 64 + +ConnectionInfo Connections[MaxSockets+1]; + +struct SEM ChatSemaphore = {0, 0}; +struct SEM AllocSemaphore = {0, 0}; +struct SEM ConSemaphore = {0, 0}; +struct SEM Semaphore = {0, 0}; +struct SEM OutputSEM = {0, 0}; + +struct UserInfo ** UserRecPtr=NULL; +int NumberofUsers=0; + +struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes + +struct MsgInfo ** MsgHddrPtr=NULL; +int NumberofMessages=0; + +int FirstMessageIndextoForward = 0; // Lowest Message with a forward bit set - limits search + +BIDRec ** BIDRecPtr=NULL; +int NumberofBIDs=0; + +BIDRec ** TempBIDRecPtr=NULL; +int NumberofTempBIDs=0; + +WPRec ** WPRecPtr=NULL; +int NumberofWPrecs=0; + +char ** BadWords=NULL; +int NumberofBadWords=0; +char * BadFile = NULL; + +int LatestMsg = 0; +struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg +int HighestBBSNumber = 0; + +int MaxMsgno = 60000; +int BidLifetime = 60; +int MaxAge = 30; +int MaintInterval = 24; +int MaintTime = 0; + +int UserLifetime = 0; + +BOOL cfgMinToTray; + +BOOL DisconnectOnClose=FALSE; + +char PasswordMsg[100]="Password:"; + +char cfgHOSTPROMPT[100]; + +char cfgCTEXT[100]; + +char cfgLOCALECHO[100]; + +char AttemptsMsg[] = "Too many attempts - Disconnected\r\r"; +char disMsg[] = "Disconnected by SYSOP\r\r"; + +char LoginMsg[]="user:"; + +char BlankCall[]=" "; + + +ULONG BBSApplMask; +ULONG ChatApplMask; + +int BBSApplNum=0; +int ChatApplNum=0; + +//int StartStream=0; +int NumberofStreams=0; +int MaxStreams=0; + +char BBSSID[]="[%s%d.%d.%d.%d-%s%s%s%sIH%sM$]\r"; + +char ChatSID[]="[BPQChatServer-%d.%d.%d.%d]\r"; + +char NewUserPrompt[100]="Please enter your Name\r>\r"; + +char * WelcomeMsg = NULL; +char * NewWelcomeMsg = NULL; +char * ExpertWelcomeMsg = NULL; + +char * Prompt = NULL; +char * NewPrompt = NULL; +char * ExpertPrompt = NULL; + +char BBSName[100] = "NOCALL"; +char SYSOPCall[50]; + +char MailForText[100]; + +char HRoute[100]; +char AMPRDomain[100]; +BOOL SendAMPRDirect = 0; + +char SignoffMsg[100]; + +char AbortedMsg[100]="\rOutput aborted\r"; + +char UserDatabaseName[MAX_PATH] = "BPQBBSUsers.dat"; +char UserDatabasePath[MAX_PATH]; + +char MsgDatabasePath[MAX_PATH]; +char MsgDatabaseName[MAX_PATH] = "DIRMES.SYS"; + +char BIDDatabasePath[MAX_PATH]; +char BIDDatabaseName[MAX_PATH] = "WFBID.SYS"; + +char WPDatabasePath[MAX_PATH]; +char WPDatabaseName[MAX_PATH] = "WP.SYS"; + +char BadWordsPath[MAX_PATH]; +char BadWordsName[MAX_PATH] = "BADWORDS.SYS"; + +char NTSAliasesPath[MAX_PATH]; +char NTSAliasesName[MAX_PATH] = "INTRCPT.APS"; + +char ConfigName[250]; +char ChatConfigName[250]; + +BOOL UsingingRegConfig = FALSE; + +BOOL MulticastRX = FALSE; + +char BaseDir[MAX_PATH]; +char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +char MailDir[MAX_PATH]; + +char RlineVer[50]; + +BOOL KISSOnly = FALSE; + +BOOL EnableUI = FALSE; +BOOL RefuseBulls = FALSE; +BOOL SendSYStoSYSOPCall = FALSE; +BOOL SendBBStoSYSOPCall = FALSE; +BOOL DontHoldNewUsers = FALSE; +BOOL DefaultNoWINLINK = FALSE; +BOOL ForwardToMe = FALSE; +BOOL OnlyKnown = FALSE; + +BOOL DontNeedHomeBBS = FALSE; +BOOL DontCheckFromCall = FALSE; + +// Send WP Params + +BOOL SendWP; +BOOL FilterWPBulls; +BOOL NoWPGuesses; + +char SendWPVIA[81]; +char SendWPTO[11]; + +char ** SendWPAddrs; // Replaces WP To and VIA + +int SendWPType; + +int SMTPMsgs; + +int MailForInterval = 0; + +char zeros[NBMASK]; // For forward bitmask tests + +time_t MaintClock; // Time to run housekeeping + +struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. + +// Filter Params + +char ** RejFrom; // Reject on FROM Call +char ** RejTo; // Reject on TO Call +char ** RejAt; // Reject on AT Call +char ** RejBID; // Reject on BID + +char ** HoldFrom; // Hold on FROM Call +char ** HoldTo; // Hold on TO Call +char ** HoldAt; // Hold on AT Call +char ** HoldBID; // Hold on BID + diff --git a/MailNode.vcproj b/MailNode.vcproj new file mode 100644 index 0000000..534a39e --- /dev/null +++ b/MailNode.vcproj @@ -0,0 +1,623 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MailNode.vcproj.DESKTOP-TGEL8RC.John.user b/MailNode.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40182c4 --- /dev/null +++ b/MailNode.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/MailNode.vcproj.HPLAPTOP.johnw.user b/MailNode.vcproj.HPLAPTOP.johnw.user new file mode 100644 index 0000000..4add25e --- /dev/null +++ b/MailNode.vcproj.HPLAPTOP.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/MailNode.vcproj.SKIGACER.johnw.user b/MailNode.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..b5b0536 --- /dev/null +++ b/MailNode.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/MailNode.vcxproj b/MailNode.vcxproj new file mode 100644 index 0000000..50042ad --- /dev/null +++ b/MailNode.vcxproj @@ -0,0 +1,271 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + LinBPQ + {3766AA10-C777-4ED8-A83D-F1452DE9B666} + MailNode + Win32Proj + 10.0.17763.0 + + + + Application + v141 + NotSet + true + + + Application + v141 + NotSet + true + + + Application + v141 + NotSet + + + Application + v141 + NotSet + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28307.799 + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ + true + + + true + C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ + c:\LinBPQ + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ + false + + + false + C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ + + + + Disabled + ..\CKernel;..\CommonSource;..\CInclude;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;LINBPQ;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + Level3 + EditAndContinue + + + kernel32.lib;WS2_32.Lib;C:\Dev\Msdev2005\Projects\BPQ32\CKernel\Debug\libconfig.lib;DbgHelp.lib;setupapi.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + c:\LINBPQ\$(ProjectName).exe + true + true + c:\linbpq\linmail.map + Console + 4000000 + 0 + MachineX86 + + + + + Disabled + ..\CKernel;..\CommonSource;..\CInclude;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;LINBPQ;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + EditAndContinue + + + kernel32.lib;WS2_32.Lib;C:\Dev\Msdev2005\Projects\BPQ32\CKernel\x64\Debug\libconfig.lib;DbgHelp.lib;setupapi.lib;C:\Users\John\OneDrive\Dev\Source\miniupnpc-2.2.3\msvc\x64\Debug\miniupnpc.lib;zlibstat64.lib;%(AdditionalDependencies) + c:\LINBPQ\$(ProjectName).exe + true + true + c:\linbpq\linmail.map + Console + 4000000 + 0 + false + + + + + ..\CKernel;..\CommonSource;..\CInclude;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;LINBPQ;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + Level3 + ProgramDatabase + + + kernel32.lib;WS2_32.Lib;..\lib\libconfig.lib;DbgHelp.lib;Setupapi.lib;%(AdditionalDependencies) + c:\devprogs\bpq32\LinBPQ.exe + true + Console + 5000000 + 10000000 + true + true + MachineX86 + + + + + ..\CKernel;..\CommonSource;..\CInclude;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;LINBPQ;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + kernel32.lib;WS2_32.Lib;..\lib\libconfig.lib;DbgHelp.lib;Setupapi.lib;%(AdditionalDependencies) + c:\devprogs\bpq32\LinBPQ.exe + true + Console + 5000000 + 10000000 + true + true + + + + + + + + + + + + + + + + + + + All + All + $(IntDir) + $(IntDir) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MailRouting.c b/MailRouting.c new file mode 100644 index 0000000..6692d69 --- /dev/null +++ b/MailRouting.c @@ -0,0 +1,2010 @@ +/* +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 +// +// Message Routing Module + +// This code decides where to send a message. + +// Private messages are routed by TO and AT if possible, if not they follow the Bull Rules + +// Bulls are routed on HA where possible + +// Bulls should not be distributed outside their designated area. + +// Use 4 char continent codes if this isn't defined + +#define TWOCHARCONT + + +#include "bpqmail.h" + +char WW[] = "WW"; + +char * MyElements[20] = {WW}; // My HA in element format + +char MyRouteElements[100]; + +int MyElementCount; + +BOOL ReaddressLocal = 0; +BOOL ReaddressReceived = 0; +BOOL WarnNoRoute = TRUE; +BOOL SendPtoMultiple = FALSE; + +BOOL Localtime = FALSE; // Use Local Time for Timebands and forward connect scripts + +struct ALIAS * CheckForNTSAlias(struct MsgInfo * Msg, char * FirstDestElement); +struct UserInfo * FindAMPR(); +struct UserInfo * FindBBS(char * Name); + +struct Continent +{ + char FourCharCode[5]; + char TwoCharCode[5]; +}; + +struct Country +{ + char Country[5]; + char Continent4[5]; + char Continent2[3]; +}; + + +struct Continent Continents[] = +{ + "EURO", "EU", // Europe + "MEDR", "EU", // Mediterranean + "ASIA", "AS", // The Orient + "INDI", "AS", // Indian Ocean including the Indian subcontinent + "MDLE", "AS", // Middle East + "SEAS", "AS", // South-East Asia + "NOAM", "NA", // North America (Canada, USA, Mexico) + "CEAM", "NA", // Central America + "CARB", "NA", // Caribbean + "SOAM", "SA", // South America + "AUNZ", "OC", // Australia/New Zealand + "EPAC", "OC", // Eastern Pacific + "NPAC", "OC", // Northern Pacific + "SPAC", "OC", // Southern Pacific + "WPAC", "OC", // Western Pacific + "NAFR", "AF", // Northern Africa + "CAFR", "AF", // Central Africa + "SAFR", "AF", // Southern Africa + "ANTR", "OC", // Antarctica + "MARS", "MARS", // Special for MARS network +}; + +struct Country Countries[] = +{ + "AFG", "****", "AS", // Afghanistan + "ALA", "EURO", "EU", // Åland Islands + "ALB", "EURO", "EU", // Albania + "DZA", "NAFR", "AF", // Algeria + "ASM", "****", "AS", // American Samoa + "AND", "EURO", "EU", // Andorra + "AGO", "CAFR", "AF", // Angola + "AIA", "CARB", "NA", // Anguilla + "ATG", "CARB", "NA", // Antigua and Barbuda + "ARG", "SOAM", "SA", // Argentina + "ARM", "****", "AS", // Armenia + "ABW", "CARB", "NA", // Aruba + "AUS", "AUNZ", "OC", // Australia + "AUT", "EURO", "EU", // Austria + "AZE", "****", "AS", // Azerbaijan + "BHS", "CARB", "NA", // Bahamas + "BHR", "MDLE", "AS", // Bahrain + "BGD", "INDE", "AS", // Bangladesh + "BRB", "CARB", "NA", // Barbados + "BLR", "EURO", "EU", // Belarus + "BEL", "EURO", "EU", // Belgium + "BLZ", "CEAM", "NA", // Belize + "BEN", "CAFR", "AF", // Benin + "BMU", "CARB", "NA", // Bermuda + "BTN", "ASIA", "AS", // Bhutan + "BOL", "SOAM", "SA", // Bolivia (Plurinational State of) + "BIH", "EURO", "EU", // Bosnia and Herzegovina + "BWA", "****", "AF", // Botswana + "BRA", "SOAM", "SA", // Brazil + "VGB", "CARB", "NA", // British Virgin Islands + "BRN", "ASIA", "AS", // Brunei Darussalam + "BGR", "EURO", "EU", // Bulgaria + "BFA", "CAFR", "AF", // Burkina Faso + "BDI", "CAFR", "AF", // Burundi + "KHM", "****", "AS", // Cambodia + "CMR", "CAFR", "AF", // Cameroon + "CAN", "NOAM", "NA", // Canada + "CPV", "NAFR", "AF", // Cape Verde + "CYM", "CARB", "NA", // Cayman Islands + "CAF", "CAFR", "AF", // Central African Republic + "TCD", "CAFR", "AF", // Chad + "CHL", "SOAM", "SA", // Chile + "CHN", "****", "AS", // China + "HKG", "****", "AS", // Hong Kong Special Administrative Region of China + "MAC", "****", "AS", // Macao Special Administrative Region of China + "COL", "****", "SA", // Colombia + "COM", "SAFR", "AF", // Comoros + "COG", "****", "AF", // Congo + "COK", "SPAC", "OC", // Cook Islands + "CRI", "CEAM", "NA", // Costa Rica + "CIV", "CAFR", "AF", // Côte d'Ivoire + "HRV", "EURO", "EU", // Croatia + "CUB", "CARB", "NA", // Cuba + "CYP", "EURO", "EU", // Cyprus + "CZE", "EURO", "EU", // Czech Republic + "PRK", "****", "AS", // Democratic People's Republic of Korea + "COD", "****", "AF", // Democratic Republic of the Congo + "DNK", "EURO", "EU", // Denmark + "DJI", "NAFR", "AF", // Djibouti + "DMA", "CARB", "NA", // Dominica + "DOM", "CARB", "NA", // Dominican Republic + "ECU", "SOAM", "SA", // Ecuador + "EGY", "MDLE", "AF", // Egypt + "SLV", "CEAM", "NA", // El Salvador + "GNQ", "CAFR", "AF", // Equatorial Guinea + "ERI", "****", "AF", // Eritrea + "EST", "EURO", "EU", // Estonia + "ETH", "****", "AF", // Ethiopia + "FRO", "EURO", "EU", // Faeroe Islands + "FLK", "SOAM", "SA", // Falkland Islands (Malvinas) + "FJI", "SPAC", "OC", // Fiji + "FIN", "EURO", "EU", // Finland + "FRA", "EURO", "EU", // France + "GUF", "SOAM", "SA", // French Guiana + "PYF", "SPAC", "OC", // French Polynesia + "GAB", "CAFR", "AF", // Gabon + "GMB", "CAFR", "AF", // Gambia + "GEO", "ASIA", "AS", // Georgia + "DEU", "EURO", "EU", // Germany + "GHA", "CAFR", "AF", // Ghana + "GIB", "EURO", "EU", // Gibraltar + "GRC", "EURO", "EU", // Greece + "GRL", "EURO", "EU", // Greenland + "GRD", "CARB", "NA", // Grenada + "GLP", "CARB", "NA", // Guadeloupe + "GUM", "SPAC", "OC", // Guam + "GTM", "CEAM", "NA", // Guatemala + "GGY", "EURO", "EU", // Guernsey + "GIN", "CAFR", "AF", // Guinea + "GNB", "CAFR", "AF", // Guinea-Bissau + "GUY", "SOAM", "SA", // Guyana + "HTI", "CARB", "NA", // Haiti + "VAT", "EURO", "EU", // Holy See + "HND", "CEAM", "NA", // Honduras + "HUN", "EURO", "EU", // Hungary + "ISL", "EURO", "EU", // Iceland + "IND", "INDI", "AS", // India + "IDN", "****", "AS", // Indonesia + "IRN", "MDLE", "AS", // Iran (Islamic Republic of) + "IRQ", "MDLE", "AS", // Iraq + "IRL", "EURO", "EU", // Ireland + "IMN", "EURO", "EU", // Isle of Man + "ISR", "MDLE", "AS", // Israel + "ITA", "EURO", "EU", // Italy + "JAM", "CEAM", "NA", // Jamaica + "JPN", "****", "AS", // Japan + "JEY", "EURO", "EU", // Jersey + "JOR", "MDLE", "AS", // Jordan + "KAZ", "****", "AS", // Kazakhstan + "KEN", "****", "AF", // Kenya + "KIR", "EPAC", "OC", // Kiribati + "KWT", "MDLE", "AS", // Kuwait + "KGZ", "ASIA", "AS", // Kyrgyzstan + "LAO", "ASIA", "AS", // Lao People's Democratic Republic + "LVA", "EURO", "EU", // Latvia + "LBN", "MDLE", "AS", // Lebanon + "LSO", "SAFR", "AF", // Lesotho + "LBR", "CAFR", "AF", // Liberia + "LBY", "MDLE", "AS", // Libyan Arab Jamahiriya + "LIE", "EURO", "EU", // Liechtenstein + "LTU", "EURO", "EU", // Lithuania + "LUX", "EURO", "EU", // Luxembourg + "MDG", "SAFR", "AF", // Madagascar + "MWI", "SAFR", "AF", // Malawi + "MYS", "ASIA", "AS", // Malaysia + "MDV", "INDI", "AS", // Maldives + "MLI", "CAFR", "AF", // Mali + "MLT", "EURO", "EU", // Malta + "MHL", "WPAC", "OC", // Marshall Islands + "MTQ", "CARB", "NA", // Martinique + "MRT", "NAFR", "AF", // Mauritania + "MUS", "SAFR", "AF", // Mauritius + "MYT", "SAFR", "AF", // Mayotte + "MEX", "****", "NA", // Mexico + "FSM", "WPAC", "OC", // Micronesia (Federated States of) + "MCO", "EURO", "EU", // Monaco + "MNG", "****", "AS", // Mongolia + "MNE", "EURO", "EU", // Montenegro + "MSR", "CARB", "NA", // Montserrat + "MAR", "NAFR", "AF", // Morocco + "MOZ", "SAFR", "AF", // Mozambique + "MMR", "ASIA", "AS", // Myanmar + "NAM", "****", "AF", // Namibia + "NRU", "WPAC", "OC", // Nauru + "NPL", "****", "AS", // Nepal + "NLD", "EURO", "EU", // Netherlands + "ANT", "CARB", "NA", // Netherlands Antilles + "NCL", "SPAC", "OC", // New Caledonia + "NZL", "AUNZ", "OC", // New Zealand + "NIC", "****", "SA", // Nicaragua + "NER", "NAFR", "AF", // Niger + "NGA", "****", "AF", // Nigeria + "NIU", "SPAC", "OC", // Niue + "NFK", "SPAC", "OC", // Norfolk Island + "MNP", "WPAC", "OC", // Northern Mariana Islands + "NOR", "EURO", "EU", // Norway + "PSE", "MDLE", "AS", // Occupied Palestinian Territory + "OMN", "MDLE", "AS", // Oman + "PAK", "INDI", "AS", // Pakistan + "PLW", "SPAC", "OC", // Palau + "PAN", "CEAM", "SA", // Panama + "PNG", "SPAC", "OC", // Papua New Guinea + "PRY", "SOAM", "SA", // Paraguay + "PER", "SOAM", "SA", // Peru + "PHL", "ASIA", "AS", // Philippines + "PCN", "SPAC", "OC", // Pitcairn + "POL", "EURO", "EU", // Poland + "PRT", "EURO", "EU", // Portugal + "PRI", "CARB", "NA", // Puerto Rico + "QAT", "MDLE", "AS", // Qatar + "KOR", "ASIA", "AS", // Republic of Korea + "MDA", "EURO", "EU", // Republic of Moldova + "REU", "SAFR", "AF", // Réunion + "ROU", "EURO", "EU", // Romania + "RUS", "ASIA", "AS", // Russian Federation + "RWA", "****", "AF", // Rwanda + "BLM", "CARB", "NA", // Saint-Barthélemy + "SHN", "SOAM", "SA", // Saint Helena + "KNA", "CARB", "NA", // Saint Kitts and Nevis + "LCA", "CARB", "NA", // Saint Lucia + "MAF", "CARB", "NA", // Saint-Martin (French part) + "SPM", "NOAM", "NA", // Saint Pierre and Miquelon + "VCT", "CARB", "NA", // Saint Vincent and the Grenadines + "WSM", "SPAC", "OC", // Samoa + "SMR", "EURO", "EU", // San Marino + "STP", "CAFR", "AF", // Sao Tome and Principe + "SAU", "MDLE", "AS", // Saudi Arabia + "SEN", "CAFR", "AF", // Senegal + "SRB", "EURO", "EU", // Serbia + "SYC", "SAFR", "AF", // Seychelles + "SLE", "****", "AF", // Sierra Leone + "SGP", "****", "AS", // Singapore + "SVK", "EURO", "EU", // Slovakia + "SVN", "EURO", "EU", // Slovenia + "SLB", "SPAC", "OC", // Solomon Islands + "SOM", "****", "AF", // Somalia + "ZAF", "SAFR", "AF", // South Africa + "ESP", "EURO", "EU", // Spain + "LKA", "INDE", "AS", // Sri Lanka + "SDN", "****", "AF", // Sudan + "SUR", "SOAM", "SA", // Suriname + "SJM", "EURO", "EU", // Svalbard and Jan Mayen Islands + "SWZ", "****", "AF", // Swaziland + "SWE", "EURO", "EU", // Sweden + "CHE", "EURO", "EU", // Switzerland + "SYR", "MDLE", "AS", // Syrian Arab Republic + "TJK", "ASIA", "AS", // Tajikistan + "THA", "****", "AS", // Thailand + "MKD", "EURO", "EU", // The former Yugoslav Republic of Macedonia + "TLS", "ASIA", "AS", // Timor-Leste + "TGO", "CAFR", "AF", // Togo + "TKL", "AUNZ", "OC", // Tokelau + "TON", "SPAC", "OC", // Tonga + "TTO", "CARB", "NA", // Trinidad and Tobago + "TUN", "****", "AF", // Tunisia + "TUR", "EURO", "EU", // Turkey + "TKM", "****", "AS", // Turkmenistan + "TCA", "CARB", "NA", // Turks and Caicos Islands + "TUV", "SPAC", "OC", // Tuvalu + "UGA", "****", "AF", // Uganda + "UKR", "EURO", "EU", // Ukraine + "ARE", "MDLE", "AS", // United Arab Emirates + "GBR", "EURO", "EU", // United Kingdom of Great Britain and Northern Ireland + "TZA", "****", "AF", // United Republic of Tanzania + "USA", "NOAM", "NA", // United States of America + "VIR", "CARB", "NA", // United States Virgin Islands + "URY", "SOAM", "SA", // Uruguay + "UZB", "ASIA", "AS", // Uzbekistan + "VUT", "SPAC", "OC", // Vanuatu + "VEN", "SOAM", "SA", // Venezuela (Bolivarian Republic of) + "VNM", "****", "AS", // Viet Nam + "WLF", "SPAC", "OC", // Wallis and Futuna Islands + "ESH", "****", "AF", // Western Sahara + "YEM", "****", "AF", // Yemen + "ZMB", "SAFR", "AF", // Zambia + "ZWE", "SAFR", "AF" // Zimbabwe +}; +char ** AliasText; +struct ALIAS ** Aliases; + +struct ALIAS ** NTSAliases = NULL; + +/*struct ALIAS Aliases[] = +{ + "AMSAT", "WW", + "USBBS", "USA", + "ALLUS", "USA"}; +*/ + +int NumberofContinents = sizeof(Continents)/sizeof(Continents[1]); +int NumberofCountries = sizeof(Countries)/sizeof(Countries[1]); + +struct Continent * FindContinent(char * Name) +{ + int i; + struct Continent * Cont; + + for(i=0; i< NumberofContinents; i++) + { + Cont = &Continents[i]; + + if ((_stricmp(Name, Cont->FourCharCode) == 0) || (_stricmp(Name, Cont->TwoCharCode) == 0)) + return Cont; + } + + return NULL; +} + +struct Country * FindCountry(char * Name) +{ + int i; + struct Country * Cont; + + for(i=0; i< NumberofCountries; i++) + { + Cont = &Countries[i]; + + if (_stricmp(Name, Cont->Country) == 0) + return Cont; + } + + return NULL; +} + +struct ALIAS * FindAlias(char * Name) +{ + struct ALIAS ** Alias; + + Alias = Aliases; + + while(Alias[0]) + { + if (_stricmp(Name, Alias[0]->Dest) == 0) + return Alias[0]; + + Alias++; + } + + return NULL; +} + +VOID SetupMyHA() +{ + int Elements = 1; // Implied WW on front + char * ptr2; + struct Continent * Continent; + + strcpy(MyRouteElements, HRoute); + + // Split it up + + ptr2 = MyRouteElements + strlen(MyRouteElements) - 1; + + if (HRoute[0]) + { + do + { + while ((*ptr2 != '.') && (ptr2 > MyRouteElements)) + ptr2 --; + + if (ptr2 == MyRouteElements) + { + // End + + MyElements[Elements++] = _strdup(ptr2); + break; + } + + MyElements[Elements++] = _strdup(ptr2+1); + *ptr2 = 0; + + } while(Elements < 19); // Just in case! + } + + MyElements[Elements++] = _strdup(BBSName); + + MyElementCount = Elements; + + if (MyElements[1]) + { +#ifdef TWOCHARCONT + if (strlen(MyElements[1]) == 4) + { + // Convert to 2 char Continent; + Continent = FindContinent(MyElements[1]); + if (Continent) + { + free(MyElements[1]); + MyElements[1] = _strdup(Continent->TwoCharCode); + } + } +#else + if (strlen(MyElements[1]) == 2) + { + // Convert to 4 char Continent; + Continent = FindContinent(MyElements[1]); + if (Continent) + { + free(MyElements[1]); + MyElements[1] = _strdup(Continent->FourCharCode); + } + } +#endif + } +} + +VOID SetupNTSAliases(char * FN) +{ + FILE *in; + char Buffer[2048]; + char *buf = Buffer; + int Count = 0; + char seps[] = " ,:/\t"; + char * Dest, * Alias, * Context; + + NTSAliases = zalloc(sizeof(struct ALIAS)); + + in = fopen(FN, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + strlop(buf, '\r'); // in case Windows Format + + Dest = _strdup(buf); // strtok changes it + + Dest = strtok_s(Dest, seps, &Context); + if (Dest == NULL) + continue; + + Alias = strtok_s(NULL, seps, &Context); + if (Alias == NULL) + continue; + + if (strlen(Dest) < 3 && (strchr(Dest, '*') == 0)) + continue; + + NTSAliases = realloc(NTSAliases, (Count+2)* sizeof(struct ALIAS)); + NTSAliases[Count] = zalloc(sizeof(struct ALIAS)); + + NTSAliases[Count]->Dest = Dest; + NTSAliases[Count]->Alias = Alias; + + Count++; + } + + NTSAliases[Count] = NULL; + fclose(in); + } +} + +VOID SetupFwdAliases() +{ + char ** Text = AliasText; + char * Dest, * Alias; + int Count = 0; + char seps[] = " ,:/"; + + Aliases = zalloc(sizeof(struct ALIAS)); + + if (Text) + { + while(Text[0]) + { + Aliases = realloc(Aliases, (Count+2)* sizeof(struct ALIAS)); + Aliases[Count] = zalloc(sizeof(struct ALIAS)); + + Dest = _strdup(Text[0]); // strtok changes it + + Dest = strtok_s(Dest, seps, &Alias); + + Aliases[Count]->Dest = Dest; + Aliases[Count]->Alias = Alias; + + Count++; + Text++; + } + Aliases[Count] = NULL; + } +} + +VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo) +{ + char * HText = ForwardingInfo->BBSHA; + char * SaveHText, * ptr2; + struct Continent * Continent; + int Elements = 1; + + ForwardingInfo->BBSHAElements = zalloc(8); // always NULL entry on end even if no values + + SaveHText = _strdup(HText); + + ptr2 = SaveHText + strlen(SaveHText) -1; + + ForwardingInfo->BBSHAElements[0] = _strdup(WW); + + do + { + ForwardingInfo->BBSHAElements = realloc(ForwardingInfo->BBSHAElements, (Elements+2) * sizeof(void *)); + + while ((*ptr2 != '.') && (ptr2 > SaveHText)) + { + ptr2 --; + } + + if (ptr2 == SaveHText) + { + // End + + ForwardingInfo->BBSHAElements[Elements++] = _strdup(ptr2); + break; + } + + ForwardingInfo->BBSHAElements[Elements++] = _strdup(ptr2+1); + *ptr2 = 0; + + } while(TRUE); + + ForwardingInfo->BBSHAElements[Elements++] = NULL; + + if (ForwardingInfo->BBSHAElements[1]) + { +#ifdef TWOCHARCONT + if (strlen(ForwardingInfo->BBSHAElements[1]) == 4) + { + // Convert to 2 char Continent; + Continent = FindContinent(ForwardingInfo->BBSHAElements[1]); + if (Continent) + { + free(ForwardingInfo->BBSHAElements[1]); + ForwardingInfo->BBSHAElements[1] = _strdup(Continent->TwoCharCode); + } + } +#else + if (strlen(ForwardingInfo->BBSHAElements[1]) == 2) + { + // Convert to 4 char Continent; + Continent = FindContinent(ForwardingInfo->BBSHAElements[1]); + if (Continent) + { + free(ForwardingInfo->BBSHAElements[1]); + ForwardingInfo->BBSHAElements[1] = _strdup(Continent->FourCharCode); + } + } +#endif + + } + + free(SaveHText); + +} + +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) +{ + int Count=0; + char ** HText = ForwardingInfo->Haddresses; + char * SaveHText, * ptr2; +// char * TopElement; + char * Num; + struct Continent * Continent; + + ForwardingInfo->HADDRS = zalloc(sizeof(void *)); // always NULL entry on end even if no values + ForwardingInfo->HADDROffet = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + while(HText[0]) + { + int Elements = 1; + ForwardingInfo->HADDRS = realloc(ForwardingInfo->HADDRS, (Count+2) * sizeof(void *)); + ForwardingInfo->HADDROffet = realloc(ForwardingInfo->HADDROffet, (Count+2) * sizeof(void *)); + + ForwardingInfo->HADDRS[Count] = zalloc(8); // Always at lesat WWW and NULL + + SaveHText = _strdup(HText[0]); + Num = strlop(SaveHText, ','); + + ptr2 = SaveHText + strlen(SaveHText) -1; + + ForwardingInfo->HADDRS[Count][0] = _strdup(WW); + + if (strcmp(HText[0], "WW") != 0) + { + do + { + ForwardingInfo->HADDRS[Count] = realloc(ForwardingInfo->HADDRS[Count], (Elements+2) * sizeof(void *)); + + while ((*ptr2 != '.') && (ptr2 > SaveHText)) + { + ptr2 --; + } + + if (ptr2 == SaveHText) + { + // End + + ForwardingInfo->HADDRS[Count][Elements++] = _strdup(ptr2); + break; + } + + ForwardingInfo->HADDRS[Count][Elements++] = _strdup(ptr2+1); + *ptr2 = 0; + + } while(TRUE); + } + + ForwardingInfo->HADDRS[Count][Elements++] = NULL; + + // If the route is not a complete HR (ie starting with a continent) + // Add elemets from BBS HA to complete it. + // (How do we know how many?? + // ?? Still ont sure about this ?? + // What i was trying to do was end up with a full HR, but knowing how many elements to use. + // Far simpler to config it, but can users cope?? + // Will config for testing. HA, N + +/* + TopElement=ForwardingInfo->HADDRS[Count][0]; + + if (strcmp(TopElement, MyElements[0]) == 0) + goto FullHR; + + if (FindContinent(TopElement)) + goto FullHR; + + // Need to add stuff from our HR + + Elements--; + + if (Elements < MyElementCount) + break; + +FullHR: +*/ + + ForwardingInfo->HADDROffet[Count] = (Num)? atoi(Num): 0; + + if (ForwardingInfo->HADDRS[Count][1]) + { +#ifdef TWOCHARCONT + if (strlen(ForwardingInfo->HADDRS[Count][1]) == 4) + { + // Convert to 2 char Continent; + Continent = FindContinent(ForwardingInfo->HADDRS[Count][1]); + if (Continent) + { + free(ForwardingInfo->HADDRS[Count][1]); + ForwardingInfo->HADDRS[Count][1] = _strdup(Continent->TwoCharCode); + } + } +#else + if (strlen(ForwardingInfo->HADDRS[Count][1]) == 2) + { + // Convert to 4 char Continent; + Continent = FindContinent(ForwardingInfo->HADDRS[Count][1]); + if (Continent) + { + free(ForwardingInfo->HADDRS[Count][1]); + ForwardingInfo->HADDRS[Count][1] = _strdup(Continent->FourCharCode); + } + } +#endif + + } + free(SaveHText); + HText++; + Count++; + } + + ForwardingInfo->HADDRS[Count] = NULL; +} + +VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo) +{ + int Count=0; + char ** HText = ForwardingInfo->HaddressesP; + char * SaveHText, * ptr2; +// char * TopElement; + char * Num; + struct Continent * Continent; + + ForwardingInfo->HADDRSP = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + while(HText[0]) + { + int Elements = 1; + ForwardingInfo->HADDRSP = realloc(ForwardingInfo->HADDRSP, (Count+2) * sizeof(void *)); + + ForwardingInfo->HADDRSP[Count] = zalloc(2 * sizeof(void *)); // Always at lesat WWW and NULL + + SaveHText = _strdup(HText[0]); + Num = strlop(SaveHText, ','); + + ptr2 = SaveHText + strlen(SaveHText) -1; + + ForwardingInfo->HADDRSP[Count][0] = _strdup(WW); + + if (strcmp(HText[0], "WW") != 0) + { + do + { + ForwardingInfo->HADDRSP[Count] = realloc(ForwardingInfo->HADDRSP[Count], (Elements+2) * sizeof(void *)); + + while ((*ptr2 != '.') && (ptr2 > SaveHText)) + { + ptr2 --; + } + + if (ptr2 == SaveHText) + { + // End + + ForwardingInfo->HADDRSP[Count][Elements++] = _strdup(ptr2); + break; + } + + ForwardingInfo->HADDRSP[Count][Elements++] = _strdup(ptr2+1); + *ptr2 = 0; + + } while(TRUE); + } + + ForwardingInfo->HADDRSP[Count][Elements++] = NULL; + + if (ForwardingInfo->HADDRSP[Count][1]) + { +#ifdef TWOCHARCONT + if (strlen(ForwardingInfo->HADDRSP[Count][1]) == 4) + { + // Convert to 2 char Continent; + Continent = FindContinent(ForwardingInfo->HADDRSP[Count][1]); + if (Continent) + { + free(ForwardingInfo->HADDRSP[Count][1]); + ForwardingInfo->HADDRSP[Count][1] = _strdup(Continent->TwoCharCode); + } + } +#else + if (strlen(ForwardingInfo->HADDRSP[Count][1]) == 2) + { + // Convert to 4 char Continent; + Continent = FindContinent(ForwardingInfo->HADDRSP[Count][1]); + if (Continent) + { + free(ForwardingInfo->HADDRSP[Count][1]); + ForwardingInfo->HADDRSP[Count][1] = _strdup(Continent->FourCharCode); + } + } +#endif + } + free(SaveHText); + HText++; + Count++; + } + + ForwardingInfo->HADDRSP[Count] = NULL; +} + +VOID CheckAndSend(struct MsgInfo * Msg, CIRCUIT * conn, struct UserInfo * bbs) +{ + struct BBSForwardingInfo * ForwardingInfo = bbs->ForwardingInfo; + + if (ForwardToMe || _stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! (unless ForwardToMe set) + { + if ((conn == NULL) || (!(conn->BBSFlags & BBS) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0))) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + if (ForwardingInfo->SendNew) + ForwardingInfo->FwdTimer = ForwardingInfo->FwdInterval; + } + } + else + { + // if User has Redirect to RMS set do it. + + struct UserInfo * user = LookupCall(Msg->to); + + if (user && user->flags & F_RMSREDIRECT) + { + user = FindBBS("RMS"); + + if (user) + { + CheckAndSend(Msg, conn, user); + } + + Logprintf(LOG_BBS, conn, '?', "Message matches this BBS and RMS Redirect set - fwd to RMS"); + + } + else + Logprintf(LOG_BBS, conn, '?', "Message matches this BBS and ForwardToMe not set - not queuing message"); + } +} + +VOID UpdateB2Dest(struct MsgInfo * Msg, char * Alias) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + if (stat(MsgFile, &STAT) == -1) + return; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return; + + MsgBytes=malloc(FileSize + 100); // A bit of space for alias substitution on B2 + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + if (MsgBytes) + { + char * To, * AT, * ATEND; + To = strstr(MsgBytes, "To:"); + + if (To) + { + ATEND = strchr(To, '\r'); + if (ATEND) + { + size_t i = ATEND - To; + AT = memchr(To, '@', i); + if (AT) + { + // Move Message up/down to make room for substitution + + size_t Diff = (ATEND - AT - 1) - strlen(Alias); + size_t BeforeAT = AT - MsgBytes + 1; + int j = 0; + + memmove(AT + 1, AT + Diff + 1, Msg->length - BeforeAT + 10); + memcpy(AT + 1, Alias, strlen(Alias)); + + j = 1; + Msg->length -= (int)Diff; + + // Write Back + + hFile = fopen(MsgFile, "wb"); + + if (hFile == NULL) + return; + + + fwrite(MsgBytes, 1, Msg->length, hFile); + fclose(hFile); + } + } + } + } +} + +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) +{ + struct UserInfo * bbs; + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo; + char ATBBS[41]=""; + char RouteElements[41]; + int Count = 0; + char * HElements[20] = {NULL}; + int Elements = 0; + char * ptr2; + int MyElement = 0; + BOOL Flood = FALSE; + char FullRoute[100]; + struct Continent * Continent; + struct Country * Country; + struct ALIAS * Alias; + struct UserInfo * RMS; + int toLen; + + if (Msg->status == 'K') + return 1; // No point, but don't want no route warning + + strcpy(RouteElements, Msg->via); + + Logprintf(LOG_BBS, conn, '?', "Msg %d Routing Trace To %s Via %s", Msg->number, Msg->to, RouteElements); + + // "NTS" Alias substitution is now done on P and T before any other processing + + // No, Not a good idea to use same aliss file for NTS and other messages + // (What about OT 2*?) + // If we need to alias other types, add a new file + + if (strchr(RouteElements, '!')) // Source Route ("Bang Routing") + { + char * bang = &RouteElements[strlen(RouteElements)]; + + while (*(--bang) != '!'); // Find last ! + + *(bang) = 0; // remove it; + _strupr(++bang); + + strcpy(Msg->via, RouteElements); // Remove from via + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (_stricmp(bbs->Call, bang) == 0) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace Source Route via %s", bbs->Call); + CheckAndSend(Msg, conn, bbs); + return 1; + } + } + + if (conn && !(conn->BBSFlags & BBS)) + nodeprintf(conn, "Routing via %s requested but %s not known to this BBS\r", bang, bang); + + Logprintf(LOG_BBS, conn, '?', "Routing via %s requested but %s not known to this BBS", bang, bang); + } + + if (Msg->type == 'T') + { + strlop(RouteElements, '.'); + + Alias = CheckForNTSAlias(Msg, RouteElements); + + if (Alias) + { + // Replace the AT in the message with the Alias + + Logprintf(LOG_BBS, conn, '?', "Routing Trace @%s taken from Alias File", Alias->Alias); + strcpy(Msg->via, Alias->Alias); + if (Msg->B2Flags & B2Msg) + UpdateB2Dest(Msg, Alias->Alias); + + SaveMessageDatabase(); + } + + strcpy(RouteElements, Msg->via); // May have changed + } + + // See if AMPR.ORG Mail + + // Note message is probably already queued to SMTP or RMS + + // If for our domain leave here + + toLen = (int)strlen(Msg->via); + + if (_memicmp(&Msg->via[toLen - 8], "ampr.org", 8) == 0) + { + // message is for ampr.org + + char toCall[48]; + char * via; + + strcpy(toCall, _strupr(Msg->via)); + + via = strlop(toCall, '@'); + + if (via == NULL) + { + // To and VIA already set up. This should only happen if message is for us + + Logprintf(LOG_BBS, conn, '?', "Routing Trace at %s is for us. Leave Here", AMPRDomain); + return 1; + } + + if (_stricmp(via, AMPRDomain) == 0) + { + // message is for us. Leave Here + + if (strlen(toCall) > 6) + toCall[6] = 0; + + strcpy(Msg->to, toCall); + strcpy(Msg->via, via); + Logprintf(LOG_BBS, conn, '?', "Routing Trace at %s is for us. Leave Here", AMPRDomain); + return 1; + } + + if (SendAMPRDirect) + { + // We want to send ampr mail direct to host. Queue to BBS AMPR + + bbs = FindAMPR(); + + if (bbs) + { + // We have bbs AMPR + + if (_stricmp(Msg->to, "RMS") == 0 || Msg->to[0] == 0) + { + // Was set to go via RMS or ISP - change it + + strcpy(Msg->to, "AMPR"); + } + + Logprintf(LOG_BBS, conn, '?', "Routing Trace to ampr.org Matches BBS AMPR"); + + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + bbs->ForwardingInfo->MsgCount++; + if (bbs->ForwardingInfo->SendNew) + bbs->ForwardingInfo->FwdTimer = bbs->ForwardingInfo->FwdInterval; + + return 1; + } + + // To AMPR, but don't have a BBS called AMPR - dropthrough in case we are forwarding AMPR to another BBS + } + } + +// See if sending @ winlink.org + + if (_stricmp(Msg->to, "RMS") == 0) + { + // If a user of this bbs with Poll RMS set, leave it here - no point in sending to winlink + + // To = RMS could come from RMS:EMAIL Address. If so, we only check user if @winlink.org, or + // we will hold g8bpq@g8bpq.org.uk + + char * Call; + char * AT; + + Call = _strupr(_strdup(Msg->via)); + AT = strlop(Call, '@'); + + if (AT) + { + if (_stricmp(AT, "WINLINK.ORG") == 0) + { + user = LookupCall(Call); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "Message @ winlink.org, but local RMS user - leave here"); + strcpy(Msg->to, Call); + strcpy(Msg->via, AT); + + free(Call); + + if (user->flags & F_BBS) // User is also a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + return 1; + } + } + } + } + free(Call); + + // To = RMS, but not winlink.org, or not local user. + + RMS = FindRMS(); + + if (RMS) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace to RMS Matches BBS RMS"); + + set_fwd_bit(Msg->fbbs, RMS->BBSNumber); + RMS->ForwardingInfo->MsgCount++; + if (RMS->ForwardingInfo->SendNew) + RMS->ForwardingInfo->FwdTimer = RMS->ForwardingInfo->FwdInterval; + + return 1; + } + + // To RMS, but don't have a BBS called RMS - dropthrough in case we are forwarding RMS to another BBS + + } + + if (_stricmp(RouteElements, "WINLINK.ORG") == 0) + { + user = LookupCall(Msg->to); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace @ winlink.org, but local RMS user - leave here"); + + if (user->flags & F_BBS) // User is also a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + return 1; // Route found + } + } + + RMS = FindRMS(); + + if (RMS) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace @ winlink.org Matches BBS RMS"); + + set_fwd_bit(Msg->fbbs, RMS->BBSNumber); + RMS->ForwardingInfo->MsgCount++; + if (RMS->ForwardingInfo->SendNew) + RMS->ForwardingInfo->FwdTimer = RMS->ForwardingInfo->FwdInterval; + + return 1; + } + Logprintf(LOG_BBS, conn, '?', "Routing Trace - @ winlink.org but no BBS RMS"); + return 0; + } + + + // See if a well-known alias + + Alias = FindAlias(RouteElements); + + if (Alias) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace Alias Substitution %s > %s", + RouteElements, Alias->Alias); + + strcpy(RouteElements, Alias->Alias); + + if (conn) + if ((ReaddressReceived && (conn->BBSFlags & BBS)) || (ReaddressLocal && ((conn->BBSFlags & BBS) == 0))) + strcpy(Msg->via, Alias->Alias); + } + + // Make sure HA is complete (starting at WW) + + if (RouteElements[0] == 0) + { + // Just a TO. If a Bull, set flood + + if (Msg->type == 'B') + Flood = TRUE; + + goto NOHA; + } + + ptr2 = RouteElements + strlen(RouteElements) - 1; + + while ((*ptr2 != '.') && (ptr2 > RouteElements)) + { + ptr2 --; + } + + if (ptr2 != RouteElements) + *ptr2++; + + if ((strcmp(ptr2, "WW") == 0) || (strcmp(ptr2, "WWW") == 0)) + { + strcpy(FullRoute, RouteElements); + goto FULLHA; + } + + if (FindContinent(ptr2)) + { + // Just need to add WW + + sprintf_s(FullRoute, sizeof(FullRoute),"%s.WW", RouteElements); + goto FULLHA; + } + + Country = FindCountry(ptr2); + + if (Country) + { + // Just need to add Continent and WW +#ifdef TWOCHARCONT + sprintf_s(FullRoute, sizeof(FullRoute),"%s.%s.WW", RouteElements, Country->Continent2); +#else + sprintf_s(FullRoute, sizeof(FullRoute),"%s.%s.WW", RouteElements, Country->Continent4); +#endif + goto FULLHA; + } + + // Don't know + + // Assume a local dis list, and set Flood. + + strcpy(FullRoute, RouteElements); + + if (Msg->type == 'B') + Flood = TRUE; + +FULLHA: + + strcpy(ATBBS, FullRoute); + + strlop(ATBBS, '.'); + + if (FullRoute) + { + // Split it up + + ptr2 = FullRoute + strlen(FullRoute) - 1; + + do + { + while ((*ptr2 != '.') && (ptr2 > FullRoute)) + { + ptr2 --; + } + + if (ptr2 != FullRoute) + *ptr2++ = 0; + + HElements[Elements++] = ptr2; + + + } while(Elements < 20 && ptr2 != FullRoute); // Just in case! + } + + if (HElements[1]) + { +#ifdef TWOCHARCONT + if (strlen(HElements[1]) == 4) + { + // Convert to 2 char Continent; + Continent = FindContinent(HElements[1]); + if (Continent) + { +// free(MyElements[1]); + HElements[1] = _strdup(Continent->TwoCharCode); + } + } +#else + if (strlen(HElements[1]) == 2) + { + // Convert to 4 char Continent; + Continent = FindContinent(HElements[1]); + if (Continent) + { +// free(MyElements[1]); + HElements[1] = _strdup(Continent->FourCharCode); + } + } +#endif + } + + + // if Bull, see if reached right area + + if (Msg->type == 'B') + { + int i = 0; + + // All elements of Helements must match Myelements + + while (MyElements[i] && HElements[i]) // Until one set runs out + { + if (strcmp(MyElements[i], HElements[i]) != 0) + break; + i++; + } + + // if HElements[i+1] = NULL, have matched all + + if (HElements[i] == NULL) + Flood = TRUE; + } + +NOHA: + + Logprintf(LOG_BBS, conn, '?', "Routing Trace Type %c %sTO %s VIA %s Route On %s %s %s %s %s", + Msg->type, (Flood) ? "(Flood) ":"", Msg->to, Msg->via, HElements[0], + HElements[1], HElements[2], HElements[3], HElements[4]); + + + if (Msg->type == 'T') + { + int depth = 0; + int bestmatch = -1; + struct UserInfo * bestbbs = NULL; + + // if NTS Traffic. Route on Wildcarded TO (best match). + + // If no match, route on AT (Should be NTSxx) + + // If no match, send to any BBS with routing to state XX and NTS flag set (no we dont!) + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS Matches TO BBS %s Length %d", bbs->Call, depth); + + if (depth > bestmatch) + { + bestmatch = depth; + bestbbs = bbs; + } + } + } + if (bestbbs) + { + if (bestbbs->flags & F_NTSMPS) + Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS Best Match is %s, but NTS MPS Set so not queued", bestbbs->Call); + else + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS Best Match is %s", bestbbs->Call); + CheckAndSend(Msg, conn, bestbbs); + } + return 1; + } + + // Check AT + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + if (bbs->flags & F_NTSMPS) + Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS %s Matches AT %s, but NTS MPS Set so not queued", ATBBS, bbs->Call); + else + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS %s Matches AT %s", ATBBS, bbs->Call); + CheckAndSend(Msg, conn, bbs); + } + return 1; + } + } + goto CheckWildCardedAT; + } + + + if (Msg->type == 'P' || Flood == 0) + { + // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA, + // and choose the best match on HA + + // now has option to send P messages to more than one BBS + + struct UserInfo * bestbbs = NULL; + int bestmatch = 0; + int depth; + int Matched = 0; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (ForwardingInfo->PersonalOnly && (Msg->type != 'P') && (Msg->type != 'T')) + continue; + + if (CheckBBSToList(Msg, bbs, ForwardingInfo)) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace TO %s Matches BBS %s", Msg->to, bbs->Call); + + CheckAndSend(Msg, conn, bbs); + + Matched++; + if (SendPtoMultiple && Msg->type == 'P') + continue; + + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (ForwardingInfo->PersonalOnly && (Msg->type != 'P')) + continue; + + // Check implied AT + + if ((strcmp(ATBBS, bbs->Call) == 0)) // @BBS = BBS + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace %s Matches implied AT %s", ATBBS, bbs->Call); + + CheckAndSend(Msg, conn, bbs); + + Matched++; + if (SendPtoMultiple && Msg->type == 'P') + continue; + + return 1; + } + + // Check AT + + if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace %s Matches AT %s", ATBBS, bbs->Call); + + CheckAndSend(Msg, conn, bbs); + + Matched++; + if (SendPtoMultiple && Msg->type == 'P') + continue; + + return 1; + } + } + + if (Matched) + { + // Should only get here if Sending P to Multiples is enabled + // and we have at least one forward. + + return 1; + } + + // We should choose the BBS with most matching elements (ie match on #23.GBR better that GBR) + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (ForwardingInfo->PersonalOnly && (Msg->type != 'P')) + continue; + + depth = CheckBBSHElements(Msg, bbs, ForwardingInfo, ATBBS, &HElements[0]); + + if (depth) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace HR Matches BBS %s Depth %d", bbs->Call, depth); + + if (depth > bestmatch) + { + bestmatch = depth; + bestbbs = bbs; + } + } + } + if (bestbbs) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace HR Best Match is %s", bestbbs->Call); + + CheckAndSend(Msg, conn, bestbbs); + + return 1; + } + + // Check for wildcarded AT address + +// if (ATBBS[0] == 0) +// return FALSE; // no AT + +CheckWildCardedAT: + + depth = 0; + bestmatch = -1; + bestbbs = NULL; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (ForwardingInfo->PersonalOnly && (Msg->type != 'P')) + continue; + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, ATBBS); + + if (depth > -1) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace Wildcarded AT Matches %s Length %d", bbs->Call, depth); + + if (depth > bestmatch) + { + bestmatch = depth; + bestbbs = bbs; + } + } + } + if (bestbbs) + { + if (Msg->type == 'T' && (bestbbs->flags & F_NTSMPS)) + Logprintf(LOG_BBS, conn, '?', "Routing Trace Wildcarded AT Best Match is %s, but NTS Msg and MPS Set so not queued", bestbbs->Call); + else + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace Wildcarded AT Best Match is %s", bestbbs->Call); + CheckAndSend(Msg, conn, bestbbs); + } + return 1; + } + + Logprintf(LOG_BBS, conn, '?', "Routing Trace - No Match"); + return FALSE; // No match + } + + // Flood Bulls go to all matching BBSs in the flood area, so the order of checking doesn't matter + + // For now I will only route on AT (for non-hierarchical addresses) and HA + + // Ver 1.0.4.2 - Try including TO + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (ForwardingInfo->PersonalOnly) + continue; + + if (CheckBBSToList(Msg, bbs, ForwardingInfo)) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace TO %s Matches BBS %s", Msg->to, bbs->Call); + + if (ForwardToMe || _stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! (unless ForwardToMe set) + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + Count++; + continue; + } + + if ((strcmp(ATBBS, bbs->Call) == 0) || // @BBS = BBS + CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace AT %s Matches BBS %s", Msg->to, bbs->Call); + CheckAndSend(Msg, conn, bbs); + + Count++; + continue; + } + + + if (CheckBBSHElementsFlood(Msg, bbs, ForwardingInfo, Msg->via, &HElements[0])) + { + Logprintf(LOG_BBS, conn, '?', "Routing Trace HR %s %s %s %s %s Matches BBS %s", + HElements[0], HElements[1], HElements[2], + HElements[3], HElements[4], bbs->Call); + + CheckAndSend(Msg, conn, bbs); + + Count++; + } + + } + + if (Count == 0) + goto CheckWildCardedAT; + + Logprintf(LOG_BBS, conn, '?', "Routing Trace - No Match"); + + return Count; +} + +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Calls; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) +{ + char ** Calls; + + // Check AT distributions + +// if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS +// return TRUE; + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +int CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements) +{ + // Used for Personal Messages, and Bulls not yot at their target area + + char *** HRoutes; + int i = 0, j, k = 0; + int bestmatch = 0; + + if (ForwardingInfo->HADDRSP) + { + // Match on Routes + + HRoutes = ForwardingInfo->HADDRSP; + k=0; + + while(HRoutes[k]) + { + i = j = 0; + + while (HRoutes[k][i] && HElements[j]) // Until one set runs out + { + if (strcmp(HRoutes[k][i], HElements[j]) != 0) + break; + i++; + j++; + } + + // Only send if all BBS elements match + + if (HRoutes[k][i] == 0) + { + if (i > bestmatch) + bestmatch = i; + } + k++; + } + } + return bestmatch; +} + + +int CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements) +{ + char *** HRoutes; + char ** BBSHA; + + int i = 0, j, k = 0; + int bestmatch = 0; + + if (ForwardingInfo->HADDRS) + { + // Match on Routes + + // Message must be in right area (all elements of message match BBS Location HA) + + BBSHA = ForwardingInfo->BBSHAElements; + + if (BBSHA == NULL) + return 0; // Not safe to flood + + i = j = 0; + + while (BBSHA[i] && HElements[j]) // Until one set runs out + { + if (strcmp(BBSHA[i], HElements[j]) != 0) + break; + i++; + j++; + } + + if (HElements[j] != 0) + return 0; // Message is not for BBS's area + + HRoutes = ForwardingInfo->HADDRS; + k=0; + + while(HRoutes[k]) + { + i = j = 0; + + while (HRoutes[k][i] && HElements[j]) // Until one set runs out + { + if (strcmp(HRoutes[k][i], HElements[j]) != 0) + break; + i++; + j++; + } + + if (i > bestmatch) + { + // As Flooding, only match if all elements match, and elements matching > offset + + // As Flooding, only match if all elements from BBS are matched + // ie if BBS has #23.gbr.eu, and msg gbr.eu, don't match + // if BBS has gbr.eu, and msg #23.gbr.eu, ok (so long as bbs in in #23, checked above. + + + if (HRoutes[k][i] == 0) + bestmatch = i; + } + k++; + } + } + return bestmatch; +} + + +int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Calls; + char * Call; + char * ptr; + int bestmatch = -1; + int MatchLen = 0; + + // Look for Matches on TO using Wildcarded Addresses. Intended for use with NTS traffic, with TO = ZIPCode + + // We forward to the BBS with the most specific match - ie minimum *'s in match + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + BOOL Invert = FALSE; // !Match + + Call = Calls[0]; + + if (Call[0] == '!' || Call[0] == '-') + { + Call++; + Invert = TRUE; + } + + ptr = strchr(Call, '*'); + + if (ptr) + { + MatchLen = ptr - Call; + + if (memcmp(Msg->to, Call, MatchLen) == 0) + { + // Match - de we have a better one? + + // if it is a !Match, return without checking any more + + if (Invert) + return -1; + + if (MatchLen > bestmatch) + bestmatch = MatchLen; + } + } + else + { + //no star - just do a normal compare + + if (strcmp(Msg->to, Call) == 0) + { + if (Invert) + return -1; + + MatchLen = (int)strlen(Call); + if (MatchLen > bestmatch) + bestmatch = MatchLen; + } + } + + Calls++; + } + } + return (int)bestmatch; +} + + +int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) +{ + char ** Calls; + char * Call; + char * ptr; + int bestmatch = -1; // must be signed! + int MatchLen = 0; + + // Look for Matches on AT using Wildcarded Addresses. Only applied after all other checks fail. Intended mainly + // for setting a default route, but could have other uses + + // We forward to the BBS with the most specific match - ie minimum *'s in match + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + Call = Calls[0]; + ptr = strchr(Call, '*'); + + // only look if * present - we have already tried routing on the full AT + + if (ptr) + { + MatchLen = ptr - Call; + + if (memcmp(ATBBS, Call, MatchLen) == 0) + { + // Match - de we have a better one? + + if (MatchLen > bestmatch) + bestmatch = MatchLen; + } + } + + Calls++; + } + } + return (int)bestmatch; +} + +struct ALIAS * CheckForNTSAlias(struct MsgInfo * Msg, char * ATFirstElement) +{ + struct ALIAS ** Alias = NTSAliases; + char * Call; + char * ptr; + size_t MatchLen = 0; + + // We have a list of wildcarded TO (ie Zip Codes) and corresponding replacement NTSXX + + // It seems the NTS people want to match on either TO or DEST, and replace the AT + // and to use only the first matching + + while(Alias[0]) + { + Call = Alias[0]->Dest; + + if (Call == NULL) + break; + + ptr = strchr(Call, '*'); + + if (ptr) + { + MatchLen = ptr - Call; + + if (memcmp(Msg->to, Call, MatchLen) == 0) + return(Alias[0]); + } + else + { + //no star - just do a normal compare + + if (strcmp(Msg->to, Call) == 0) + return(Alias[0]); + } + + // Try AT + + if (ATFirstElement && ATFirstElement[0]) + { + if (ptr) + { + if (memcmp(ATFirstElement, Call, MatchLen) == 0) + return(Alias[0]); + } + else + { + if (strcmp(ATFirstElement, Call) == 0) + return(Alias[0]); + } + } + + Alias++; + } + + return NULL; +} + + +VOID ReRouteMessages() +{ + // Pass all messages to the Mail Routing routine. + + // Used if a new BBS is set up, or to readdress messages from a failed BBS + + struct MsgInfo * Msg; + int n; + char SaveStatus; + char Savefbbs[NBMASK]; + + for (n = 1; n <= NumberofMessages; n++) + { + Msg = MsgHddrPtr[n]; + + // if killed, or forwarded and not a bull, ignore + + // If status was F and we add anther BBS, set back to N + + if (Msg->status == 'K' || (Msg->status == 'F' && Msg->type != 'B')) + continue; + + + if (Msg->type == 'B') + SaveStatus = Msg->status; + + memcpy(Savefbbs, Msg->fbbs, NBMASK); + + memset(Msg->fbbs, 0, NBMASK); + + MatchMessagetoBBSList(Msg, NULL); + + if (memcmp(Savefbbs, Msg->fbbs, NBMASK) != 0) + { + // Have changed Message Routing + + // Clear fwd bits on any BBS it has been sent to + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + struct UserInfo * user; + + for (user = BBSChain; user; user = user->BBSNext) + { + if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) // for this BBS? + { + if (check_fwd_bit(Msg->forw, user->BBSNumber)) // Already sent? + clear_fwd_bit(Msg->fbbs, user->BBSNumber); + + } + } + + // If still some bits set, change to $ + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward > n) + FirstMessageIndextoForward = n; + + if (Msg->type == 'B') + Msg->status = '$'; + } + else + { + // all clear - set to N or F + + if (Msg->type == 'B') + { + if (memcmp(Msg->forw, zeros, NBMASK) == 0) + Msg->status = 'N'; // Not sent anywhere, and nowhere to send, so N + else + Msg->status = 'F'; + } + } + } + } + } + SaveMessageDatabase(); +} + + + + diff --git a/MailTCP.c b/MailTCP.c new file mode 100644 index 0000000..ebdaf77 --- /dev/null +++ b/MailTCP.c @@ -0,0 +1,4128 @@ +/* +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 +// +// TCP access module - POP and SMTP + +#include "bpqmail.h" + +VOID ReleaseSock(SOCKET sock); +void Base64EncodeAndSend(SocketConn * sockptr, UCHAR * Msg, int Len); + +#define MaxSockets 64 + +SocketConn * Sockets = NULL; + +int CurrentConnections; + +int CurrentSockets=0; + +#define MAX_PENDING_CONNECTS 4 + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +SOCKADDR_IN local_sin; /* Local socket - internet style */ + +PSOCKADDR_IN psin; + +SOCKET smtpsock, pop3sock; + +char szBuff[80]; + +int SMTPInPort; +int POP3InPort; + +BOOL RemoteEmail; // Set to listen on INADDR_ANY rather than LOCALHOST + +BOOL ISP_Gateway_Enabled; + +char MyDomain[50]; // Mail domain for BBS<>Internet Mapping + +char ISPSMTPName[50]; +char ISPEHLOName[50] = ""; + +int ISPSMTPPort; + +char ISPPOP3Name[50]; +int ISPPOP3Port; + +char ISPAccountName[50]; +char ISPAccountPass[50]; +char EncryptedISPAccountPass[100]; +int EncryptedPassLen; + +BOOL SMTPAuthNeeded; + +BOOL GMailMode = FALSE; +char GMailName[50]; + +int POP3Timer=9999; // Run on startup +int ISPPOP3Interval; + +BOOL SMTPMsgCreated=FALSE; // Set to cause SMTP client to send messages to ISP +BOOL SMTPActive=FALSE; // SO we don't try every 10 secs! + +char mycd64[256]; +static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +char sockTypes[6][12] = {"Undefined", "SMTPServer", "POP3Server", "SMTPClient", "POP3Client", "NNTPServer"}; + +void decodeblock( unsigned char in[4], unsigned char out[3] ); +VOID FormatTime(char * Time, time_t cTime); +static int Socket_Accept(SOCKET SocketId); + +int SendSock(SocketConn * sockptr, char * msg) +{ + int len = (int)strlen(msg), sent; + char * newmsg = malloc(len+10); + + WriteLogLine(NULL, '>',msg, len, LOG_TCP); + + strcpy(newmsg, msg); + + strcat(newmsg, "\r\n"); + + len+=2; + + if (sockptr->SendBuffer) + { + // Already queued, so add to end + + if ((sockptr->SendSize + len) > sockptr->SendBufferSize) + { + sockptr->SendBufferSize += (10000 + len); + sockptr->SendBuffer = realloc(sockptr->SendBuffer, sockptr->SendBufferSize); + } + + memcpy(&sockptr->SendBuffer[sockptr->SendSize], newmsg, len); + sockptr->SendSize += len; + free (newmsg); + return len; + } + + sent = send(sockptr->socket, newmsg, len, 0); + + if (sent < len) + { + int error, remains; + + // Not all could be sent - queue rest + + if (sent == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + sent=0; + + // What else?? + } + + remains = len - sent; + + sockptr->SendBufferSize += (10000 + remains); + sockptr->SendBuffer = malloc(sockptr->SendBufferSize); + + memcpy(sockptr->SendBuffer, &newmsg[sent], remains); + + sockptr->SendSize = remains; + sockptr->SendPtr = 0; + + } + + free (newmsg); + + return sent; +} + +VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...) +{ + // printf to a socket + + char buff[1000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + SendSock(sockptr, buff); +} + +extern int SMTPMsgs; + +fd_set ListenSet; +SOCKET ListenMax = 0; + +extern SOCKET nntpsock; + +int NNTP_Read(SocketConn * sockptr, SOCKET sock); + +VOID SetupListenSet() +{ + // Set up master set of fd's for checking for incoming calls + + fd_set * readfd = &ListenSet; + SOCKET sock; + + FD_ZERO(readfd); + + sock = nntpsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > ListenMax) + ListenMax = sock; + } + + sock = smtpsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > ListenMax) + ListenMax = sock; + } + + sock = pop3sock; + + if (sock) + { + FD_SET(sock, readfd); + if (sock > ListenMax) + ListenMax = sock; + } +} + +VOID Socket_Connected(SocketConn * sockptr, int error) +{ + SOCKET sock = sockptr->socket; + + if (error) + { + Logprintf(LOG_TCP, NULL, '|', "Connect Failed"); + + if (sockptr->Type == SMTPClient) + SMTPActive = FALSE; + + ReleaseSock(sock); + + return; + } + + sockptr->State = WaitingForGreeting; + + if (sockptr->Type == NNTPServer) + SendSock(sockptr, "200 BPQMail NNTP Server ready"); + + else if (sockptr->Type == SMTPServer) + SendSock(sockptr, "220 BPQMail SMTP Server ready"); + + else if (sockptr->Type == POP3SLAVE) + { + SendSock(sockptr, "+OK POP3 server ready"); + sockptr->State = GettingUser; + } +} + +VOID TCPFastTimer() +{ + // we now poll for incoming connections and data + + fd_set readfd, writefd, exceptfd; + struct timeval timeout; + int retval; + SocketConn * sockptr = Sockets; + SOCKET sock; + int Active = 0; + SOCKET maxsock; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (ListenMax) + { + memcpy(&readfd, &ListenSet, sizeof(fd_set)); + + retval = select((int)ListenMax + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + retval = 0; + perror("Listen select"); + } + + if (retval) + { + sock = pop3sock; + if (sock) + if (FD_ISSET(sock, &readfd)) + Socket_Accept(sock); + + sock = smtpsock; + if (sock) + if (FD_ISSET(sock, &readfd)) + Socket_Accept(sock); + + sock = nntpsock; + if (sock) + if (FD_ISSET(sock, &readfd)) + NNTP_Accept(sock); + } + } + + // look for data on any active sockets + + maxsock = 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + sockptr=Sockets; + + while (sockptr) + { + sockptr->Timeout++; + + if (sockptr->Timeout > 1200) // 2 mins + { + Logprintf(LOG_TCP, NULL, '|', "Timeout on Socket = %d", sockptr->socket); + shutdown(sockptr->socket, 0); + closesocket(sockptr->socket); + ReleaseSock(sockptr->socket); + + return; // We've messed with chain + } + + if (sockptr->State & Connecting) + { + // look for complete or failed + + FD_SET(sockptr->socket, &writefd); + FD_SET(sockptr->socket, &exceptfd); + } + else + FD_SET(sockptr->socket, &readfd); + + Active++; + + if (sockptr->socket > maxsock) + maxsock = sockptr->socket; + + sockptr = sockptr->Next; + } + + if (Active == 0) + return; + + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("select"); + + // we need to do something or the error will recur. + // As there are unlikely to be a lot of open tcp connections perhaps + // simplest is to close all + + sockptr = Sockets; + + while (sockptr) + { + Debugprintf("MAILTCP Select Failed Active %s Socket", sockTypes[sockptr->Type]); + shutdown(sockptr->socket, 0); + closesocket(sockptr->socket); + ReleaseSock(sockptr->socket); + + sockptr = Sockets; // We've messed with chain + } + } + else + { + if (retval) + { + sockptr = Sockets; + + // see who has data + + while (sockptr) + { + sock = sockptr->socket; + + if (FD_ISSET(sock, &readfd)) + { + sockptr->Timeout = 0; + + if (sockptr->Type == NNTPServer) + { + if (NNTP_Read(sockptr, sock) == 0) + break; // We've messed with the chain + } + else + { + if (DataSocket_Read(sockptr, sock) == 0) + break; // We've messed with the chain + } + } + if (FD_ISSET(sockptr->socket, &writefd)) + Socket_Connected(sockptr, 0); + + if (FD_ISSET(sockptr->socket, &exceptfd)) + { + Socket_Connected(sockptr, 1); + return; + } + sockptr = sockptr->Next; + } + } + } +} + +VOID TCPTimer() +{ + POP3Timer+=10; + +// Debugprintf("POP3 Debug Timer = %d Interval = %d Port %d Enabled %d", +// POP3Timer, ISPPOP3Interval, ISPPOP3Port, ISP_Gateway_Enabled); + + if (POP3Timer > ISPPOP3Interval) // 5 mins + { + POP3Timer=0; + + if ((ISPSMTPPort && ISP_Gateway_Enabled)) + SendtoISP(); + + if (ISPPOP3Port && ISP_Gateway_Enabled) + { +// Debugprintf("Calling POP3 Connect"); + POP3Connect(ISPPOP3Name, ISPPOP3Port); + } + + if (SMTPMsgs && ISPSMTPPort && ISP_Gateway_Enabled) + SendtoISP(); + } + else + { + if (SMTPMsgCreated && ISPSMTPPort && ISP_Gateway_Enabled) + SendtoISP(); + } +} +BOOL InitialiseTCP() +{ + int Error; // catches return value of WSAStartup +#ifdef WIN32 + WORD VersionRequested; // passed to WSAStartup + WSADATA WsaData; // receives data from WSAStartup +#endif + int i,j; + + + for (i=0;i<64; i++) + { + j=cb64[i]; + mycd64[j]=i; + } + +#ifdef WIN32 + + VersionRequested = MAKEWORD(VERSION_MAJOR, VERSION_MINOR); + + Error = WSAStartup(VersionRequested, &WsaData); + + if (Error) + { +#ifndef LINBPQ + MessageBox(NULL, + "Could not find high enough version of WinSock", + "BPQMailChat", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND); +#else + printf("Could not find high enough version of WinSock\n"); +#endif + return FALSE; + } + +#endif + +// Create listening sockets + + + if (SMTPInPort) + smtpsock = CreateListeningSocket(SMTPInPort); + + if (POP3InPort) + pop3sock = CreateListeningSocket(POP3InPort); + + if (ISP_Gateway_Enabled) + { + // See if using GMail + + char * ptr = strchr(ISPAccountName, '@'); + + if (ptr) + { + if (_stricmp(&ptr[1], "gmail.com") == 0 || _stricmp(&ptr[1], "googlemail.com") == 0) + { + strcpy(GMailName, ISPAccountName); + strlop(GMailName, '@'); + GMailMode = TRUE; + SMTPAuthNeeded = TRUE; + } + } + } + + return TRUE; + +} + + +SOCKET CreateListeningSocket(int Port) +{ + SOCKET sock; + unsigned int param = 1; + + sock = socket( AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "socket() failed error %d", WSAGetLastError()); +#ifdef LINBPQ + perror(szBuff); +#else + MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK); +#endif + return FALSE; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + psin=&local_sin; + + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = htonl(RemoteEmail ? INADDR_ANY : INADDR_LOOPBACK); // Local Host Olny + + psin->sin_port = htons(Port); /* Convert to network ordering */ + + if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(%d) failed Error %d", Port, WSAGetLastError()); +#ifdef LINBPQ + perror(szBuff); +#else + MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK); +#endif + closesocket( sock ); + return FALSE; + } + + if (listen( sock, MAX_PENDING_CONNECTS ) < 0) + { + sprintf(szBuff, "listen(%d) failed Error %d", Port, WSAGetLastError()); +#ifdef LINBPQ + perror(szBuff); +#else + MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK); +#endif + closesocket( sock ); + return FALSE; + } + + ioctl(sock, FIONBIO, ¶m); + return sock; +} + +static int Socket_Accept(SOCKET SocketId) +{ + int addrlen; + SocketConn * sockptr; + SOCKET sock; + unsigned int param = 1; + + addrlen=sizeof(struct sockaddr); + + // Allocate a Socket entry + + sockptr = malloc(sizeof(SocketConn)); + memset(sockptr, 0, sizeof (SocketConn)); + + sockptr->Next = Sockets; + Sockets = sockptr; + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + Logprintf(LOG_TCP, NULL, '|', " accept() failed Error %d", WSAGetLastError()); + + // get rid of socket record + + Sockets = sockptr->Next; + free(sockptr); + return FALSE; + } + + ioctl(sock, FIONBIO, ¶m); + + + sockptr->socket = sock; + + if (SocketId == pop3sock) + { + sockptr->Type = POP3SLAVE; + SendSock(sockptr, "+OK POP3 server ready"); + sockptr->State = GettingUser; + Logprintf(LOG_TCP, NULL, '|', "Incoming POP3 Connect Socket = %d", sock); + } + else + { + sockptr->Type = SMTPServer; + sockptr->State = WaitingForGreeting; + SendSock(sockptr, "220 BPQMail SMTP Server ready"); + Logprintf(LOG_TCP, NULL, '|', "Incoming SMTP Connect Socket = %d", sock); + } + + return 0; +} + + +VOID ReleaseSock(SOCKET sock) +{ + // remove and free the socket record + + SocketConn * sockptr, * lastptr; + + sockptr=Sockets; + lastptr=NULL; + + while (sockptr) + { + if (sockptr->socket == sock) + { + if (lastptr) + lastptr->Next=sockptr->Next; + else + Sockets=sockptr->Next; + + if (sockptr->POP3User) + sockptr->POP3User->POP3Locked = FALSE; + + if (sockptr->State == WaitingForGreeting) + { + Logprintf(LOG_TCP, NULL, '|', "Premature Close on Socket %d", sock); + + if (sockptr->Type == SMTPClient) + SMTPActive = FALSE; + } + else + Logprintf(LOG_TCP, NULL, '|', "Socket %d Closed", sock); + + + free(sockptr); + return; + } + else + { + lastptr=sockptr; + sockptr=sockptr->Next; + } + } +} + +/* +int Socket_Data(int sock, int error, int eventcode) +{ + SocketConn * sockptr; + + // Find Connection Record + + sockptr=Sockets; + + while (sockptr) + { + if (sockptr->socket == sock) + { + switch (eventcode) + { + case FD_READ: + + return DataSocket_Read(sockptr,sock); + + case FD_WRITE: + + // Either Just connected, or flow contorl cleared + + if (sockptr->SendBuffer) + // Data Queued + SendFromQueue(sockptr); + else + { + if (sockptr->Type == SMTPServer) + SendSock(sockptr, "220 BPQMail SMTP Server ready"); + else + { + if (sockptr->Type == POP3SLAVE) + { + SendSock(sockptr, "+OK POP3 server ready"); + sockptr->State = GettingUser; + } + } + } + return 0; + + case FD_OOB: + + return 0; + + case FD_ACCEPT: + + return 0; + + case FD_CONNECT: + + return 0; + + case FD_CLOSE: + + closesocket(sock); + ReleaseSock(sock); + return 0; + } + return 0; + } + else + sockptr=sockptr->Next; + } + + return 0; +} +*/ +int DataSocket_Read(SocketConn * sockptr, SOCKET sock) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (sockptr->InputLen > 1000) // Shouldnt have lines longer than this in text mode + { + sockptr->InputLen=0; + } + + InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); + + if (InputLen <= 0) + { + int x = WSAGetLastError(); + + closesocket(sock); + ReleaseSock(sock); + + return 0; // Does this mean closed? + } + + sockptr->InputLen += InputLen; + +loop: + + ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + if (sockptr->Type == SMTPServer) + ProcessSMTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + else + if (sockptr->Type == POP3SLAVE) + ProcessPOP3ServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + else + if (sockptr->Type == SMTPClient) + ProcessSMTPClientMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + else + if (sockptr->Type == POP3Client) + ProcessPOP3ClientMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + + sockptr->InputLen=0; + + } + else + { + // buffer contains more that 1 message + + MsgLen = sockptr->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + + if (sockptr->Type == SMTPServer) + ProcessSMTPServerMessage(sockptr, Buffer, MsgLen); + else + if (sockptr->Type == POP3SLAVE) + ProcessPOP3ServerMessage(sockptr, Buffer, MsgLen); + else + if (sockptr->Type == SMTPClient) + ProcessSMTPClientMessage(sockptr, Buffer, MsgLen); + else + if (sockptr->Type == POP3Client) + ProcessPOP3ClientMessage(sockptr, Buffer, MsgLen); + + + memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); + + sockptr->InputLen -= MsgLen; + + goto loop; + + } + } + return TRUE; +} + +char * FindPart(char ** Msg, char * Boundary, int * PartLen) +{ + char * ptr = *Msg, * ptr2; + char * Msgptr = *Msg; + int BLen = (int)strlen(Boundary); + char * Part; + + while(*ptr) // Just in case we run off end + { + ptr2 = strchr(ptr, 10); // Find LF + + if (ptr2 == NULL) return NULL; + + if (*ptr == '-' && *(ptr+1) == '-') + { + if (memcmp(&ptr[2], Boundary, BLen) == 0) + { + // Found Boundary + + int Partlen = (int)(ptr - Msgptr); + Part = malloc(Partlen + 1); + memcpy(Part, Msgptr, Partlen); + Part[Partlen] = 0; + + *Msg = ++ptr2; + + *PartLen = Partlen; + + return Part; + } + } + + ptr = ++ptr2; + } + return NULL; +} + + + + + +BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen) // Will reformat message if necessary. +{ + int i; + char * ptr, * ptr2, * ptr3, * ptr4; + char Boundary[1000]; + BOOL Multipart = FALSE; + BOOL ALT = FALSE; + int Partlen; + char * Save; + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + char FileName[100][250] = {""}; + int FileLen[100]; + char * FileBody[100]; + char * MallocSave[100]; + UCHAR * NewMsg; + + int Files = 0; + + ptr = Msg; + + 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 + } + +// Content-Type: multipart/mixed; +// boundary="----=_NextPart_000_025B_01CAA004.84449180" +// 7.2.2 The Multipart/mixed (primary) subtype +// 7.2.3 The Multipart/alternative subtype + + + if (_memicmp(ptr, "Content-Type: ", 14) == 0) + { + char Line[1000] = ""; + char lcLine[1000] = ""; + + char * ptr3; + + memcpy(Line, &ptr[14], ptr2-ptr-14); + memcpy(lcLine, &ptr[14], ptr2-ptr-14); + _strlwr(lcLine); + + if (_memicmp(Line, "Multipart/", 10) == 0) + { + Multipart = TRUE; + + if (_memicmp(&Line[10], "alternative", 11) == 0) + { + ALT = TRUE; + } + + ptr3 = strstr(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; + + } + else + return FALSE; // Can't do anything without a boundary ?? + } + + } + + else if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0) + { + if (strstr(&ptr[26], "base64")) + Base64 = TRUE; + else + if (strstr(&ptr[26], "quoted-printable")) + QuotedP = TRUE; + } + + + ptr = ptr2; + ptr++; + + } + + if (Multipart == FALSE) + { + // We only have one part, but it could have an odd encoding + + if (Base64) + { + int i = 0, Len = *MsgLen, NewLen; + char * ptr2; + char * End; + + ptr = ptr2 = *Body; + End = ptr + Len; + + while (ptr < End) + { + while (*ptr < 33) + {ptr++;} + + *ptr2++ = *ptr++; + } + + *ptr2 = 0; + + ptr = *Body; + Len = (int)(ptr2 - ptr - 1); + + ptr2 = ptr; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - *Body); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + *MsgLen = NewLen; + } + else if (QuotedP) + { + int i = 0, Len = *MsgLen; + char * ptr2; + char * End; + + ptr = ptr2 =*Body; + + End = ptr + Len; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + + if (c > 9) + c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) + d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + { + *ptr2++ = *ptr++; + } + } + *ptr2 = 0; + + *MsgLen = (int)(ptr2 - *Body); + + } + + return FALSE; + } + // FindPart Returns Next Part of Message, Updates Input Pointer + // Skip to first Boundary (over the non MIME Alt Part) + + ptr = FindPart(Body, Boundary, &Partlen); + + if (ptr == NULL) + return FALSE; // Couldn't find separator + + free(ptr); + + if (ALT) + { + // Assume HTML and Plain Text Versions of the same single body. + + ptr = FindPart(Body, Boundary, &Partlen); + + Save = ptr; // For free(); + + // Should be the First (Least desireable part, but the bit we want, as we are only interested in plain text) + + // Skip any headers + + while(*ptr != 13) + { + if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0) + { + if (strstr(&ptr[26], "base64")) + Base64 = TRUE; + else + if (strstr(&ptr[26], "quoted-printable")) + QuotedP = TRUE; + } + + ptr2 = strchr(ptr, 10); // Find CR + + while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line + { + ptr2 = strchr(&ptr2[1], 10); // Find CR + } + + ptr = ++ptr2; + } + + ptr += 2; // Skip rerminating line + + // Should now have a plain text body to return; + + // But could be an odd encoding + + if (Base64) + { + int i = 0, Len = (int)strlen(ptr), NewLen; + char * ptr2; + char * End; + char * Save = ptr; + + ptr2 = ptr; + End = ptr + Len; + + while (ptr < End) + { + while (*ptr < 33) + {ptr++;} + + *ptr2++ = *ptr++; + } + + *ptr2 = 0; + + ptr = Save; + Len = (int)(ptr2 - ptr - 1); + + ptr2 = *Body; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - *Body); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + *MsgLen = NewLen; + } + else if (QuotedP) + { + int i = 0, Len = (int)strlen(ptr); + char * ptr2; + char * End; + char * Save = ptr; + + ptr2 = *Body; + + End = ptr + Len; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + + if (c > 9) + c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) + d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + { + *ptr2++ = *ptr++; + } + } + *ptr2 = 0; + + *MsgLen = (int)(ptr2 - *Body); + } + else + { + strcpy(*Body, ptr); + *MsgLen = (int)strlen(ptr); + } + free(Save); + + return FALSE; + } + + // Assume Multipart/Mixed - Message with attachments + + ptr = FindPart(Body, Boundary, &Partlen); + + if (ptr == NULL) + return FALSE; // Couldn't find separator + + while (ptr) + { + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + MallocSave[Files] = ptr; // For free(); + + // Should be the First (Least desireable part, but the bit we want, as we are only interested in plain text) + + // Process headers - looking for Content-Disposition: attachment; + + // The first could also be a Content-Type: multipart/alternative; - if so, feed back to mime handler + + while(*ptr != 13) + { + char lcLine[1000] = ""; + + ptr2 = strchr(ptr, 10); // Find CR + + if (ptr2 == 0) + return FALSE; + + while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line + { + ptr2 = strchr(&ptr2[1], 10); // Find CR + } + + memcpy(lcLine, ptr, ptr2-ptr-1); + _strlwr(lcLine); + + ptr = lcLine; + + if (_memicmp(ptr, "Content-Type: Multipart/alternative", 30) == 0) + { + // Feed Back + int MsgLen; + char * Text = malloc(Partlen+1); + + memcpy(Text, MallocSave[Files], Partlen); + + free(MallocSave[Files]); + MallocSave[Files] = Text; + + + CheckforMIME(sockptr, Text, &Text, &MsgLen); + + FileName[Files][0] = 0; + FileBody[Files] = Text; + + + FileLen[Files++] = MsgLen; + + goto NextPart; + + } + else if (_memicmp(ptr, "Content-Disposition: ", 21) == 0) + { + ptr3 = strstr(&ptr[21], "filename"); + + if (ptr3) + { + ptr3 += 9; + if (*ptr3 == '"') ptr3++; + ptr4 = strchr(ptr3, '"'); + if (ptr4) *ptr4 = 0; + + strcpy(FileName[Files], ptr3); + } + } + + else if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0) + { + if (strstr(&ptr[26], "base64")) + Base64 = TRUE; + else + if (strstr(&ptr[26], "quoted-printable")) + QuotedP = TRUE; + } + + ptr = ++ptr2; + } + + ptr += 2; + + // Should now have file or plain text. If file is Base64 encoded, decode it. + + FileBody[Files] = ptr; + FileLen[Files] = (int)(Partlen - 2 - (ptr - MallocSave[Files])); + + if (Base64) + { + int i = 0, Len = FileLen[Files], NewLen; + char * ptr2 = ptr; + char * End; + + End = ptr + FileLen[Files]; + + while (ptr < End) + { + while (*ptr < 33) + {ptr++;} + + *ptr2++ = *ptr++; + } + *ptr2 = 0; + + ptr = FileBody[Files]; + Len = (int)(ptr2 - ptr - 1); + + ptr2 = ptr; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - FileBody[Files]); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + FileLen[Files] = NewLen; + } + else if (QuotedP) + { + int i = 0, Len = FileLen[Files], NewLen; + char * ptr2 = ptr; + char * End; + + End = ptr + FileLen[Files]; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + + if (c > 9) + c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) + d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + { + *ptr2++ = *ptr++; + } + } + *ptr2 = 0; + + NewLen = (int)(ptr2 - FileBody[Files]); + + FileLen[Files] = NewLen; + } + + Files++; + + NextPart: + ptr = FindPart(Body, Boundary, &Partlen); + } + + // Now have all the parts - build a B2 Message. Leave the first part of header for later, + // as we may have multiple recipients. Start with the Body: Line. + + // We need to add the first part of header later, so start message part way down buffer. + // Make sure buffer is big enough. + + if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 2000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sockptr->socket, 0); + return FALSE; + } + } + + + NewMsg = sockptr->MailBuffer + 1000; + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); + + for (i = 1; i < Files; i++) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + + for (i = 0; i < Files; i++) + { + memcpy(NewMsg, FileBody[i], FileLen[i]); + NewMsg += FileLen[i]; + free(MallocSave[i]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + *MsgLen = (int)(NewMsg - (sockptr->MailBuffer + 1000)); + *Body = sockptr->MailBuffer + 1000; + + return TRUE; // B2 Message +} + + +VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + int i; + time_t Date = 0; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + if (sockptr->Flags == GETTINGMESSAGE) + { + if(memcmp(Buffer, ".\r\n", 3) == 0) + { + char * ptr1, * ptr2; + int linelen, MsgLen; + char Msgtitle[62]; + BOOL B2Flag; + int ToLen = 0; + char * ToString; + char * Via; + + // Scan headers for a Subject: or Date: Line (Headers end at blank line) + + ptr1 = sockptr->MailBuffer; + Loop: + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == NULL) + { + SendSock(sockptr, "500 Eh"); + return; + } + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "Subject:", 8) == 0) + { + if (linelen > 68) linelen = 68; + memcpy(Msgtitle, &ptr1[9], linelen-9); + Msgtitle[linelen-9]=0; + } + + if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char * Context; + char seps[] = " ,\t\r"; + char Offset[10] = ""; + int i, HH, MM; + char Copy[500]=""; + + // Copy message, so original isn't messed up by strtok + + memcpy(Copy, ptr1, linelen); + + ptr1 = Copy; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Tue, 9 Jun 2009 20:54:55 +0100 + + ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day + ptr1 = strtok_s(NULL, seps, &Context); // Day + + rtime.tm_mday = atoi(ptr1); + + ptr1 = strtok_s(NULL, seps, &Context); // Month + + for (i=0; i < 12; i++) + { + if (strcmp(month[i], ptr1) == 0) + { + rtime.tm_mon = i; + break; + } + } + + sscanf(Context, "%04d %02d:%02d:%02d%s", + &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = 0; + else + { + if ((Offset[0] == '+') || (Offset[0] == '-')) + { + MM = atoi(&Offset[3]); + Offset[3] = 0; + HH = atoi(&Offset[1]); + MM = MM + (60 * HH); + + if (Offset[0] == '+') + Date -= (60*MM); + else + Date += (60*MM); + + } + } + } + + ptr1 = ptr2 + 2; // Skip crlf + + if (linelen) // Not Null line + { + goto Loop; + } + + ptr2 = ptr1; + ptr1 = sockptr->MailBuffer; + + MsgLen = (int)(sockptr->MailSize - (ptr2 - ptr1)); + + // We Just want the from call, not the full address. + + TidyString(sockptr->MailFrom); + + // Examine Message to look for html formatting and attachments. + + B2Flag = CheckforMIME(sockptr, sockptr->MailBuffer, &ptr2, &MsgLen); // Will reformat message if necessary. + + // If any recipients are via RMS, create one message for them, and separate messages for all others + + ToString = zalloc(sockptr->Recipients * 100); + + for (i=0; i < sockptr->Recipients; i++) + { + char Addr[256]; // Need copy, as we may change it then decide it isn't for RMS + + strcpy(Addr, sockptr->RecpTo[i]); + Debugprintf("To Addr %s", Addr); + + TidyString(Addr); + Debugprintf("To Addr after Tidy %s", Addr); + + if ((_memicmp (Addr, "RMS:", 4) == 0) |(_memicmp (Addr, "RMS/", 4) == 0)) + { + // Add to B2 Message for RMS + + _strlwr(Addr); + + Via = strlop(&Addr[4], '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) // if local RMS - Leave Here + continue; + + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, &Addr[4]); + *sockptr->RecpTo[i] = 0; // So we dont create individual one later + continue; + } + + ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, &Addr[4], Via); + *sockptr->RecpTo[i] = 0; // So we dont create individual one later + continue; + } + + _strupr(Addr); + Debugprintf("To Addr after strupr %s", Addr); + + Via = strlop(Addr, '@'); + Debugprintf("Via %s", Via); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) // if local RMS - Leave Here + continue; + + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + *sockptr->RecpTo[i] = 0; // So we dont create individual one later + + continue; + } + } + + if (ToLen) // Have some RMS Addresses + { + char B2Hddr[1000]; + int B2HddrLen; + char DateString[80]; + char * NewBody; + struct tm * tm; + struct MsgInfo * Msg; + BIDRec * BIDRec; + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + Msg->length = MsgLen; + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + Msg->type = 'P'; + Msg->status = 'N'; + strcpy(Msg->to, "RMS"); + strlop(sockptr->MailFrom, '@'); + if (strlen(sockptr->MailFrom) > 6) sockptr->MailFrom[6]=0; + strcpy(Msg->from, sockptr->MailFrom); + strcpy(Msg->title, Msgtitle); + + 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->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (Date) + Msg->datecreated = Date; + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + if (B2Flag) // Message has attachments, so Body: line is present + { + Msg->B2Flags = B2Msg | Attachments; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, "Private", Msg->from, ToString, Msg->title, BBSName); + } + else + { + Msg->B2Flags = B2Msg; + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", + Msg->bid, DateString, "Private", Msg->from, ToString, Msg->title, BBSName, Msg->length); + + } + + NewBody = ptr2 - B2HddrLen; + + memcpy(NewBody, B2Hddr, B2HddrLen); + + Msg->length += B2HddrLen; + + free(ToString); + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + CreateSMTPMessageFile(NewBody, Msg); + } + + for (i=0; i < sockptr->Recipients; i++) + { + if (*sockptr->RecpTo[i]) // not already sent to RMS? + CreateSMTPMessage(sockptr, i, Msgtitle, Date, ptr2, MsgLen, B2Flag); + else + free(sockptr->RecpTo[i]); + } + + free(sockptr->RecpTo); + sockptr->RecpTo = NULL; + free(sockptr->MailFrom); + free(sockptr->MailBuffer); + + sockptr->MailBufferSize=0; + sockptr->MailBuffer=0; + sockptr->MailSize = 0; + + sockptr->Flags = 0; + sockptr->Recipients = 0; + + SendSock(sockptr, "250 Ok"); + return; + } + + if ((sockptr->MailSize + Len) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 10000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sock, 0); + return; + } + } + + memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len); + sockptr->MailSize += Len; + + return; + } + + if (sockptr->State == GettingUser) + { + char Out[30]; + + Buffer[Len-2]=0; + + decodeblock(Buffer, Out); + decodeblock(&Buffer[4], &Out[3]); + decodeblock(&Buffer[8], &Out[6]); + decodeblock(&Buffer[12], &Out[9]); + + if (strlen(Out) > 10) Out[10] = 0; + + strcpy(sockptr->CallSign, Out); + + sockptr->State = GettingPass; + SendSock(sockptr, "334 UGFzc3dvcmQ6"); + return; + } + + if (sockptr->State == GettingPass) + { + struct UserInfo * user = NULL; + char Out[30]; + + Buffer[Len-2]=0; + + decodeblock(Buffer, Out); + decodeblock(&Buffer[4], &Out[3]); + decodeblock(&Buffer[8], &Out[6]); + decodeblock(&Buffer[12], &Out[9]); + decodeblock(&Buffer[16], &Out[12]); + decodeblock(&Buffer[20], &Out[15]); + + user = LookupCall(sockptr->CallSign); + + if (user) + { + if (strcmp(user->pass, Out) == 0) + { + sockptr->State = Authenticated; + SendSock(sockptr, "235 2.0.0 OK Authenticated"); //535 authorization failed + return; + } + } + + SendSock(sockptr, "535 authorization failed"); + sockptr->State = 0; + return; + } + + + +/*AUTH LOGIN + +334 VXNlcm5hbWU6 +a4msl9ux +334 UGFzc3dvcmQ6 +ZvVx9G1hcg== +235 2.0.0 OK Authenticated +*/ + + + if(memcmp(Buffer, "AUTH LOGIN", 10) == 0) + { + sockptr->State = GettingUser; + SendSock(sockptr, "334 VXNlcm5hbWU6"); + return; + } + + if(memcmp(Buffer, "EHLO",4) == 0) + { + SendSock(sockptr, "250-BPQ Mail Server"); + SendSock(sockptr, "250 AUTH LOGIN"); + + //250-8BITMIME + + return; + } + + if(memcmp(Buffer, "AUTH LOGIN", 10) == 0) + { + sockptr->State = GettingUser; + SendSock(sockptr, "334 VXNlcm5hbWU6"); + return; + } + + + if(memcmp(Buffer, "HELO",4) == 0) + { + SendSock(sockptr, "250 Ok"); + return; + } + + if(_memicmp(Buffer, "MAIL FROM:", 10) == 0) + { + if (sockptr->State != Authenticated) + { + // Accept if from 44/8 and ends in ampr.org + + if (_memicmp(&Buffer[Len - 11], "ampr.org", 8) == 0 && + (sockptr->sin.sin_addr.s_addr & 0xff) == 44) + { + } + else + { + SendSock(sockptr, "530 Authentication required"); + return; + } + } + + sockptr->MailFrom = zalloc(Len); + memcpy(sockptr->MailFrom, &Buffer[10], Len-12); + + SendSock(sockptr, "250 Ok"); + + return; + } + + if(_memicmp(Buffer, "RCPT TO:", 8) == 0) + { + if (sockptr->State != Authenticated) + { + // Accept if from 44/8 and ends in ampr.org + + + + if (_memicmp(&Buffer[Len - 11], "ampr.org", 8) == 0 && + (sockptr->sin.sin_addr.s_addr & 0xff) == 44) + { + } + else + { + SendSock(sockptr, "530 Authentication required"); + return; + } + } + + sockptr->RecpTo=realloc(sockptr->RecpTo, (sockptr->Recipients+1) * sizeof(void *)); + sockptr->RecpTo[sockptr->Recipients] = zalloc(Len); + + memcpy(sockptr->RecpTo[sockptr->Recipients++], &Buffer[8], Len-10); + + SendSock(sockptr, "250 Ok"); + return; + } + + if(memcmp(Buffer, "DATA\r\n", 6) == 0) + { + sockptr->MailBuffer=malloc(10000); + sockptr->MailBufferSize=10000; + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to create SMTP Message Buffer"); + SendSock(sockptr, "250 Failed"); + shutdown(sock, 0); + + return; + } + + sockptr->Flags |= GETTINGMESSAGE; + + SendSock(sockptr, "354 End data with ."); + return; + } + + if(memcmp(Buffer, "QUIT\r\n", 6) == 0) + { + SendSock(sockptr, "221 OK"); + Sleep(500); + shutdown(sock, 0); + return; + } + + if(memcmp(Buffer, "RSET\r\n", 6) == 0) + { + SendSock(sockptr, "250 Ok"); + + // This cancelled AUTH which I think is wrong + //sockptr->State = 0; + + if (sockptr->State != Authenticated) + sockptr->State = 0; + + sockptr->Recipients = 0; + + return; + } + + return; +} + + +int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * To; + char * via; + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + Msg->length = MsgLen; + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + Msg->type = 'P'; + Msg->status = 'N'; + + 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->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (Date) + Msg->datecreated = Date; + + To = sockptr->RecpTo[i]; + + Debugprintf("To %s", To); + + TidyString(To); + + Debugprintf("To after tidy %s", To); + + if (_memicmp(To, "bull/", 5) == 0) + { + Msg->type = 'B'; + memmove(To, &To[5], strlen(&To[4])); + } + + if ((_memicmp(To, "nts/", 4) == 0) ||(_memicmp(To, "nts:", 4) == 0) || + (_memicmp(To, "nts.", 4) == 0)) + { + Msg->type = 'T'; + memmove(To, &To[4], strlen(&To[3])); + } + + if (_memicmp(To, "rms:", 4) == 0) + { + via = _strlwr(strlop(To, ':')); + } + else if (_memicmp(To, "rms/", 4) == 0) + { + via = _strlwr(strlop(To, '/')); + } + else if (_memicmp(To, "rms.", 4) == 0) + { + via = _strlwr(strlop(To, '.')); + } + else if (_memicmp(To, "smtp:", 5) == 0) + { + via = _strlwr(strlop(To, ':')); + To[0] = 0; + } + else if (_memicmp(To, "smtp/", 5) == 0) + { + via = _strlwr(strlop(To, '/')); + To[0] = 0; + } + else + { + via = strlop(To, '@'); + } + + Debugprintf("via %s", via); + + if (via) + { + int toLen; + + if (strlen(via) > 40) via[40] = 0; + + strcpy(Msg->via, via); // Save before messing with it + + // if ending in AMPR.ORG send via ISP if we have enabled forwarding AMPR + + toLen = (int)strlen(via); + + if (_memicmp(&via[toLen - 8], "ampr.org", 8) == 0) + { + // if our domain keep here. + + // if not, and SendAMPRDirect set, set as ISP, + // else set as RMS + + if (_stricmp(via, AMPRDomain) == 0) + { + // Our Message- dont forward + } + else + { + // AMPR but not us + + if (SendAMPRDirect) + { + sprintf(Msg->via,"%s@%s", To, via); + strcpy(To, "AMPR"); + } + else + { + sprintf(Msg->via,"%s@%s", To, via); + strcpy(To, "RMS"); + } + } + } + else + { + strlop(via, '.'); // Get first part of address + + if (_stricmp(via, BBSName) == 0) + { + // sent via us - clear the name + + Msg->via[0] = 0; + } + } + } + + if (strlen(To) > 6) To[6]=0; + + strcpy(Msg->to, To); + + if (strchr(sockptr->MailFrom, '@')) + { + char * FromHA = strlop(sockptr->MailFrom, '@'); + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], FromHA); + } + + if (strlen(sockptr->MailFrom) > 6) sockptr->MailFrom[6]=0; + + strcpy(Msg->from, sockptr->MailFrom); + + strcpy(Msg->title, MsgTitle); + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + // If NTS message (TO is numeric and AT is NTSxx or NTSxx.NTS - Outlook won't accept x@y) + + if (isdigits(Msg->to) && memcmp(Msg->via, "NTS", 3) == 0) + { + if (Msg->via[5] == 0 || strcmp(&Msg->via[5], ".NTS") == 0) + { + Msg->type = 'T'; + Msg->via[5] = 0; + } + } + + Debugprintf("Msg->Via %s", Msg->via); + + if (B2Flag) + { + char B2Hddr[1000]; + int B2HddrLen; + char B2To[80]; + char * NewBody; + char DateString[80]; + char * TypeString; + struct tm * tm; + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + + if (strcmp(Msg->to, "RMS") == 0) // Address is in via + strcpy(B2To, Msg->via); + else + if (Msg->via[0]) + sprintf(B2To, "%s@%s", Msg->to, Msg->via); + else + strcpy(B2To, Msg->to); + + + Msg->B2Flags = B2Msg | Attachments; + + if (Msg->type == 'P') + TypeString = "Private" ; + else if (Msg->type == 'B') + TypeString = "Bulletin"; + else if (Msg->type == 'T') + TypeString = "Traffic"; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, TypeString, + Msg->from, B2To, Msg->title, BBSName); + + NewBody = MsgBody - B2HddrLen; + + memcpy(NewBody, B2Hddr, B2HddrLen); + + Msg->length += B2HddrLen; + + free(To); + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + return CreateSMTPMessageFile(NewBody, Msg); + + } + + free(To); + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + if (Msg->type == 'B' && memcmp( Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + return CreateSMTPMessageFile(MsgBody, Msg); + +} + + +BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg) +{ + char MsgFile[250]; + FILE * hFile; + int WriteLen=0; + char Mess[255]; + int len; + + struct UserInfo * ToUser = LookupCall(Msg->to); + + if (ToUser && ToUser->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); + } + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = (int)fwrite(Message, 1, Msg->length, hFile); + fclose(hFile); + } + + if (WriteLen != Msg->length) + { + len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + CriticalErrorHandler(Mess); + + return FALSE; + } + + SaveMessageDatabase(); + SaveBIDDatabase(); + + return TRUE; +} + +int TidyString(char * Address) +{ + // Cleans up a From: or To: Address + + // May have leading or trailing spaces, or be enclosed by <>, or have a " " part + + // From: "John Wiseman" + + char * ptr1, * ptr2; + size_t len; + + _strupr(Address); + + ptr1 = strchr(Address, '<'); + + if (ptr1) + { + ptr1++; + ptr2 = strlop(ptr1, '>'); + len = (int)strlen(ptr1); + memmove(Address, ptr1, len); + Address[len] = 0; + + // Could have surrounding "" "" + + if (Address[0] == '"') + { + int len = (int)strlen(Address) - 1; + + if (Address[len] == '"') + { + Address[len] = 0; + memmove(Address, &Address[1], len); + return 0; + } + + // Thunderbird can put "" round part of address "rms:john.wiseman"@cantab.net + + ptr2 = strchr(&Address[1], '"'); + + if (ptr2) + { + memmove(Address, &Address[1], ptr2 - &Address[1]); + memmove(ptr2 - 1, ptr2 + 1, strlen(ptr2 + 1) + 1); + + } + + + } + + return 0; + } + + ptr1 = Address; + + while (*ptr1 == ' ') ptr1++; + + if (*ptr1 == '"') + { + ptr1++; + ptr1=strlop(ptr1, '"'); + ptr2=strlop(ptr1, ' '); + ptr1=ptr2; + } + + if (*ptr1 == '<') ptr1++; + + ptr2 = strlop(ptr1, '>'); + strlop(ptr1, ' '); + + len = strlen(ptr1); + memmove(Address, ptr1, len); + Address[len] = 0; + + return 0; +} +/* ++OK POP3 server ready +USER john.wiseman ++OK please send PASS command +PASS gb7bpq ++OK john.wiseman is welcome here +STAT ++OK 6 115834 + +UIDL ++OK 6 messages +1 <4A0DC6E0.5020504@hb9bza.net> +2 +3 <1085101c9d5d0$09b15420$16f9280a@phx.gbl> +4 +5 +6 <20090516011401.53DB013804@panix1.panix.com> +. +LIST ++OK 6 messages +1 7167 +2 10160 +3 52898 +4 4746 +5 20218 +6 20645 +. + +*/ + +VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + int i; + struct MsgInfo * Msg; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + if(memcmp(Buffer, "CAPA",4) == 0) + { + SendSock(sockptr, "+OK Capability list follows"); + SendSock(sockptr, "UIDL"); + SendSock(sockptr, "TOP"); + SendSock(sockptr, "EXPIRE 30"); + SendSock(sockptr, "."); + return; + } + + if(memcmp(Buffer, "AUTH",4) == 0) + { + SendSock(sockptr, "-ERR"); + return; + } + if (sockptr->State == GettingUser) + { + + Buffer[Len-2]=0; + if (Len > 15) Buffer[15]=0; + + strcpy(sockptr->CallSign, &Buffer[5]); + + sockptr->State = GettingPass; + SendSock(sockptr, "+OK please send PASS command"); + return; + } + + if (sockptr->State == GettingPass) + { + struct UserInfo * user = NULL; + + Buffer[Len-2]=0; + user = LookupCall(sockptr->CallSign); + + if (user) + { + if (strcmp(user->pass, &Buffer[5]) == 0) + { + if (user->POP3Locked) + { + SendSock(sockptr, "-ERR Mailbox Locked"); + sockptr->State = 0; + return; + } + + sockptr->State = Authenticated; + SendSock(sockptr, "+OK Authenticated"); + + sockptr->POP3User = user; + user->POP3Locked = TRUE; + + // Get Message List + + for (i=0; i<=NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, sockptr->CallSign) == 0) || + ((_stricmp(Msg->to, "SYSOP") == 0) && (user->flags & F_SYSOP) && (Msg->type == 'P'))) + { + if (Msg->status != 'K' && Msg->status != 'H') + { + sockptr->POP3Msgs = realloc(sockptr->POP3Msgs, (sockptr->POP3MsgCount+1) * sizeof(void *)); + sockptr->POP3Msgs[sockptr->POP3MsgCount++] = MsgHddrPtr[i]; + } + } + } + + return; + } + } + + SendSock(sockptr, "-ERR Authentication failed"); + sockptr->State = 0; + return; + } + + if (memcmp(Buffer, "QUIT",4) == 0) + { + SendSock(sockptr, "+OK Finished"); + + if (sockptr->POP3User) + sockptr->POP3User->POP3Locked = FALSE; + + return; + } + + if (memcmp(Buffer, "NOOP",4) == 0) + { + SendSock(sockptr, "+OK "); + return; + } + +// if (memcmp(Buffer, "LAST",4) == 0) +// { +// SendSock(sockptr, "+OK 0"); +// return; +// } + + if (sockptr->State != Authenticated) + { + SendSock(sockptr, "-ERR Need Authentication"); + sockptr->State = 0; + return; + } + + if (memcmp(Buffer, "STAT",4) == 0) + { + char reply[40]; + int i, size=0; + + for (i=0; i< sockptr->POP3MsgCount; i++) + { + size+=sockptr->POP3Msgs[i]->length; + } + + sprintf_s(reply, sizeof(reply), "+OK %d %d", sockptr->POP3MsgCount, size); + + SendSock(sockptr, reply); + return; + } + + if (memcmp(Buffer, "UIDL",4) == 0) + { + char reply[40]; + int i, count=0, size=0; + int MsgNo=1; + + SendSock(sockptr, "+OK "); + + for (i=0; i< sockptr->POP3MsgCount; i++) + { + sprintf_s(reply, sizeof(reply), "%d %s", i+1, sockptr->POP3Msgs[i]->bid); + SendSock(sockptr, reply); + } + + SendSock(sockptr, "."); + return; + } + + if (memcmp(Buffer, "LIST", 4) == 0) + { + char reply[40]; + int i, count=0, size=0; + int MsgNo = atoi(&Buffer[4]); + + if (Buffer[4] == 13) // CR + MsgNo = 0; + + Debugprintf("%s %d", Buffer, MsgNo); + + if (MsgNo) + { + if (MsgNo > sockptr->POP3MsgCount) + sprintf(reply, "-ERR no such message, only %d messages in maildrop", sockptr->POP3MsgCount); + else + sprintf(reply, "+OK %d %d", MsgNo, sockptr->POP3Msgs[MsgNo - 1]->length); + SendSock(sockptr, reply); + return; + } + + + SendSock(sockptr, "+OK "); + + for (i=0; i< sockptr->POP3MsgCount; i++) + { + sprintf_s(reply, sizeof(reply), "%d %d", i+1, sockptr->POP3Msgs[i]->length); + SendSock(sockptr, reply); + } + + SendSock(sockptr, "."); + return; + } + + if (memcmp(Buffer, "RETR", 4) == 0 || memcmp(Buffer, "TOP", 3) == 0) + { + char * ptr; + char Header[120]; + int i, count=0, size=0; + int MsgNo=1; + char * msgbytes; + struct MsgInfo * Msg; + char B2From[80]; + struct UserInfo * FromUser; + char TimeString[64]; + BOOL TOP = FALSE; + int Len; + + if (memcmp(Buffer, "TOP", 3) == 0) + TOP = TRUE; + + ptr=strlop(Buffer, ' '); // Get Number + + i=atoi(ptr); + + if ((i > sockptr->POP3MsgCount) || (i == 0)) + { + SendSock(sockptr, "-ERR no such message"); + return; + } + + Msg = sockptr->POP3Msgs[i-1]; + + msgbytes = ReadMessageFile(Msg->number); + + if (msgbytes == NULL) + { + SendSock(sockptr, "-ERR no such message"); + return; + } + + SendSock(sockptr, "+OK "); + + // Build an RFC822 ish header + +//Received: from [69.147.65.148] by n15.bullet.sp1.yahoo.com with NNFMP; 16 May 2009 02:30:47 -0000 +//Received: from [69.147.108.192] by t11.bullet.mail.sp1.yahoo.com with NNFMP; 16 May 2009 02:30:47 -0000 + + FormatTime(TimeString, (time_t)Msg->datecreated); + + sprintf_s(Header, sizeof(Header), "Date: %s", TimeString); + SendSock(sockptr, Header); + + sprintf_s(Header, sizeof(Header), "To: %s", Msg->to); + SendSock(sockptr, Header); + + sprintf_s(Header, sizeof(Header), "Message-ID: %s", Msg->bid); + SendSock(sockptr, Header); + + if (_stricmp(Msg->from, "smtp:") == 0) + { + sprintf_s(Header, sizeof(Header), "From: smtp/%s", Msg->emailfrom); + SendSock(sockptr, Header); + sprintf_s(Header, sizeof(Header), "Replyto: smtp/%s", Msg->emailfrom); + } + else + { + if (_stricmp(Msg->from, "rms:") == 0) + { + sprintf_s(Header, sizeof(Header), "From: RMS/%s", Msg->emailfrom); + SendSock(sockptr, Header); + sprintf_s(Header, sizeof(Header), "Replyto: RMS/%s", Msg->emailfrom); + } + else + { + // If there is an adddress in Msg->emailfrom use it + + if (Msg->emailfrom[0]) + { + strcpy(B2From, Msg->from); + strcat(B2From, Msg->emailfrom); + } + else + { + // Packet Address. Mail client will need more than just a call to respond to + + strcpy(B2From, Msg->from); + + if (strcmp(Msg->from, "SMTP:") == 0) // Address is in via + strcpy(B2From, Msg->emailfrom); + else + { + FromUser = LookupCall(Msg->from); + + if (FromUser) + { + 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); + if (WP) + sprintf(B2From, "%s@%s", Msg->from, WP->first_homebbs); + } + } + } + sprintf_s(Header, sizeof(Header), "From: %s", B2From); + SendSock(sockptr, Header); + sprintf_s(Header, sizeof(Header), "Replyto: %s", B2From); + } + } + SendSock(sockptr, Header); + sprintf_s(Header, sizeof(Header), "Subject: %s", Msg->title); + SendSock(sockptr, Header); + + if ((Msg->B2Flags & Attachments) && TOP == FALSE) + { + // B2 Message with Attachments. Create a Mime-Encoded Multipart message + + SendMultiPartMessage(sockptr, Msg, msgbytes); + return; + } + + if (TOP) + { + // Get first i lines of message + + char * ptr1, * ptr2; + + ptr = strlop(ptr, ' '); // Get Number of lines + i = atoi(ptr); + + ptr1 = msgbytes; + ptr2 = --ptr1; // Point both to char before message + + while(i--) + { + ptr2 = strchr(++ptr1, 10); + + if (ptr2 == 0) // No more lines + i = 0; + + ptr1 = ptr2; + } + if (ptr2) + *(ptr2 + 1) = 0; + } + + // If message has characters above 7F convert to UFT8 if necessary and send as Base64 + + Len = (int)strlen(msgbytes); + + if (Is8Bit(msgbytes, Len)) + { + // 8 Bit. Will send as UFT8 + + if (WebIsUTF8(msgbytes, Len) == FALSE) + { + // Must be some other coding + + int code = TrytoGuessCode(msgbytes, Len); + UCHAR * UTF = malloc(Len * 3); + + if (code == 437) + Len = Convert437toUTF8(msgbytes, Len, UTF); + else if (code == 1251) + Len = Convert1251toUTF8(msgbytes, Len, UTF); + else + Len = Convert1252toUTF8(msgbytes, Len, UTF); + + free(msgbytes); + msgbytes = UTF; + } + + SendSock(sockptr, "Content-Type: text/plain; charset=\"utf-8\""); + SendSock(sockptr, "Content-Transfer-Encoding: base64"); + SendSock(sockptr, "Content-Disposition: inline"); + + SendSock(sockptr, ""); // Blank line before body + + Base64EncodeAndSend(sockptr, msgbytes, Len); + + } + else + { + // send as USASCII + + SendSock(sockptr, ""); // Blank line before body + SendSock(sockptr, msgbytes); + } + + SendSock(sockptr, ""); + SendSock(sockptr, "."); + + free(msgbytes); + return; + } + + + if (memcmp(Buffer, "DELE",4) == 0) + { + char * ptr; + int i; + struct MsgInfo * Msg; + + ptr=strlop(Buffer, ' '); // Get Number + + i=atoi(ptr); + + if ((i > sockptr->POP3MsgCount) || (i == 0)) + { + SendSock(sockptr, "-ERR no such message"); + return; + } + + Msg = sockptr->POP3Msgs[i-1]; + + FlagAsKilled(Msg, TRUE); + + SendSock(sockptr, "+OK "); + return; + } + + + if (memcmp(Buffer, "QUIT",4) == 0) + { + SendSock(sockptr, "+OK Finished"); + + if (sockptr->POP3User) + sockptr->POP3User->POP3Locked = FALSE; + + return; + } + + SendSock(sockptr, "-ERR Unrecognised Command"); + +} + + + +/* jer: + * This is the original file, my mods were only to change the name/semantics on the b64decode function + * and remove some dependencies. + */ +/* + LibCGI base64 manipulation functions is extremly based on the work of Bob Tower, + from its projec http://base64.sourceforge.net. The functions were a bit modicated. + Above is the MIT license from b64.c original code: + +LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the + Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall + be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +*/ +void encodeblock( unsigned char in[3], unsigned char out[4], int len ) +{ + out[0] = cb64[ in[0] >> 2 ]; + out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ]; + out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '='); + out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '='); +} + +void decodeblock( unsigned char in[4], unsigned char out[3] ) +{ + char Block[5]; + + Block[0]=mycd64[in[0]]; + Block[1]=mycd64[in[1]]; + Block[2]=mycd64[in[2]]; + Block[3]=mycd64[in[3]]; + + out[0] = (unsigned char ) (Block[0] << 2 | Block[1] >> 4); + out[1] = (unsigned char ) (Block[1] << 4 | Block[2] >> 2); + out[2] = (unsigned char ) (((Block[2] << 6) & 0xc0) | Block[3]); +} + +/** +* @ingroup libcgi_string +* @{ +*/ + +/** +* Encodes a given tring to its base64 form. +* +* @param *str String to convert +* @return Base64 encoded String +* @see str_base64_decode +**/ +char *str_base64_encode(char *str) +{ + unsigned int i = 0, j = 0, len = (int)strlen(str); + char *tmp = str; + char *result = (char *)zalloc((len+1) * sizeof(void *)); + + if (!result) + return NULL; + + while (len > 2 ) + { + encodeblock(&str[i], &result[j],3); + i+=3; + j+=4; + len -=3; + } + if (len) + { + encodeblock(&str[i], &result[j], len); + } + + return result; +} + +SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody) +{ + int err; + u_long param=1; + BOOL bcopt=TRUE; + + SocketConn * sockptr; + + SOCKADDR_IN sinx; + SOCKADDR_IN destaddr; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + + // Resolve Name if needed + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(Port); + + destaddr.sin_addr.s_addr = inet_addr(Host); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (Host); + + if (!HostEnt) + { + Logprintf(LOG_TCP, NULL, '|', "Resolve Failed for SMTP Server %s", Host); + SMTPActive = FALSE; + return FALSE; // Resolve failed + } + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// Allocate a Socket entry + + sockptr=malloc(sizeof(SocketConn)); + memset(sockptr, 0, sizeof (SocketConn)); + + sockptr->Next=Sockets; + Sockets=sockptr; + + sockptr->socket=socket(AF_INET,SOCK_STREAM,0); + + if (sockptr->socket == INVALID_SOCKET) + { + return FALSE; + } + + sockptr->Type = SMTPClient; + sockptr->AMPR = AMPR; + + if (AMPR) + strcpy(sockptr->FromDomain, AMPRDomain); + else + strcpy(sockptr->FromDomain, MyDomain); + + sockptr->SMTPMsg = Msg; + sockptr->MailBuffer = MsgBody; + + ioctlsocket (sockptr->socket, FIONBIO, ¶m); + + setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + return FALSE; + } + + if (connect(sockptr->socket,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + sockptr->State = WaitingForGreeting; + + return sockptr; + } + else + { + err=WSAGetLastError(); + + if (err == WSAEWOULDBLOCK || err == 115 || err == 36) + { + // + // Connect in Progress + // + + sockptr->State = Connecting; + return sockptr; + } + else + { + // + // Connect failed + // + + printf("SMTP Connect failed immediately\n"); + closesocket(sockptr->socket); + ReleaseSock(sockptr->socket); + return FALSE; + + return FALSE; + } + } + return FALSE; +} + +int TryHELO = 0; // Not thread safe but taking the chance.. + +VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + Buffer[Len] = 0; + + if (sockptr->State == WaitingForGreeting) + { + if (memcmp(Buffer, "220 ",4) == 0) + { + TryHELO = 0; + + if (sockptr->AMPR) + sockprintf(sockptr, "EHLO %s", AMPRDomain); + else if (ISPEHLOName[0]) + sockprintf(sockptr, "EHLO %s", ISPEHLOName); + else + sockprintf(sockptr, "EHLO %s", BBSName); + + sockptr->State = WaitingForHELOResponse; + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + } + + return; + } + + if (sockptr->State == WaitingForHELOResponse) + { +/* + if (memcmp(Buffer, "500 ",4) == 0 && TryHELO == 0) + { + TryHELO = 1; + + if (sockptr->AMPR) + sockprintf(sockptr, "HELO %s", AMPRDomain); + else if (ISPEHLOName[0]) + sockprintf(sockptr, "HELO %s", ISPEHLOName); + else + sockprintf(sockptr, "HELO %s", BBSName); + + return; + } +*/ + if (memcmp(Buffer, "250-",4) == 0) + return; + + if (memcmp(Buffer, "250 ",4) == 0) + { + if (SMTPAuthNeeded && sockptr->AMPR == FALSE) + { + sockprintf(sockptr, "AUTH LOGIN"); + sockptr->State = WaitingForAUTHResponse; + } + else + { + sockprintf(sockptr, "MAIL FROM: <%s@%s>", sockptr->SMTPMsg->from, sockptr->FromDomain); + sockptr->State = WaitingForFROMResponse; + } + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + SMTPActive = FALSE; + + } + + return; + } + + if (sockptr->State == WaitingForAUTHResponse) + { + if (memcmp(Buffer, "334 VXN", 7) == 0) + { + char * Msg = str_base64_encode(ISPAccountName); + SendSock(sockptr, Msg); + free(Msg); + return; + } + else if (memcmp(Buffer, "334 UGF", 7) == 0) + { + char * Msg = str_base64_encode(ISPAccountPass); + SendSock(sockptr, Msg); + free(Msg); + return; + } + else if (memcmp(Buffer, "235 ", 4) == 0) + { + sockprintf(sockptr, "MAIL FROM: <%s@%s>", sockptr->SMTPMsg->from, sockptr->FromDomain); +// sockprintf(sockptr, "MAIL FROM: <%s@%s.%s>", sockptr->SMTPMsg->from, BBSName, HRoute); + sockptr->State = WaitingForFROMResponse; + } + + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + SMTPActive = FALSE; + } + + return; + + } + + + if (sockptr->State == WaitingForFROMResponse) + { + if (memcmp(Buffer, "250 ",4) == 0) + { + sockprintf(sockptr, "RCPT TO: <%s>", sockptr->SMTPMsg->via); + sockptr->State = WaitingForTOResponse; + } + else + { + sockptr->SMTPMsg->status = 'H'; // Hold for review + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + SMTPActive = FALSE; + } + + return; + } + + if (sockptr->State == WaitingForTOResponse) + { + if (memcmp(Buffer, "250 ",4) == 0) + { + SendSock(sockptr, "DATA"); + sockptr->State = WaitingForDATAResponse; + } + else + { + sockptr->SMTPMsg->status = 'H'; // Hold for review + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + SMTPActive = FALSE; + } + + return; + } + + if (sockptr->State == WaitingForDATAResponse) + { + int Len; + UCHAR * UTF; + + if (memcmp(Buffer, "354 ",4) == 0) + { + sockprintf(sockptr, "To: %s", sockptr->SMTPMsg->via); + sockprintf(sockptr, "From: %s <%s@%s>", sockptr->SMTPMsg->from, sockptr->SMTPMsg->from, sockptr->FromDomain); + sockprintf(sockptr, "Sender: %s@%s", sockptr->SMTPMsg->from, sockptr->FromDomain); + if (GMailMode && sockptr->AMPR == FALSE) + sockprintf(sockptr, "Reply-To: %s+%s@%s", GMailName, sockptr->SMTPMsg->from, sockptr->FromDomain); + else + sockprintf(sockptr, "Reply-To: %s@%s", sockptr->SMTPMsg->from, sockptr->FromDomain); + + sockprintf(sockptr, "Subject: %s", sockptr->SMTPMsg->title); + + sockptr->State = WaitingForBodyResponse; + + if (sockptr->SMTPMsg->B2Flags & Attachments) + { + // B2 Message with Attachments. Create a Mime-Encoded Multipart message + + SendMultiPartMessage(sockptr, sockptr->SMTPMsg, sockptr->MailBuffer); + return; + } + + // If message has characters above 7F convert to UFT8 if necessary and send as Base64 + + + Len = (int)strlen(sockptr->MailBuffer); + + if (Is8Bit(sockptr->MailBuffer, Len)) + { + // 8 Bit. Will send as UFT8 + + SendSock(sockptr, "Content-Type: text/plain; charset=\"utf-8\""); + SendSock(sockptr, "Content-Transfer-Encoding: base64"); + SendSock(sockptr, "Content-Disposition: inline"); + + SendSock(sockptr, ""); // Blank line before body + + if (WebIsUTF8(sockptr->MailBuffer, Len) == FALSE) + { + // Must be some other coding + + int code = TrytoGuessCode(sockptr->MailBuffer, Len); + UTF = malloc(Len * 3); + + if (code == 437) + Len = Convert437toUTF8(sockptr->MailBuffer, Len, UTF); + else if (code == 1251) + Len = Convert1251toUTF8(sockptr->MailBuffer, Len, UTF); + else + Len = Convert1252toUTF8(sockptr->MailBuffer, Len, UTF); // Default + + Base64EncodeAndSend(sockptr, UTF, Len); + free(UTF); + } + else + Base64EncodeAndSend(sockptr, sockptr->MailBuffer, Len); + + } + else + { + // send as USASCII + + SendSock(sockptr, ""); // Blank line before body + SendSock(sockptr, sockptr->MailBuffer); + } + + SendSock(sockptr, "."); + + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + SMTPActive = FALSE; + } + + return; + } + + if (sockptr->State == WaitingForBodyResponse) + { + struct MsgInfo * Msg = sockptr->SMTPMsg; + + if (memcmp(Buffer, "250 ", 4) == 0) + { + // if AMPR, clear forwarding bitmap + + if (sockptr->AMPR) + { + // Mark mail as sent, and look for more + + struct UserInfo * bbs = sockptr->bbs; + + clear_fwd_bit(Msg->fbbs, bbs->BBSNumber); + set_fwd_bit(Msg->forw, bbs->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + Msg->status = 'F'; // Mark as forwarded + Msg->datechanged=time(NULL); + } + + bbs->ForwardingInfo->MsgCount--; + bbs->ForwardingInfo->Forwarding = 0; + + // See if any more + + if (bbs->ForwardingInfo->MsgCount) + bbs->ForwardingInfo->FwdTimer = bbs->ForwardingInfo->FwdInterval; // Reschdul send + + } + else + { + Msg->status = 'F'; + SMTPActive = FALSE; + SMTPMsgCreated=TRUE; // See if any more + } + } + + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + + SMTPActive = FALSE; + + SMTPMsgCreated=TRUE; // See if any more + + return; + } +} + +BOOL SendtoAMPR(CIRCUIT * conn) +{ + struct MsgInfo * Msg = conn->FwdMsg; + SocketConn * sockptr; + + char * Body; + int toLen; + char * tocopy; + char * Host; + + // Make sure message exists + + Body = ReadMessageFile(Msg->number); + + if (Body == NULL) + { + FlagAsKilled(Msg, TRUE); + return FALSE; + } + + toLen = (int)strlen(Msg->via); + + tocopy = _strdup(Msg->via); + + Host = strlop(tocopy, '@'); + + if (Host == NULL) + { + Logprintf(LOG_TCP, NULL, '|', "AMPR Forward - Host Name missing from VIA %s for Msg %d", Msg->via, Msg->number); + free(tocopy); + return FALSE; + } + + Logprintf(LOG_TCP, NULL, '|', "Connecting to Server %s to send Msg %d", Host, Msg->number); + + sockptr = SMTPConnect(Host, 25, TRUE, Msg, Body); + + free(tocopy); + + if (sockptr) + { + sockptr->bbs = conn->UserPointer; + + return TRUE; + } + + return FALSE; +} + +BOOL SendtoISP() +{ + // Find a message intended for the Internet and send it + + int m = NumberofMessages; + char * Body; + + struct MsgInfo * Msg; + + if (SMTPActive) + return FALSE; + + do + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status == 'N') && (Msg->to[0] == 0) && (Msg->from[0] != 0)) + { + // Make sure message exists + + Body = ReadMessageFile(Msg->number); + + if (Body == NULL) + { + FlagAsKilled(Msg, TRUE); + return FALSE; + } + + Logprintf(LOG_TCP, NULL, '|', "Connecting to Server %s to send Msg %d", ISPSMTPName, Msg->number); + + SMTPMsgCreated=FALSE; // Stop any more attempts + SMTPConnect(ISPSMTPName, ISPSMTPPort, FALSE, Msg, Body); + + SMTPActive = TRUE; + + return TRUE; + } + + m--; + + } while (m> 0); + + return FALSE; + +} + + +BOOL POP3Connect(char * Host, int Port) +{ + int err; + u_long param=1; + BOOL bcopt=TRUE; + + SocketConn * sockptr; + + SOCKADDR_IN sinx; + SOCKADDR_IN destaddr; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + + Logprintf(LOG_TCP, NULL, '|', "Connecting to POP3 Server %s", Host); + + // Resolve Name if needed + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(Port); + + destaddr.sin_addr.s_addr = inet_addr(Host); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (Host); + + if (!HostEnt) + { + Logprintf(LOG_TCP, NULL, '|', "Resolve Failed for POP3 Server %s", Host); + return FALSE; // Resolve failed + } + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// Allocate a Socket entry + + sockptr = malloc(sizeof(SocketConn)); + memset(sockptr, 0, sizeof (SocketConn)); + + sockptr->Next = Sockets; + Sockets = sockptr; + + sockptr->socket = socket(AF_INET,SOCK_STREAM,0); + + if (sockptr->socket == INVALID_SOCKET) + { + return FALSE; + } + + sockptr->Type = POP3Client; + + ioctlsocket (sockptr->socket, FIONBIO, ¶m); + + setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + return FALSE; + } + + if (connect(sockptr->socket,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + sockptr->State = WaitingForGreeting; + + return TRUE; + } + else + { + err=WSAGetLastError(); + + if (err == WSAEWOULDBLOCK || err == 115 || err == 36) + { + // + // Connect in Progressing + // + + sockptr->State = Connecting; + return TRUE; + } + else + { + // + // Connect failed + // + + printf("Connect failed immediately %d\n", err); + perror("POP Connect"); + closesocket(sockptr->socket); + ReleaseSock(sockptr->socket); + return FALSE; + } + } + return FALSE; +} + +VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + time_t Date; + BOOL B2Flag; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + if (sockptr->Flags == GETTINGMESSAGE) + { + if(memcmp(Buffer, ".\r\n", 3) == 0) + { + // File Message + + char * ptr1, * ptr2; + int linelen, MsgLen; + char MsgFrom[62], MsgTo[100], Msgtitle[62]; + + // Scan headers for From: To: and Subject: Line (Headers end at blank line) + + ptr1 = sockptr->MailBuffer; + Loop: + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == NULL) + { + SendSock(sockptr, "500 Eh"); + return; + } + + linelen = (int)(ptr2 - ptr1); + + // From: "John Wiseman" + // To: + // + + + if (_memicmp(ptr1, "From:", 5) == 0) + { + if (linelen > 65) linelen = 65; + memcpy(MsgFrom, &ptr1[5], linelen-5); + MsgFrom[linelen-5]=0; + } + else + if (_memicmp(ptr1, "To:", 3) == 0) + { + if (linelen > 99) linelen = 99; + memcpy(MsgTo, &ptr1[4], linelen-4); + MsgTo[linelen-4]=0; + } + else + if (_memicmp(ptr1, "Subject:", 8) == 0) + { + if (linelen > 68) linelen = 68; + memcpy(Msgtitle, &ptr1[9], linelen-9); + Msgtitle[linelen-9]=0; + } + else + if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char * Context; + char seps[] = " ,\t\r"; + char Offset[10] = ""; + int i, HH, MM; + char Copy[500]=""; + + // Copy message, so original isn't messed up by strtok + + memcpy(Copy, ptr1, linelen); + + ptr1 = Copy; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Tue, 9 Jun 2009 20:54:55 +0100 + + ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day + ptr1 = strtok_s(NULL, seps, &Context); // Day + + rtime.tm_mday = atoi(ptr1); + + ptr1 = strtok_s(NULL, seps, &Context); // Month + + for (i=0; i < 12; i++) + { + if (strcmp(month[i], ptr1) == 0) + { + rtime.tm_mon = i; + break; + } + } + + sscanf(Context, "%04d %02d:%02d:%02d%s", + &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = 0; + else + { + if ((Offset[0] == '+') || (Offset[0] == '-')) + { + MM = atoi(&Offset[3]); + Offset[3] = 0; + HH = atoi(&Offset[1]); + MM = MM + (60 * HH); + + if (Offset[0] == '+') + Date -= (60*MM); + else + Date += (60*MM); + + + } + } + } + + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + ptr1 = sockptr->MailBuffer; + + TidyString(MsgFrom); + _strlwr(MsgFrom); + + MsgLen = (int)(sockptr->MailSize - (ptr2 - ptr1)); + + B2Flag = CheckforMIME(sockptr, sockptr->MailBuffer, &ptr2, &MsgLen); // Will reformat message if necessary. + + CreatePOP3Message(MsgFrom, MsgTo, Msgtitle, Date, ptr2, MsgLen, B2Flag); + + free(sockptr->MailBuffer); + sockptr->MailBufferSize=0; + sockptr->MailBuffer=0; + sockptr->MailSize = 0; + + sockptr->Flags &= ~GETTINGMESSAGE; + + if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++) + { + sockprintf(sockptr, "RETR %d", sockptr->POP3MsgNum); + + sockptr->State = WaitingForRETRResponse; + } + else + { + sockptr->POP3MsgNum = 1; + sockprintf(sockptr, "DELE %d", sockptr->POP3MsgNum);; + sockptr->State = WaitingForDELEResponse; + } + + return; + } + + if ((sockptr->MailSize + Len) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 10000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sock, 0); + return; + } + } + + memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len); + sockptr->MailSize += Len; + + return; + } + + if (sockptr->State == WaitingForGreeting) + { + if (memcmp(Buffer, "+OK", 3) == 0) + { + sockprintf(sockptr, "USER %s", ISPAccountName); + sockptr->State = WaitingForUSERResponse; + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = 0; + } + + return; + } + + if (sockptr->State == WaitingForUSERResponse) + { + if (memcmp(Buffer, "+OK", 3) == 0) + { + sockprintf(sockptr, "PASS %s", ISPAccountPass); + sockptr->State = WaitingForPASSResponse; + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + } + + return; + } + + if (sockptr->State == WaitingForPASSResponse) + { + if (memcmp(Buffer, "+OK", 3) == 0) + { + SendSock(sockptr, "STAT"); + sockptr->State = WaitingForSTATResponse; + } + else + { + shutdown(sock, 0); + sockptr->State = 0; + } + + return; + } + + if (sockptr->State == WaitingForSTATResponse) + { + if (memcmp(Buffer, "+OK", 3) == 0) + { + int Msgs = atoi(&Buffer[3]); + + if (Msgs > 0) + { + sockptr->POP3MsgCount = Msgs; + sockptr->POP3MsgNum = 1; + SendSock(sockptr, "RETR 1"); + + sockptr->State = WaitingForRETRResponse; + + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + } + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + } + + return; + } + + if (sockptr->State == WaitingForRETRResponse) + { + if (memcmp(Buffer, "+OK", 3) == 0) + { + sockptr->MailBuffer=malloc(10000); + sockptr->MailBufferSize=10000; + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to create POP3 Message Buffer"); + SendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + shutdown(sock, 0); + + return; + } + + sockptr->Flags |= GETTINGMESSAGE; + + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + } + + return; + } + if (sockptr->State == WaitingForDELEResponse) + { + if (memcmp(Buffer, "+OK", 3) == 0) + { + if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++) + { + sockprintf(sockptr, "DELE %d", sockptr->POP3MsgNum);; + } + else + { + SendSock(sockptr, "QUIT"); + sockptr->Flags = WaitingForQUITResponse; + } + } + else + { + shutdown(sock,0); + sockptr->State = 0; + } + return; + } + + if (sockptr->State == WaitingForQUITResponse) + { + shutdown(sock,0); + sockptr->State = 0; + return; + } + + SendSock(sockptr, "QUIT"); + shutdown(sock,0); + sockptr->State = 0; + +} + +static char Winlink[] = "WINLINK.ORG"; + +int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + Msg->length = MsgLen; + + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (Date) + Msg->datecreated = Date; + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + + TidyString(To); + strlop(To, '@'); + + // Could have surrounding "" "" + + if (To[0] == '"') + { + int len = (int)strlen(To) - 1; + + if (To[len] == '"') + { + To[len] = 0; + memmove(To, &To[1], len); + } + } + + if (GMailMode) + { + // + separates our address and the target user + + char * GMailto; + + GMailto = strlop(To,'+'); + + if (GMailto) + { + char * GmailVia = NULL; + + strcpy(To, GMailto); + GmailVia = strlop(To, '|'); + + if (GmailVia) + strcpy(Msg->via, GmailVia); + } + else + { + // Someone has sent to the GMAIL account without a +. + // This should go to the BBS Call + + strcpy(To, BBSName); + } + } + + if ((_memicmp(To, "bull/", 5) == 0) || (_memicmp(To, "bull.", 5) == 0) + || (_memicmp(To, "bull:", 5) == 0)) + { + Msg->type = 'B'; + memmove(To, &To[5], strlen(&To[4])); + } + + if ((_memicmp(To, "nts/", 4) == 0) || (_memicmp(To, "nts.", 4) == 0) + || (_memicmp(To, "nts:", 4) == 0)) + { + Msg->type = 'T'; + memmove(To, &To[4], strlen(&To[3])); + } + + if (Msg->type == 'P' && Msg->via[0] == 0) + { + // No via - add one from HomeBBS or WP + + struct UserInfo * ToUser = LookupCall(To); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + strcpy(Msg->via, Winlink); + } + else if (ToUser->HomeBBS[0]) + strcpy(Msg->via, ToUser->HomeBBS); + } + else + { + WPRec * WP = LookupWP(To); + + if (WP) + strcpy(Msg->via, WP->first_homebbs); + } + } + +/* if (_memicmp(To, "rms:", 4) == 0) + { + via = _strlwr(strlop(To, ':')); + } + else if (_memicmp(To, "rms/", 4) == 0) + { + via = _strlwr(strlop(To, '/')); + } + else if (_memicmp(To, "rms.", 4) == 0) + { + via = _strlwr(strlop(To, '.')); + } +*/ + + + if (strlen(To) > 6) To[6]=0; + + strcpy(Msg->to, To); + strcpy(Msg->from, "smtp:"); + strcpy(Msg->emailfrom, From); + strcpy(Msg->title, MsgTitle); + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + if (B2Flag) + { + char B2Hddr[1000]; + int B2HddrLen; + char B2To[80]; + char * NewBody; + char DateString[80]; + struct tm * tm; + char Type[16] = "Private"; + + // Get Type + + if (Msg->type == 'B') + strcpy(Type, "Bulletin"); + else if (Msg->type == 'T') + strcpy(Type, "Traffic"); + + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + + if (strcmp(Msg->to, "RMS") == 0) // Address is in via + strcpy(B2To, Msg->via); + else + if (Msg->via[0]) + sprintf(B2To, "%s@%s", Msg->to, Msg->via); + else + strcpy(B2To, Msg->to); + + + Msg->B2Flags = B2Msg | Attachments; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, Type, + Msg->from, B2To, Msg->title, BBSName); + + NewBody = MsgBody - B2HddrLen; + + memcpy(NewBody, B2Hddr, B2HddrLen); + + Msg->length += B2HddrLen; + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + return CreateSMTPMessageFile(NewBody, Msg); + } + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + return CreateSMTPMessageFile(MsgBody, Msg); + +} + +VOID base64_encode(char *str, char * result, int len) +{ + unsigned int i = 0, j = 0; + char *tmp = str; + + + while (len > 2 ) + { + encodeblock(&str[i], &result[j],3); + i+=3; + j+=4; + len -=3; + } + if (len) + { + encodeblock(&str[i], &result[j], len); + } + + return; +} + +void Base64EncodeAndSend(SocketConn * sockptr, UCHAR * Msg, int Len) +{ + char Base64Line[80]; + int i = Len; + int j = 0; + + Base64Line[76] = 13; + Base64Line[77] = 10; + Base64Line[78] = 0; + + // Need to encode in 57 byte chunks to give 76 char lines. + + while(i > 57) + { + base64_encode(&Msg[j], Base64Line, 57); + SendSock(sockptr, Base64Line); + + j += 57; + i -= 57; + } + + memset(Base64Line, 0, 79); + + base64_encode(&Msg[j], Base64Line, i); + SendSock(sockptr, Base64Line); + SendSock(sockptr, ""); +} + +VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes) +{ + char * ptr; + char Header[120]; + char Separator[33]=""; + char FileName[100][250] = {""}; + int FileLen[100]; + int Files = 0; + int BodyLen; + int i; + + CreateOneTimePassword(&Separator[0], "Key", 0); + CreateOneTimePassword(&Separator[16], "Key", 1); + + SendSock(sockptr, "MIME-Version: 1.0"); + + sprintf_s(Header, sizeof(Header), "Content-Type: multipart/mixed; boundary=\"%s\"", Separator); + SendSock(sockptr, Header); + + SendSock(sockptr, ""); // Blank line before body + +// Get Part Sizes and Filenames + + ptr = msgbytes; + + while(*ptr != 13) + { + char * ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + memcpy(FileName[Files++], &ptr1[1], (ptr2-ptr1 - 2)); + } + + ptr = ptr2; + ptr++; + } + + ptr += 2; // Over Blank Line + + // Write the none-Mime Part + + SendSock(sockptr, "This is a multi-part message in MIME format."); + SendSock(sockptr, ""); + + // Write the Body as the first part. + + sprintf_s(Header, sizeof(Header), "--%s", Separator); + SendSock(sockptr, Header); + SendSock(sockptr, "Content-Type: text/plain"); + SendSock(sockptr, ""); + + ptr[BodyLen] = 0; + + SendSock(sockptr, ptr); + + ptr += BodyLen; // to first file + ptr += 2; // Over Blank Line + + // Write Each Attachment + + for (i = 0; i < Files; i++) + { + sprintf_s(Header, sizeof(Header), "--%s", Separator); + SendSock(sockptr, Header); +// Content-Type: image/png; name="UserParams.png" + SendSock(sockptr, "Content-Transfer-Encoding: base64"); + + sprintf_s(Header, sizeof(Header), "Content-Disposition: attachment; filename=\"%s\"", FileName[i]); + SendSock(sockptr, Header); + + SendSock(sockptr, ""); + + // base64 encode and send file + + Base64EncodeAndSend(sockptr, ptr, FileLen[i]); + + ptr += FileLen[i]; + ptr +=2; // Over separator + } + + sprintf_s(Header, sizeof(Header), "--%s--", Separator); + SendSock(sockptr, Header); + + SendSock(sockptr, ""); + SendSock(sockptr, "."); + + free(msgbytes); + + return; +} + +BOOL SendAMPRSMTP(CIRCUIT * conn) +{ + struct UserInfo * bbs = conn->UserPointer; + + while (FindMessagestoForward(conn)) + { + if (SendtoAMPR(conn)) + { + bbs->ForwardingInfo->Forwarding = TRUE; + return TRUE; + } + } + + bbs->ForwardingInfo->Forwarding = FALSE; + return FALSE; +} + diff --git a/Moncode.c b/Moncode.c new file mode 100644 index 0000000..7afcf48 --- /dev/null +++ b/Moncode.c @@ -0,0 +1,937 @@ +/* +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 +*/ + + + +// Monitor Code - from moncode.asm + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "CHeaders.h" +#include "tncinfo.h" + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +char * strlop(char * buf, char delim); +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, int msglen); + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); +int CountBits(uint64_t in); + +DllExport int APIENTRY SetTraceOptions(int mask, int mtxparam, int mcomparam) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + + return (0); +} + +DllExport int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +int IntSetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, unsigned long long Mask, BOOL APRS, BOOL MCTL); + +int APRSDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, UINT Mask) +{ + return IntDecodeFrame(msg, buffer, Stamp, Mask, TRUE, FALSE); +} +DllExport int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, int Stamp) +{ + return IntDecodeFrame(msg, buffer, Stamp, MMASK, FALSE, FALSE); +} + +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, unsigned long long Mask, BOOL APRS, BOOL MINI) +{ + UCHAR * ptr; + int n; + MESSAGE * ADJBUFFER; + ptrdiff_t Work; + UCHAR CTL; + BOOL PF = 0; + char CRCHAR[3] = " "; + char PFCHAR[3] = " "; + int Port; + int MSGFLAG = 0; //CR and V1 flags + char * Output = buffer; + int HH, MM, SS; + char TR = 'R'; + char From[10], To[10]; + BOOL Info = 0; + BOOL FRMRFLAG = 0; + BOOL XIDFLAG = 0; + BOOL TESTFLAG = 0; + + size_t MsgLen = msg->LENGTH; + + // MINI mode is for Node Listen (remote monitor) Mode. Keep info to minimum +/* +KO6IZ*>K7TMG-1: +/ex +KO6IZ*>K7TMG-1: +b +KO6IZ*>K7TMG-1 (UA) +W0TX*>KC6OAR>KB9KC>ID: +W0TX/R DRC/D W0TX-2/G W0TX-1/B W0TX-7/N +KC6OAR*>ID: +*/ + // Check Port + + Port = msg->PORT; + + if (Port & 0x80) + { + if (MTX == 0) + return 0; // TRANSMITTED FRAME - SEE IF MTX ON + + TR = 'T'; + } + + Port &= 0x7F; + + if (((1ll << (Port - 1)) & Mask) == 0) // Check MMASK + return 0; + + + // We now pass Text format monitoring from non-ax25 drivers through this code + // As a valid ax.25 address must have bottom bit set flag plain text messages + // with hex 01. + + // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED + + if (msg->DEST[0] == 1) + { + // Just copy text (Null Terminated) to output + + // Need Timestamp and T/R + + Stamp = Stamp % 86400; // Secs + HH = (int)(Stamp / 3600); + + Stamp -= HH * 3600; + MM = (int)(Stamp / 60); + + SS = (int)(Stamp - MM * 60); + + // Add Port: unless Mail Mon (port 33) + + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", HH, MM, SS, TR); + + strcpy(Output, &msg->DEST[1]); + Output += strlen(Output); + + if (buffer[strlen(buffer) -1] == '\r') + Output--; + + if (Port == 33) + Output += sprintf((char *)Output, " \r"); + else + Output += sprintf((char *)Output, " Port=%d\r", Port); + + return (int)strlen(buffer); + } + + n = 8; // MAX DIGIS + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + n--; + + if (n == 0) + { + return 0; // Corrupt - no end of address bit + } + } + + // Reached End of digis + + Work = ptr - &msg->ORIGIN[6]; // Work is length of digis + + MsgLen -= Work; + + ADJBUFFER = (MESSAGE *)((UCHAR *)msg + Work); // ADJBUFFER points to CTL, etc. allowing for digis + + CTL = ADJBUFFER->CTL; + + if (CTL & PFBIT) + PF = TRUE; + + CTL &= ~PFBIT; + + if (MUIONLY) + if (CTL != 3) + return 0; + + if ((CTL & 1) == 0 || CTL == FRMR || CTL == 3) + { + } + else + { + if (((CTL & 2) && MINI) == 0) // Want Control (but not super unless MCOM + if (MCOM == 0) + return 0; // Dont do control + } + + + Stamp = Stamp % 86400; // Secs + HH = (int)(Stamp / 3600); + + Stamp -= HH * 3600; + MM = (int)(Stamp / 60); + + SS = (int)(Stamp - MM * 60); + + // Add Port: if MINI mode and monitoring more than one port + + if (MINI == 0) + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", HH, MM, SS, TR); + else + if (CountBits(Mask) > 1) + Output += sprintf((char *)Output, "%d:", Port); + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + Output += sprintf((char *)Output, "%s>%s", From, To); + + // Display any Digi-Peaters + + n = 8; // Max number of digi-peaters + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + Output += sprintf((char *)Output, ",%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + *(Output++) = '*'; + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + *(Output++) = '*'; // No, so need * + } + } + + if (MINI == 0) + Output += sprintf((char *)Output, " Port=%d ", Port); + + // Set up CR and PF + + CRCHAR[0] = 0; + PFCHAR[0] = 0; + + if (msg->DEST[6] & 0x80) + { + if (msg->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + { + MSGFLAG |= CMDBIT; + CRCHAR[0] = ' '; + CRCHAR[1] = 'C'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'P'; + } + } + } + else + { + if (msg->ORIGIN[6] & 0x80) // Only Origin Set + { + MSGFLAG |= RESP; + CRCHAR[0] = ' '; + CRCHAR[1] = 'R'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'F'; + } + } + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + if ((CTL & 1) == 0) // I frame + { + int NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + int NR = (CTL >> 5) & 7; + + Info = 1; + + if (MINI == 0) + Output += sprintf((char *)Output, "", CRCHAR, PFCHAR, NS, NR); + } + else if (CTL == 3) + { + // Un-numbered Information Frame + + Output += sprintf((char *)Output, "", CRCHAR); + Info = 1; + } + else if (CTL & 2) + { + // UN Numbered + + char SUP[6] = "??"; + + switch (CTL) + { + case SABM: + + strcpy(SUP, "C"); + break; + + case SABME: + + strcpy(SUP, "SABME"); + break; + + case XID: + + strcpy(SUP, "XID"); + XIDFLAG = 1; + break; + + case TEST: + + strcpy(SUP, "TEST"); + TESTFLAG = 1; + break; + + case DISC: + + strcpy(SUP, "D"); + break; + + case DM: + + strcpy(SUP, "DM"); + break; + + case UA: + + strcpy(SUP, "UA"); + break; + + + case FRMR: + + strcpy(SUP, "FRMR"); + FRMRFLAG = 1; + break; + } + + if (MINI) + Output += sprintf((char *)Output, "<%s>", SUP); + else + Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); + } + else + { + // Super + + int NR = (CTL >> 5) & 7; + char SUP[5] = "??"; + + switch (CTL & 0x0F) + { + case RR: + + strcpy(SUP, "RR"); + break; + + case RNR: + + strcpy(SUP, "RNR"); + break; + + case REJ: + + strcpy(SUP, "REJ"); + break; + case SREJ: + + strcpy(SUP, "SREJ"); + break; + } + + Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); + + } + + if (FRMRFLAG) + Output += sprintf((char *)Output, " %02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + + if (XIDFLAG) + { + // Decode and display XID + + UCHAR * ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + +// G8BPQ-2>G8BPQ:(XID cmd, p=1) Half-Duplex SREJ modulo-128 I-Field-Length-Rx=256 Window-Size-Rx=32 Ack-Timer=3000 Retries=10 + + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + case 3: + + Output += sprintf((char *)Output, " %d=%x", Type, value); + break; + + case 6: //RX Size + + Output += sprintf((char *)Output, " RX Paclen=%d", value / 8); + break; + + case 8: //RX Window + + Output += sprintf((char *)Output, " RX Window=%d", value); + break; + } + } + } + } + if (Info) + { + // We have an info frame + + switch (ADJBUFFER->PID) + { + case 0xF0: // Normal Data + { + char Infofield[257]; + char * ptr1 = Infofield; + char * ptr2 = ADJBUFFER->L2DATA; + UCHAR C; + size_t len; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return 0; // Duff + + while (MsgLen--) + { + C = *(ptr2++); + + if (APRS) + *(ptr1++) = C; // MIC-E needs all chars + else + { + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + + break; + } + case NETROM_PID: + + Output = DISPLAY_NETROM(ADJBUFFER, Output, (int)MsgLen); + break; + + case IP_PID: + + Output += sprintf((char *)Output, " \r"); + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, (int)MsgLen); + break; + + case ARP_PID: + + Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output); + break; + + case 8: // Fragmented IP + + n = ADJBUFFER->L2DATA[0]; // Frag Count + + Output += sprintf((char *)Output, "\r", n); + + if (ADJBUFFER->L2DATA[0] & 0x80) // First Frag - Display Header + { + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[2], Output, (int)MsgLen - 1); + } + + break; + } + } + + if (Output[-1] != 13) + Output += sprintf((char *)Output, "\r"); + + return (int)(Output - buffer); + +} +// Display NET/ROM data + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen) +{ + char Alias[7]= ""; + char Dest[10]; + char Node[10]; + UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window; + UCHAR * ptr = &ADJBUFFER->L2DATA[0]; + + if (ADJBUFFER->L2DATA[0] == NODES_SIG) + { + // Display NODES + + + // If an INP3 RIF (type <> UI) decode as such + + if (ADJBUFFER->CTL != 3) // UI + return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - 24); + + memcpy(Alias, ++ptr, 6); + + ptr += 6; + + Output += sprintf((char *)Output, " NODES broadcast from %s\r", Alias); + + MsgLen -= 30; //Header, mnemonic and signature length + + while(MsgLen > 20) // Entries are 21 bytes + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + memcpy(Alias, ptr, 6); + ptr +=6; + strlop(Alias, ' '); + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]); + ptr++; + MsgLen -= 21; + } + return Output; + } + + // Display normal NET/ROM transmissions + + Output += sprintf((char *)Output, " NET/ROM\r "); + + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + TTL = *(ptr++); + Index = *(ptr++); + ID = *(ptr++); + TXNO = *(ptr++); + RXNO = *(ptr++); + OpCode = Flags = *(ptr++); + + OpCode &= 15; // Remove Flags + + Output += sprintf((char *)Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID ); + MsgLen -= 20; + + switch (OpCode) + { + case L4CREQ: + + Window = *(ptr++); + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " w=%d %s at %s", Window, Dest, Node); + + if (MsgLen > 38) // BPQ Extended Params + { + short Timeout = (SHORT)*ptr; + Output += sprintf((char *)Output, " t/o %d", Timeout); + } + + return Output; + + case L4CACK: + + if (Flags & L4BUSY) // BUSY RETURNED + return Output + sprintf((char *)Output, " - BUSY"); + + return Output + sprintf((char *)Output, " w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO); + + case L4DREQ: + + return Output + sprintf((char *)Output, " "); + + case L4DACK: + + return Output + sprintf((char *)Output, " "); + + case L4INFO: + { + char Infofield[257]; + char * ptr1 = Infofield; + UCHAR C; + size_t len; + + Output += sprintf((char *)Output, " ", TXNO, RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return Output; // Duff + + while (MsgLen--) + { + C = *(ptr++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + } + + return Output; + + case L4IACK: + + Output += sprintf((char *)Output, " ", RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + return Output; + + + case 0: + + // OPcode zero is used for several things + + if (Index == 0x0c && ID == 0x0c) // IP + { + *(Output++) = 13; + *(Output++) = ' '; + Output = DISPLAYIPDATAGRAM((IPMSG *)ptr, Output, MsgLen); + return Output; + } + + if (Index == 0 && ID == 1) // NRR + { + Output += sprintf((char *)Output, " \r"); + + MsgLen -= 23; + + while (MsgLen > 6) + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + + if (ptr[7] & 0x80) + Output += sprintf((char *)Output, "%s* ", Dest); + else + Output += sprintf((char *)Output, "%s ", Dest); + + ptr +=8; + MsgLen -= 8; + } + + return Output; + } + } + + Output += sprintf((char *)Output, " "); + return Output; +} + +/* + + PUBLIC L3IP +L3IP: +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER + +*/ +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen) +{ + UCHAR * ptr; + USHORT FRAGWORD; + + ptr = (UCHAR *)&IP->IPSOURCE; + Output += sprintf((char *)Output, "%d.%d.%d.%d>", ptr[0], ptr[1], ptr[2], ptr[3]); + + ptr = (UCHAR *)&IP->IPDEST; + Output += sprintf((char *)Output, "%d.%d.%d.%d LEN:%d ", ptr[0], ptr[1], ptr[2], ptr[3], htons(IP->IPLENGTH)); + + FRAGWORD = ntohs(IP->FRAGWORD); + + if (FRAGWORD) + { + // If nonzero, check which bits are set + + //Bit 0: reserved, must be zero + //Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. + //Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. + //Fragment Offset: 13 bits + + if (FRAGWORD & (1 << 14)) + Output += sprintf((char *)Output, "DF "); + + if (FRAGWORD & (1 << 13)) + Output += sprintf((char *)Output, "MF "); + + FRAGWORD &= 0xfff; + + if (FRAGWORD) + { + Output += sprintf((char *)Output, "Offset %d ", FRAGWORD * 8); + return Output; // Cant display proto fields + } + } + + if (IP->IPPROTOCOL == 6) + { + PTCPMSG TCP = (PTCPMSG)&IP->Data; + + Output += sprintf((char *)Output, "TCP Src %d Dest %d ", ntohs(TCP->SOURCEPORT), ntohs(TCP->DESTPORT)); + return Output; + } + + if (IP->IPPROTOCOL == 1) + { + PICMPMSG ICMPptr = (PICMPMSG)&IP->Data; + + Output += sprintf((char *)Output, "ICMP "); + + if (ICMPptr->ICMPTYPE == 8) + Output += sprintf((char *)Output, "Echo Request "); + else + if (ICMPptr->ICMPTYPE == 0) + Output += sprintf((char *)Output, "Echo Reply "); + else + Output += sprintf((char *)Output, "Code %d", ICMPptr->ICMPTYPE); + + return Output; + } + +/* + MOV AL,IPPROTOCOL[ESI] + CMP AL,6 + JNE @F + + MOV EBX, OFFSET TCP + CALL NORMSTR + JMP ADD_CR +@@: + + CMP AL,1 + JNE @F + + MOV EBX, OFFSET ICMP + CALL NORMSTR + JMP ADD_CR +@@: + + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + +; mov AL,CR +; call PUTCHAR +; +; MOV ECX,39 ; TESTING +;IPLOOP: +; lodsb +; CALL BYTE_TO_HEX +; +; LOOP IPLOOP + + JMP ADD_CR + + +*/ + return Output; +} + + + +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output) +{ + UCHAR * ptr = Datagram; + char Dest[10]; + + if (ptr[7] == 1) // Request + return Output + sprintf((char *)Output, " ARP Request who has %d.%d.%d.%d? Tell %d.%d.%d.%d", + ptr[26], ptr[27], ptr[28], ptr[29], ptr[15], ptr[16], ptr[17], ptr[18]); + + // Response + + Dest[ConvFromAX25(&ptr[8], Dest)] = 0; + + return Output + sprintf((char *)Output, " ARP Reply %d.%d.%d.%d is at %s Tell %d.%d.%d.%d", + ptr[15], ptr[16], ptr[17], ptr[18], Dest, ptr[26], ptr[27], ptr[28], ptr[29]); + +} diff --git a/Monitor.c b/Monitor.c new file mode 100644 index 0000000..a06d3b6 --- /dev/null +++ b/Monitor.c @@ -0,0 +1,424 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Monitor Window(s) Module + +#include "bpqmail.h" + +static char ClassName[]="BPQMONWINDOW"; + +char SYSOPCall[50]; + +static WNDPROC wpOrigInputProc; +static WNDPROC wpOrigOutputProc; + +HWND hMonitor; + +static HWND hwndInput; +static HWND hwndOutput; + +static HMENU hMenu; // handle of menu + + +#define InputBoxHeight 25 +RECT MonitorRect; +RECT OutputRect; + +int Height, Width, LastY; + +static char kbbuf[160]; +static int kbptr=0; + +static char * readbuff; +static int readbufflen; + +static BOOL StripLF = TRUE; +BOOL MonBBS = TRUE; +BOOL MonCHAT = TRUE; +BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + + +static int PartLinePtr=0; +static int PartLineIndex=0; // Listbox index of (last) incomplete line + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static void MoveWindows(); +static void MoveMCWindows(); + +#define BGCOLOUR RGB(236,233,216) + +BOOL CreateMonitor() +{ + WNDCLASS wc; + HBRUSH bgBrush; + + if (hMonitor) + { + ShowWindow(hMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMonitor); + return FALSE; // Alreaqy open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MonWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hMonitor=CreateDialog(hInst,ClassName,0,NULL); + + if (!hMonitor) + return (FALSE); + + readbuff = zalloc(1000); + readbufflen = 1000; + + hMenu=GetMenu(hMonitor); + + CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + // Retrieve the handlse to the edit controls. + + hwndOutput = GetDlgItem(hMonitor, 121); + + // Set our own WndProcs for the controls. + + wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); + + if (cfgMinToTray) + AddTrayMenuItem(hMonitor, "Mail Monitor"); + + ShowWindow(hMonitor, SW_SHOWNORMAL); + + if (MonitorRect.right < 100 || MonitorRect.bottom < 100) + { + GetWindowRect(hMonitor, &MonitorRect); + } + + MoveWindow(hMonitor,MonitorRect.left,MonitorRect.top, MonitorRect.right-MonitorRect.left, MonitorRect.bottom-MonitorRect.top, TRUE); + + MoveWindows(); + + return TRUE; + +} + +static void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMonitor, &rcMain); + GetClientRect(hMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + + switch (message) + { + + case WM_ACTIVATE: + + SetFocus(hwndInput); + break; + + case WM_CLOSE: + if (wParam) // Used by Close All Programs. + return 0; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case MONBBS: + + ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); + break; + + case MONCHAT: + + ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); + break; + + case MONTCP: + + ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); + break; + + + case BPQCLEAROUT: + + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyToClipboard(hwndOutput); + break; + + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMonitor = NULL; + + free(readbuff); + readbufflen = 0; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_LBUTTONDBLCLK) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +int WritetoMonitorWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len+PartLinePtr > readbufflen) + { + readbufflen += len+PartLinePtr; + readbuff = realloc(readbuff, readbufflen); + } + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + *(ptr2)=32; + + } while (ptr2); + +lineloop: + +// if (PartLinePtr > 300) +// PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return (0); + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + + + return (0); +} + +static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; ihConsole) + { + ShowWindow(Cinfo->hConsole, SW_SHOWNORMAL); + SetForegroundWindow(Cinfo->hConsole); + return FALSE; // Already open + } + + memset(Cinfo, 0, sizeof(struct ConsoleInfo)); + + Cinfo->BPQStream = Stream; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = ConsWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hConsole = CreateDialog(hInst,ClassName,0,NULL); + + if (!hConsole) + return (FALSE); + + Cinfo->Bells = Bells; + Cinfo->FlashOnBell = FlashOnBell; + Cinfo->StripLF = StripLF; + Cinfo->CloseWindowOnBye = CloseWindowOnBye; + Cinfo->WarnWrap = WarnWrap; + Cinfo->WrapInput= WrapInput; + Cinfo->FlashOnConnect = FlashOnConnect; + + Cinfo->ConsoleRect = ConsoleRect; + + Cinfo->readbuff = zalloc(1000); + Cinfo->readbufflen = 1000; + + hMenu=GetMenu(hConsole); + Cinfo->hMenu = hMenu; + + CheckMenuItem(hMenu,BPQBELLS, (Cinfo->Bells) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQFLASHONBELL, (Cinfo->FlashOnBell) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQStripLF, (Cinfo->StripLF) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WARNINPUT, (Cinfo->WarnWrap) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WRAPTEXT, (Cinfo->WrapInput) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_Flash, (Cinfo->FlashOnConnect) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_CLOSEWINDOW, (Cinfo->CloseWindowOnBye) ? MF_CHECKED : MF_UNCHECKED); + + + hActionMenu=GetSubMenu(hMenu,2); + hBBSUSERCHAT=GetSubMenu(hActionMenu,0); + + DrawMenuBar(hWnd); + + if (trayMenu == 0) + { + trayMenu = CreatePopupMenu(); + AppendMenu(trayMenu,MF_STRING,40000,"Copy"); + } + + // Set up RTF Header, including Colours String; + + memcpy(RTFColours, "{\\colortbl ;", 12); + n = 12; + + for (i = 1; i < 100; i++) + { + COLORREF Colour = Colours[i]; + n += sprintf(&RTFColours[n], "\\red%d\\green%d\\blue%d;", GetRValue(Colour), GetGValue(Colour),GetBValue(Colour)); + } + + RTFColours[n++] = '}'; + RTFColours[n] = 0; + + strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1 FixedSys;}}"); +// strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fcharset204\\fprq1 FixedSys;}}"); + strcat(RTFHeader, RTFColours); + strcat(RTFHeader, "\\viewkind4\\uc1\\pard\\f0"); + + RTFHddrLen = strlen(RTFHeader); + + // Create a Rich Text Control + + Cinfo->SendHeader = TRUE; + Cinfo->Finished = TRUE; + Cinfo->CurrentColour = 1; + + LoadLibrary("riched20.dll"); + + Cinfo->hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 6,145,290,130, hConsole, NULL, hInst, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(Cinfo->hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(Cinfo->hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + Cinfo->hwndInput = GetDlgItem(hConsole, 118); + + // Set our own WndProcs for the controls. + + Cinfo->wpOrigInputProc = (WNDPROC) SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, (LONG) InputProc); + + if (cfgMinToTray) + if (Stream == -1) + AddTrayMenuItem(hConsole, "BBS Console"); + else + AddTrayMenuItem(hConsole, "Chat Console"); + + if (Stream == -1) + SetWindowText(hConsole, "BBS Console"); + else + SetWindowText(hConsole, "Chat Console"); + + ShowWindow(hConsole, SW_SHOWNORMAL); + + if (Cinfo->ConsoleRect.right < 100 || Cinfo->ConsoleRect.bottom < 100) + { + GetWindowRect(hConsole, &Cinfo->ConsoleRect); + } + + MoveWindow(hConsole, Cinfo->ConsoleRect.left, Cinfo->ConsoleRect.top, + Cinfo->ConsoleRect.right-Cinfo->ConsoleRect.left, + Cinfo->ConsoleRect.bottom-Cinfo->ConsoleRect.top, TRUE); + + Cinfo->hConsole = hConsole; + + MoveWindows(Cinfo); + + Cinfo->Console = zalloc(sizeof(CIRCUIT)); + + Cinfo->Console->Active = TRUE; + Cinfo->Console->BPQStream = Stream; + + strcpy(Cinfo->Console->Callsign, SYSOPCall); + + user = LookupCall(SYSOPCall); + + if (user == NULL) + { + user = AllocateUserRecord(SYSOPCall); + + if (user == NULL) return 0; // Cant happen?? + + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + time((time_t *)&user->TimeLastConnected); + user->Total.ConnectsIn++; + + Cinfo->Console->UserPointer = user; + Cinfo->Console->lastmsg = user->lastmsg; + Cinfo->Console->paclen=236; + Cinfo->Console->sysop = TRUE; + + Cinfo->Console->PageLen = user->PageLen; + Cinfo->Console->Paging = (user->PageLen > 0); + + nodeprintf(Cinfo->Console, BBSSID, "BPQ-", Ver[0], Ver[1], Ver[2], Ver[3], "B", "", "", "", "F"); + + if (user->Name[0] == 0) + { + Cinfo->Console->Flags |= GETTINGUSER; + SendUnbuffered(-1, NewUserPrompt, strlen(NewUserPrompt)); + } + else + { + if (Stream == -2) + { + if(ChatApplMask == 0) + { + BBSputs(Cinfo->Console, "Chat Node is disabled\r"); + SendPrompt(Cinfo->Console, user); + return TRUE; + } + } + else + SendWelcomeMsg(-1, Cinfo->Console, user); + } + return TRUE; + +} + + +VOID CloseConsole(int Stream) +{ + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + CloseConsoleSupport(Cinfo); + return; + } + } + } +} + + + +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo) +{ + GetWindowRect(Cinfo->hConsole, &ConsoleRect); + + if (Cinfo->CloseWindowOnBye) + { +// PostMessage(hConsole, WM_DESTROY, 0, 0); + DestroyWindow(Cinfo->hConsole); + } +} + +void MoveWindows(struct ConsoleInfo * Cinfo) +{ + RECT rcClient; + int ClientWidth; + + GetClientRect(Cinfo->hConsole, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(Cinfo->hwndOutput,2, 2, ClientWidth-4, Cinfo->ClientHeight-InputBoxHeight-4, TRUE); + MoveWindow(Cinfo->hwndInput,2, Cinfo->ClientHeight-InputBoxHeight-2, ClientWidth-4, InputBoxHeight, TRUE); + + GetClientRect(Cinfo->hwndOutput, &rcClient); + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + Cinfo->WarnLen = ClientWidth/8 - 1; + Cinfo->WrapLen = Cinfo->WarnLen; + Cinfo->maxlinelen = Cinfo->WarnLen; + +} + +LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + int i; + struct ConsoleInfo * Cinfo; + ConnectionInfo * conn; + + UCHAR tchBuffer[100000]; + UCHAR * buf = tchBuffer; + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hConsole == hWnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + switch (message) { + + case WM_CTLCOLOREDIT: + + if (Cinfo->Scrolled) + { + HDC hdcStatic = (HDC)wParam; + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)GetStockObject(LTGRAY_BRUSH); + } + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + case WM_VSCROLL: + break; + + case WM_NOTIFY: + { + const MSGFILTER * pF = (MSGFILTER *)lParam; + POINT pos; + CHARRANGE Range; + + if(pF->nmhdr.hwndFrom == Cinfo->hwndOutput) + { + if(pF->msg == WM_VSCROLL) + { +// int Command = LOWORD(pF->wParam); +// int Pos = HIWORD(pF->wParam); + +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + DoRefresh(Cinfo); + break; + } + + if(pF->msg == WM_KEYUP) + { + if (pF->wParam == VK_PRIOR || pF->wParam == VK_NEXT) + { +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + DoRefresh(Cinfo); + } + } + + if(pF->msg == WM_RBUTTONDOWN) + { + // Only allow popup if something is selected + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + if (Range.cpMin == Range.cpMax) + return TRUE; + + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return TRUE; + } + } + break; + } + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + return TRUE; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + SendMessage(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + if ((Cinfo->BPQStream == -2) && (tchBuffer[0] == 0x1b)) + { + SetTextColor(lpdis->hDC, Colours[tchBuffer[1] - 10]); + buf += 2; + } +// SetBkColor(lpdis->hDC, 0); + + TextOut(lpdis->hDC, + 6, + y, + buf, + strlen(buf)); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + return TRUE; + + + case WM_ACTIVATE: + + SetFocus(Cinfo->hwndInput); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= BBSUSERCHAT && wmId < BBSUSERCHAT + 63) + { + // Chat to user + + conn=&Connections[wmId-BBSUSERCHAT]; + + if (conn->Active) + { + conn->BBSFlags |= SYSOPCHAT; + Cinfo->Console->SysopChatStream = conn; + SendUnbuffered(conn->BPQStream, chatMsg, strlen(chatMsg)); + + +// Disconnect(conn->BPQStream); + } + } + + switch (wmId) { + + case ENDUSERCHAT: + + if (Cinfo->Console->SysopChatStream) + { + SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, endChatMsg, strlen(endChatMsg)); + Cinfo->Console->SysopChatStream->BBSFlags &= ~SYSOPCHAT; + SendPrompt(Cinfo->Console->SysopChatStream, Cinfo->Console->SysopChatStream->UserPointer); + SendPrompt(Cinfo->Console, Cinfo->Console->UserPointer); + Cinfo->Console->SysopChatStream = 0; + } + + break; + + + case 40000: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(Cinfo->hConsole)) + { + len = SendMessage(Cinfo->hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + SetFocus(Cinfo->hwndInput); + break; + } + + case BPQBELLS: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->Bells, BPQBELLS); + Bells = Cinfo->Bells; + break; + + case BPQFLASHONBELL: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->FlashOnBell, BPQFLASHONBELL); + FlashOnBell = Cinfo->FlashOnBell; + break; + + case BPQStripLF: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->StripLF, BPQStripLF); + StripLF = Cinfo->StripLF; + break; + + case IDM_WARNINPUT: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->WarnWrap, IDM_WARNINPUT); + WarnWrap = Cinfo->WarnWrap; + break; + + + case IDM_WRAPTEXT: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->WrapInput, IDM_WRAPTEXT); + Cinfo->WrapInput = WrapInput; + break; + + case IDM_Flash: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->FlashOnConnect, IDM_Flash); + FlashOnConnect = Cinfo->FlashOnConnect; + break; + + case IDM_CLOSEWINDOW: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->CloseWindowOnBye, IDM_CLOSEWINDOW); + CloseWindowOnBye = Cinfo->CloseWindowOnBye; + break; + + case BPQCLEAROUT: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + break; + + + SendMessage(Cinfo->hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyRichTextToClipboard(Cinfo->hwndOutput); + break; + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Cinfo->Height = lprc->bottom-lprc->top; + Cinfo->Width = lprc->right-lprc->left; + + MoveWindows(Cinfo); + + return TRUE; + + case WM_SIZE: + + MoveWindows(Cinfo); + return TRUE; + + case WM_CLOSE: + + + if (Cinfo->Console->SysopChatStream) + { + SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, endChatMsg, strlen(endChatMsg)); + Cinfo->Console->SysopChatStream->BBSFlags &= ~SYSOPCHAT; + SendPrompt(Cinfo->Console->SysopChatStream, Cinfo->Console->SysopChatStream->UserPointer); + SendPrompt(Cinfo->Console, Cinfo->Console->UserPointer); + Cinfo->Console->SysopChatStream = 0; + } + + CloseConsoleSupport(Cinfo); + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &ConsoleRect); // For save soutine + + SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, + (LONG) Cinfo->wpOrigInputProc); + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + if (Cinfo->Console && Cinfo->Console->Active) + { + ClearQueue(Cinfo->Console); + + Cinfo->Console->Active = FALSE; + RefreshMainWindow(); + + { + SendUnbuffered(Cinfo->Console->BPQStream, SignoffMsg, strlen(SignoffMsg)); + if (Cinfo->Console->lastmsg > user->lastmsg) + { + user->lastmsg = Cinfo->Console->lastmsg; + SaveUserDatabase(); + } + } + } + + // Free Scrollback + + for (i = 0; i < MAXSTACK ; i++) + { + if (Cinfo->KbdStack[i]) + { + free(Cinfo->KbdStack[i]); + Cinfo->KbdStack[i] = NULL; + } + } + + Sleep(500); + + free(Cinfo->readbuff); + Cinfo->readbufflen = 0; + + free(Cinfo->Console); + Cinfo->Console = 0; + Cinfo->hConsole = NULL; + + break; + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)hBBSUSERCHAT) + { + // Set up Chat Menu + + CIRCUIT * conn; + char MenuLine[30]; + int n; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + RemoveMenu(hBBSUSERCHAT, BBSUSERCHAT + n, MF_BYCOMMAND); + + if (conn->Active) + { + sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); + AppendMenu(hBBSUSERCHAT, MF_STRING, BBSUSERCHAT + n, MenuLine); + } + } + return TRUE; + } + break; + + + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int i; + unsigned int TextLen; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hwndInput == hwnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + + if (uMsg == WM_KEYUP) + { + unsigned int i; +// Debugprintf("5%x", LOBYTE(HIWORD(lParam))); + + if (LOBYTE(HIWORD(lParam)) == 0x48 && wParam == 0x26) + { + // Scroll up + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput, WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + Cinfo->StackIndex++; + if (Cinfo->StackIndex == 20) + Cinfo->StackIndex = 19; + + return TRUE; + } + + if (LOBYTE(HIWORD(lParam)) == 0x50 && wParam == 0x28) + { + // Scroll up + + Cinfo->StackIndex--; + if (Cinfo->StackIndex < 0) + Cinfo->StackIndex = 0; + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + return TRUE; + } + } + + + if (uMsg == WM_CHAR) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (TextLen > INPUTLEN-10) Beep(220, 150); + + if(Cinfo->WarnWrap || Cinfo->WrapInput) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (Cinfo->WarnWrap) + if (TextLen == Cinfo->WarnLen) Beep(220, 150); + + if (Cinfo->WrapInput) + if ((wParam == 0x20) && (TextLen > Cinfo->WrapLen)) + wParam = 13; // Replace space with Enter + + } + + if (wParam == 13) + { + Cinfo->kbptr=SendMessage(Cinfo->hwndInput, WM_GETTEXT, INPUTLEN-1, + (LPARAM) (LPCSTR)Cinfo->kbbuf); + + Cinfo->StackIndex = 0; + + // Stack it + + if (Cinfo->KbdStack[19]) + free(Cinfo->KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + Cinfo->KbdStack[i+1] = Cinfo->KbdStack[i]; + } + + Cinfo->KbdStack[0] = _strdup(Cinfo->kbbuf); + + Cinfo->kbbuf[Cinfo->kbptr]=13; + + // Echo + + if (Cinfo->BPQStream == -2) + { + char Msg[INPUTLEN+4]; + Msg[0] = 0x1b; + Msg[1] = 11; + memcpy(&Msg[2], Cinfo->kbbuf, Cinfo->kbptr+1); + + WritetoConsoleWindow(Cinfo->BPQStream, Msg, Cinfo->kbptr+3); + + } + else + WritetoConsoleWindow(Cinfo->BPQStream, Cinfo->kbbuf, Cinfo->kbptr+1); + + if (Cinfo->Scrolled) + { + POINT Point; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(Cinfo->hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Scrolled = FALSE; + } + + DoRefresh(Cinfo); + + if (Cinfo->Console->SysopChatStream) + SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + else + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + if (wParam == 0x1a) // Ctrl/Z + { + + Cinfo->kbbuf[0]=0x1a; + Cinfo->kbbuf[1]=13; + + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], 2); + + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + + } + + return CallWindowProc(Cinfo->wpOrigInputProc, hwnd, uMsg, wParam, lParam); +} + + + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, char * Msg, int len); + +int WritetoConsoleWindow(int Stream, char * Msg, int len) +{ + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + WritetoConsoleWindowSupport(Cinfo, Msg, len); + DoRefresh(Cinfo); + return 0; + } + } + } + return 0; +} + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, char * Msg, int len) +{ + char * ptr1, * ptr2; + + if (len + Cinfo->PartLinePtr > Cinfo->readbufflen) + { + Cinfo->readbufflen += len + Cinfo->PartLinePtr; + Cinfo->readbuff = realloc(Cinfo->readbuff, Cinfo->readbufflen); + } + + if (Cinfo->PartLinePtr != 0) + { + Cinfo->CurrentLine--; // Overwrite part line in buffer + if (Cinfo->CurrentLine < 0) + Cinfo->CurrentLine = MAXLINES - 1; + + if (Msg[0] == 0x1b && len > 1) + { + Msg += 2; // Remove Colour Escape + len -= 2; + } + } + + memcpy(&Cinfo->readbuff[Cinfo->PartLinePtr], Msg, len); + + len=len+Cinfo->PartLinePtr; + + ptr1=&Cinfo->readbuff[0]; + Cinfo->readbuff[len]=0; + + if (Cinfo->Bells) + { + do { + + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + + if (Cinfo->FlashOnBell) + FlashWindow(Cinfo->hConsole, TRUE); + else + Beep(440,250); + } + + } while (ptr2); + } + +lineloop: + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + Cinfo->PartLinePtr=len; + memmove(Cinfo->readbuff,ptr1,len); + AddLinetoWindow(Cinfo, ptr1); +// InvalidateRect(Cinfo->hwndOutput, NULL, FALSE); + + return (0); + } + + *(ptr2++)=0; + + // If len is greater that screen with, fold + + if ((ptr2 - ptr1) > Cinfo->maxlinelen) + { + char * ptr3; + char * saveptr1 = ptr1; + int linelen = ptr2 - ptr1; + int foldlen; + char save; + + foldloop: + + ptr3 = ptr1 + Cinfo->maxlinelen; + + while(*ptr3!= 0x20 && ptr3 > ptr1) + { + ptr3--; + } + + foldlen = ptr3 - ptr1 ; + + if (foldlen == 0) + { + // No space before, so split at width + + foldlen = Cinfo->maxlinelen; + ptr3 = ptr1 + Cinfo->maxlinelen; + + } + else + { + ptr3++ ; // Omit space + linelen--; + } + save = ptr1[foldlen]; + ptr1[foldlen] = 0; + AddLinetoWindow(Cinfo, ptr1); + ptr1[foldlen] = save; + linelen -= foldlen; + ptr1 = ptr3; + + if (linelen > Cinfo->maxlinelen) + goto foldloop; + + AddLinetoWindow(Cinfo, ptr1); + + ptr1 = saveptr1; + } + else + AddLinetoWindow(Cinfo, ptr1); + + Cinfo->PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && Cinfo->StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + goto lineloop; + } + + + return (0); +} + +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +void CopyRichTextToClipboard(HWND hWnd) +{ + int len=0; + HGLOBAL hMem; + char * ptr; + + // Copy Rich Text to Clipboard + + len = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); + + hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(MainWnd)) + { + len = SendMessage(hWnd, WM_GETTEXT , len, (LPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); +} + + +void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; iSendHeader) + { + // Return header + + memcpy(lpBuff, RTFHeader, RTFHddrLen); + *pcb = RTFHddrLen; + Cinfo->SendHeader = FALSE; + Cinfo->Finished = FALSE; + Cinfo->Index = 0; + return 0; + } + + if (Cinfo->Finished) + { + *pcb = 0; + return 0; + } + +/* + if (BufferLen > cb) + { + memcpy(lpBuff, &Buffer[Offset], cb); + BufferLen -= cb; + Offset += cb; + *pcb = cb; + return 0; + } + + memcpy(lpBuff, &Buffer[Offset], BufferLen); + + *pcb = BufferLen; +*/ + + // Return 10 line at a time + + for (i = 0; i < 10; i++); + { + Line = Cinfo->Index++ + Cinfo->CurrentLine - MAXLINES; + + if (Line <0) + Line = Line + MAXLINES; + + sprintf(lpBuff, "\\cf%d ", Cinfo->Colourvalue[Line]); + strcat(lpBuff, Cinfo->OutputScreen[Line]); + strcat(lpBuff, "\\line"); + + if (Cinfo->Index == MAXLINES) + { + Cinfo->Finished = TRUE; + strcat(lpBuff, "}"); + i = 10; + } + } + *pcb = strlen(lpBuff); + return 0; +} + +VOID DoRefresh(struct ConsoleInfo * Cinfo) +{ + EDITSTREAM es = {0}; + int Min, Max, Pos; + POINT Point; + SCROLLINFO ScrollInfo; + int LoopTrap = 0; + HWND hwndOutput = Cinfo->hwndOutput; + + if(WINE) + Cinfo->Thumb = 30000; + else + Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + Pos = Cinfo->Thumb + Cinfo->ClientHeight; + + if ((Cinfo->Thumb + Cinfo->ClientHeight) > Cinfo->RTFHeight - 10) // Don't bother writing to screen if scrolled back + { + es.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback; + es.dwCookie = (DWORD_PTR)Cinfo; + Cinfo->SendHeader = TRUE; + SendMessage(hwndOutput, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); + ScrollInfo.cbSize = sizeof(ScrollInfo); + ScrollInfo.fMask = SIF_ALL; + + GetScrollInfo(hwndOutput, SB_VERT, &ScrollInfo); + +// Debugprintf("Pos %d Max %d Min %d nMax %d ClientH %d", Pos, Min, Max, ScrollInfo.nMax, Cinfo->ClientHeight); + + if (Cinfo->FirstTime == FALSE) + { + // RTF Controls don't immediately scroll to end - don't know why. + + Cinfo->FirstTime = TRUE; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + while (LoopTrap++ < 20) + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + Cinfo->RTFHeight = Max; + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + } + + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + + if (Cinfo->Thumb > (Point.y - 10)) // Don't Scroll if user has scrolled back + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + + if (Cinfo->Scrolled) + { + Cinfo->Scrolled = FALSE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } + return; + } + + if (!Cinfo->Scrolled) + { + Cinfo->Scrolled = TRUE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } +} + +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, char * Line) +{ + int Len = strlen(Line); + char * ptr1 = Line; + char * ptr2; + int l, Index; + char LineCopy[LINELEN * 2]; + + if (Len > 199) // Console can't handle long lines + { + Line[198] = 13; + Line[199] = 0; + Len = 199; + } + + if (Line[0] == 0x1b && Len > 1) + { + // Save Colour Char + + Cinfo->CurrentColour = Line[1] - 10; + ptr1 +=2; + Len -= 2; + } + + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], ptr1); + + // Look for chars we need to escape (\ { }) + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '\\'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ++ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy Including found char + Index += l; + LineCopy[Index++] = '\\'; + Len++; + ptr1 = ptr2; + ptr2 = strchr(ptr1, '\\'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '{'); + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '{'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '{'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '}'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '}'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '}'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + + Cinfo->Colourvalue[Cinfo->CurrentLine] = Cinfo->CurrentColour; + Cinfo->LineLen[Cinfo->CurrentLine++] = Len; + if (Cinfo->CurrentLine >= MAXLINES) Cinfo->CurrentLine = 0; +} + + +/* +#define XBITMAP 80 +#define YBITMAP 20 + +#define BUFFER MAX_PATH + +HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork; +HBITMAP hbmpPicture, hbmpOld; + +void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp) +{ + int nItem; + + nItem = SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)lpstr); + SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)nItem, (LPARAM)hbmp); +} + +DWORD APIENTRY DlgDrawProc( + HWND hDlg, // window handle to dialog box + UINT message, // type of message + UINT wParam, // message-specific information + LONG lParam) +{ + int nItem; + TCHAR tchBuffer[BUFFER]; + HBITMAP hbmp; + HWND hListBox; + TEXTMETRIC tm; + int y; + HDC hdcMem; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + RECT rcBitmap; + HRESULT hr; + size_t * pcch; + + switch (message) + { + + case WM_INITDIALOG: + + // Load bitmaps. + + hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700)); + hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701)); + hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702)); + hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703)); + hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704)); + + // Retrieve list box handle. + + hListBox = GetDlgItem(hDlg, IDL_STUFF); + + // Initialize the list box text and associate a bitmap + // with each list box item. + + AddItem(hListBox, "pencil", hbmpPencil); + AddItem(hListBox, "crayon", hbmpCrayon); + AddItem(hListBox, "marker", hbmpMarker); + AddItem(hListBox, "pen", hbmpPen); + AddItem(hListBox, "fork", hbmpFork); + + SetFocus(hListBox); + SendMessage(hListBox, LB_SETCURSEL, 0, 0); + return TRUE; + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 20; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + break; + } + + // Draw the bitmap and text for the list box item. Draw a + // rectangle around the bitmap if it is selected. + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // Display the bitmap associated with the item. + + hbmpPicture =(HBITMAP)SendMessage(lpdis->hwndItem, + LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0); + + hdcMem = CreateCompatibleDC(lpdis->hDC); + hbmpOld = SelectObject(hdcMem, hbmpPicture); + + BitBlt(lpdis->hDC, + lpdis->rcItem.left, lpdis->rcItem.top, + lpdis->rcItem.right - lpdis->rcItem.left, + lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem, 0, 0, SRCCOPY); + + // Display the text associated with the item. + + SendMessage(lpdis->hwndItem, LB_GETTEXT, + lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - + tm.tmHeight) / 2; + + hr = StringCchLength(tchBuffer, BUFFER, pcch); + if (FAILED(hr)) + { + // TODO: Handle error. + } + + TextOut(lpdis->hDC, + XBITMAP + 6, + y, + tchBuffer, + pcch); + + SelectObject(hdcMem, hbmpOld); + DeleteDC(hdcMem); + + // Is the item selected? + + if (lpdis->itemState & ODS_SELECTED) + { + // Set RECT coordinates to surround only the + // bitmap. + + rcBitmap.left = lpdis->rcItem.left; + rcBitmap.top = lpdis->rcItem.top; + rcBitmap.right = lpdis->rcItem.left + XBITMAP; + rcBitmap.bottom = lpdis->rcItem.top + YBITMAP; + + // Draw a rectangle around bitmap to indicate + // the selection. + + DrawFocusRect(lpdis->hDC, &rcBitmap); + } + break; + + case ODA_FOCUS: + + // Do not process focus changes. The focus caret + // (outline rectangle) indicates the selection. + // The IDOK button indicates the final + // selection. + + break; + } + return TRUE; + + case WM_COMMAND: + + switch (LOWORD(wParam)) + { + case IDOK: + // Get the selected item's text. + + nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETCURSEL, 0, (LPARAM) 0); + hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETITEMDATA, nItem, 0); + + // If the item is not the correct answer, tell the + // user to try again. + // + // If the item is the correct answer, congratulate + // the user and destroy the dialog box. + + if (hbmp != hbmpFork) + { + MessageBox(hDlg, "Try again!", "Oops", MB_OK); + return FALSE; + } + else + { + MessageBox(hDlg, "You're right!", + "Congratulations.", MB_OK); + + // Fall through. + } + + case IDCANCEL: + + // Destroy the dialog box. + + EndDialog(hDlg, TRUE); + return TRUE; + + default: + + return FALSE; + } + + case WM_DESTROY: + + // Free any resources used by the bitmaps. + + DeleteObject(hbmpPencil); + DeleteObject(hbmpCrayon); + DeleteObject(hbmpMarker); + DeleteObject(hbmpPen); + DeleteObject(hbmpFork); + + return TRUE; + + default: + return FALSE; + + } + return FALSE; +} +*/ diff --git a/Multicast.c b/Multicast.c new file mode 100644 index 0000000..8283d3f --- /dev/null +++ b/Multicast.c @@ -0,0 +1,1676 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Support for FLAMP compatible Mulitcast + +#include "bpqmail.h" + +void decodeblock( unsigned char in[4], unsigned char out[3]); // Base64 Decode + +time_t MulticastMaxAge = 48 * 60 * 60; // 48 Hours in secs + +struct MSESSION * MSessions = NULL; + +#ifndef LINBPQ + +#include "AFXRES.h" + +HWND hMCMonitor = NULL; +HWND MCList; + +static HMENU hMCMenu; // handle of menu + +static char MCClassName[]="BPQMCWINDOW"; + +RECT MCMonitorRect; + +static int Height, Width, LastY; + +#define BGCOLOUR RGB(236,233,216) + +void MCMoveWindows() +{ + RECT rcClient; + int ClientWidth, ClientHeight; + + GetClientRect(hMCMonitor, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); +} + +void CopyMCToClipboard(HWND hWnd); + +LRESULT CALLBACK MCWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + struct MSESSION * Sess = MSessions; + struct MSESSION * Temp; + + switch (message) + { + + case WM_ACTIVATE: + + break; + + case WM_CLOSE: + if (wParam) // Used by Close All Programs. + return 0; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + + case ID_EDIT_COPY: + + CopyMCToClipboard(hMCMonitor); + return 0;; + + case ID_EDIT_CLEAR: + + while (Sess) + { + ListView_DeleteItem(MCList, Sess->Index); + + if (Sess->FileName) + free(Sess->FileName); + + if (Sess->OrigTimeStamp) + free(Sess->OrigTimeStamp); + + if (Sess->Message) + free(Sess->Message); + + if (Sess->BlockList) + free(Sess->BlockList); + + if (Sess->ID) + free(Sess->ID); + + Temp = Sess; + Sess = Sess->Next; + } + + MSessions = NULL; + return 0; + + default: + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MCMoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMCMonitor = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +static void MoveMCWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMCMonitor, &rcMain); + GetClientRect(hMCMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); +// MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + + + +HWND CreateMCListView (HWND hwndParent) +{ + INITCOMMONCONTROLSEX icex; // Structure for control initialization. + HWND hList; + LV_COLUMN Column; + LOGFONT lf; + HFONT hFont; + int n = 0; + + memset(&lf, 0, sizeof(LOGFONT)); + + lf.lfHeight = 12; + lf.lfWidth = 8; + lf.lfPitchAndFamily = FIXED_PITCH; + strcpy (lf.lfFaceName, "FIXEDSYS"); + + hFont = CreateFontIndirect(&lf); + + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + // Create the list-view window in report view with label editing enabled. + + hList = CreateWindow(WC_LISTVIEW, + "Messages", + WS_CHILD | LVS_REPORT | LVS_EDITLABELS, + 0, 0, 100, 100, + hwndParent, + (HMENU)NULL, + hInst, + NULL); + + SendMessage(hList, WM_SETFONT,(WPARAM) hFont, 0); + + + ListView_SetExtendedListViewStyle(hList,LVS_EX_FULLROWSELECT); + + Column.cx=45; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="ID"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + Column.cx=95; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="From"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=140; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="FileName"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=50; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Size"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=40; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="%"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=55; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Time"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=55; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Age"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=20; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="C"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=430; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="BlockList"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM) &Column); + + ShowWindow(hList, SW_SHOWNORMAL); + UpdateWindow(hList); + + return (hList); +} + +#define OurSetItemText(hwndLV, i, iSubItem_, pszText_) \ +{ LV_ITEM _ms_lvi;\ + _ms_lvi.iSubItem = iSubItem_;\ + _ms_lvi.pszText = pszText_;\ + SNDMSG((hwndLV), LVM_SETITEMTEXT, (WPARAM)i, (LPARAM)(LV_ITEM FAR *)&_ms_lvi);\ +} + +void RefreshMCLine(struct MSESSION * MSession) +{ + LV_ITEM Item; + LVFINDINFO Finfo; + int ret, n, pcent; + char Time[80]; + char Agestring[80]; + char Size[16] = "??"; + char Percent[16] = "??"; + char Key[16]; + + char BlockList[101] = ""; + struct tm * TM; + time_t Age; + + if (MCList == 0) + return; + + sprintf(Key, "%04X", MSession->Key); + + Age = time(NULL) - MSession->LastUpdated; + +// if (LocalTime) +// TM = localtime(&MSession->LastUpdated); +// else + TM = gmtime(&Age); + + sprintf(Agestring, "%.2d:%.2d", + TM->tm_hour, TM->tm_min); + + TM = gmtime(&MSession->Created); + + sprintf(Time, "%.2d:%.2d", + TM->tm_hour, TM->tm_min); + + + Finfo.flags = LVFI_STRING; + Finfo.psz = Key; + Finfo.vkDirection = VK_DOWN; + ret = SendMessage(MCList, LVM_FINDITEM, (WPARAM)-1, (LPARAM) &Finfo); + + if (ret == -1) + { + n = ListView_GetItemCount(MCList); + MSession->Index = n; + } + else + MSession->Index = ret; + + Item.mask=LVIF_TEXT; + Item.iItem = MSession->Index; + Item.iSubItem = 0; + Item.pszText = Key; + + ret = SendMessage(MCList, LVM_SETITEMTEXT, (WPARAM)MSession->Index, (LPARAM) &Item); + + if (ret == 0) + MSession->Index = ListView_InsertItem(MCList, &Item); + + sprintf(Size, "%d", MSession->MessageLen); + + if (MSession->MessageLen) + { + int i; + + pcent = (MSession->BlocksReceived * 100) / MSession->BlockCount; + sprintf(Percent, "%d", pcent); + + // Flag received blocks. Normalise to 50 wide + + memset(BlockList, '.', 50); + + for (i = 0; i < 50; i++) + { + int posn = (i * MSession->BlockCount) / 50; + if (MSession->BlockList[posn] == 1) + BlockList[i] = 'Y'; + } + } + + n = 0; + + OurSetItemText(MCList, MSession->Index, n++, Key); + if (MSession->ID) + OurSetItemText(MCList, MSession->Index, n++, MSession->ID) + else + OurSetItemText(MCList, MSession->Index, n++, " "); + + OurSetItemText(MCList, MSession->Index, n++, MSession->FileName); + OurSetItemText(MCList, MSession->Index, n++, Size); + OurSetItemText(MCList, MSession->Index, n++, Percent); + OurSetItemText(MCList, MSession->Index, n++, Time); + OurSetItemText(MCList, MSession->Index, n++, Agestring); + + if (MSession->Completed) + OurSetItemText(MCList, MSession->Index, n++, "Y") + else + OurSetItemText(MCList, MSession->Index, n++, " "); + + OurSetItemText(MCList, MSession->Index, n++, BlockList); +} + + +BOOL CreateMulticastConsole() +{ + WNDCLASS wc; + HBRUSH bgBrush; + RECT rcClient; + + if (hMCMonitor) + { + ShowWindow(hMCMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMCMonitor); + return FALSE; // Already open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MCWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = MCClassName; + + RegisterClass(&wc); + + hMCMonitor=CreateDialog(hInst, MCClassName, 0, NULL); + + if (!hMCMonitor) + return (FALSE); + + hMCMenu=GetMenu(hMCMonitor); + +// CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); +// CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); +// CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hMCMonitor); + + // Create List View + + GetClientRect (hMCMonitor, &rcClient); + + MCList = CreateMCListView(hMCMonitor); + + MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); + + if (cfgMinToTray) + AddTrayMenuItem(hMCMonitor, "Mail Multicast Monitor"); + + ShowWindow(hMCMonitor, SW_SHOWNORMAL); + + if (MCMonitorRect.right < 100 || MCMonitorRect.bottom < 100) + { + GetWindowRect(hMCMonitor, &MCMonitorRect); + } + + MoveWindow(hMCMonitor, MCMonitorRect.left, MCMonitorRect.top, + MCMonitorRect.right-MCMonitorRect.left, MCMonitorRect.bottom-MCMonitorRect.top, TRUE); + + MoveMCWindows(); + + return TRUE; + +} +void CopyMCToClipboard(HWND hWnd) +{ + int i,n, len=0; + char * Buffer; + HGLOBAL hMem; + char * ptr; + + char Time[80]; + char Agestring[80]; + char From[16]; + char Size[16]; + char Percent[16]; + char FileName[128]; + char Key[16]; + char Complete[2]; + + char BlockList[128]; + + n = ListView_GetItemCount(MCList); + + Buffer = malloc((n + 1) * 200); + + len = sprintf(Buffer, "ID From FileName Size %% Time Age Blocklist\r\n"); + + for (i=0; i> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +static unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} + +struct MSESSION * FindMSession(unsigned int Key) +{ + struct MSESSION * Sess = MSessions; + struct MSESSION * LastSess = NULL; + + while (Sess) + { + if (Sess->Key == Key) + return Sess; + + LastSess = Sess; + Sess = Sess->Next; + } + + // Not found + + Sess = zalloc(sizeof(struct MSESSION)); + + if (Sess == NULL) + return NULL; + + Sess->Key = Key; + + Sess->Created = time(NULL); + + if (LastSess) + LastSess->Next = Sess; + else + MSessions = Sess; + + return Sess; +} + +#include "LzmaLib.h" + +#define LZMA_STR "\1LZMA" + +UCHAR * LZUncompress(UCHAR * Decoded, int Len, int * NewLen) +{ + unsigned char * buf; + unsigned char inprops[LZMA_PROPS_SIZE]; + size_t inlen; + int r; + + UINT rlen; + UINT outlen; + + memcpy(&rlen, &Decoded[5], 4); + + outlen = ntohl(rlen); + *NewLen = outlen; + + buf = malloc(outlen); + + if (outlen > 1 << 25) + { + Debugprintf("Refusing to decompress data (> 32 MiB)"); + return NULL; + } + + + memcpy(inprops, Decoded + strlen(LZMA_STR) + sizeof(int), LZMA_PROPS_SIZE); + + inlen = Len - strlen(LZMA_STR) - sizeof(int) - LZMA_PROPS_SIZE; + + if ((r = LzmaUncompress(buf, &outlen, (const unsigned char*)Decoded + Len - inlen, &inlen, + inprops, LZMA_PROPS_SIZE)) != SZ_OK) + { + Debugprintf("Lzma Uncompress failed: %s", LZMA_ERRORS[r]); + return NULL; + } + else + { + return buf; + } +} + +void decodeblock128(unsigned char in[8], unsigned char out[7] ) +{ + out[0] = (unsigned char) (in[0] << 1 | in[1] >> 6); + out[1] = (unsigned char) (in[1] << 2 | in[2] >> 5); + out[2] = (unsigned char) (in[2] << 3 | in[3] >> 4); + out[3] = (unsigned char) (in[3] << 4 | in[4] >> 3); + out[4] = (unsigned char) in[4] << 5 | in[5] >> 2; + out[5] = (unsigned char) in[5] << 6 | in[6] >> 1; + out[6] = (unsigned char) in[6] << 7 | in[7]; +} + + +void SaveMulticastMessage(struct MSESSION * MSession) +{ + UCHAR * Decoded = NULL; // Output from Basexxx decode + UCHAR * Uncompressed = NULL; + int DecodedLen; // Length of decoded message + int UncompressedLen; // Length of decompressed message + int ExpectedLen; // From front of Base128 or Base256 message + int HddrLen; // Length of Expected Len Header + + if (MSession->FileName == NULL) + return; // Need Name + + MSession->Completed = TRUE; // So we don't get it again + + // If compresses and encoded, decode and decompress + + if (memcmp(MSession->Message, "[b64:start]", 11) == 0) + { + UCHAR * ptr1 = &MSession->Message[11]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + + int Len = MSession->MessageLen - 21; // Header and Trailer + + Decoded = ptr2; + + // Decode Base64 encoding + + while (Len > 0) + { + decodeblock(ptr1, ptr2); + ptr1 += 4; + ptr2 += 3; + Len -= 4; + } + + DecodedLen = (int)(ptr2 - Decoded); + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else if (memcmp(MSession->Message, "[b128:start]", 12) == 0) + { + UCHAR * ptr1 = &MSession->Message[12]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + UCHAR ch; + UCHAR * Intermed; + + int Len = MSession->MessageLen - 23; // Header and Trailer + + Intermed = ptr2; + + // Decode Base128 encoding + + // First remove transparency (as in base256) + + + // Extract decoded msg len + + ExpectedLen = atoi(ptr1); + + ptr1 = strchr(ptr1, 10); + ptr1++; + + HddrLen = (int)(ptr1 - &MSession->Message[12]); + + if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) + { + Debugprintf("MCAST Missing Length Field"); + return; + } + + Len -= HddrLen;; + + while (Len > 0) + { + ch = *(ptr1++); + Len --; + + if (ch == ':') + { + ch = *(ptr1++); + Len--; + + switch (ch) + { + case ':' : *(ptr2++) = ':'; break; + case '0' : *(ptr2++) = 0x00; break; + case '1' : *(ptr2++) = 0x01; break; + case '2' : *(ptr2++) = 0x02; break; + case '3' : *(ptr2++) = 0x03; break; + case '4' : *(ptr2++) = 0x04; break; + case '5' : *(ptr2++) = 0x05; break; + case '6' : *(ptr2++) = 0x06; break; + case '7' : *(ptr2++) = 0x07; break; + case '8' : *(ptr2++) = 0x08; break; + case '9' : *(ptr2++) = 0x09; break; + case 'A' : *(ptr2++) = '\n'; break; + case 'B' : *(ptr2++) = '\r'; break; + case 'C' : *(ptr2++) = '^'; break; + case 'D' : *(ptr2++) = 0x7F; break; + case 'E' : *(ptr2++) = 0xFF; break; + } + } + else + *(ptr2++) = ch; + } + + + Len = ptr2 - Intermed; + + ptr1 = Intermed; + ptr2 = malloc(MSession->MessageLen); // Must get smaller + Decoded = ptr2; + + while (Len > 0) + { + decodeblock128(ptr1, ptr2); + ptr1 += 8; + ptr2 += 7; + Len -= 8; + } + + DecodedLen = ptr2 - Decoded; + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else if (memcmp(MSession->Message, "[b256:start]", 12) == 0) + { + UCHAR * ptr1 = &MSession->Message[12]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + UCHAR ch; + + int Len = MSession->MessageLen - 23; // Header and Trailer + + Decoded = ptr2; + + // Decode Base256 encoding + + // Extract decoded msg len + + ExpectedLen = atoi(ptr1); + + ptr1 = strchr(ptr1, 10); + ptr1++; + + HddrLen = ptr1 - &MSession->Message[12]; + + if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) + { + Debugprintf("MCAST Missing Length Field"); + return; + } + + Len -= HddrLen;; + + while (Len > 0) + { + ch = *(ptr1++); + Len --; + + if (ch == ':') + { + ch = *(ptr1++); + Len--; + + switch (ch) + { + case ':' : *(ptr2++) = ':'; break; + case '0' : *(ptr2++) = 0x00; break; + case '1' : *(ptr2++) = 0x01; break; + case '2' : *(ptr2++) = 0x02; break; + case '3' : *(ptr2++) = 0x03; break; + case '4' : *(ptr2++) = 0x04; break; + case '5' : *(ptr2++) = 0x05; break; + case '6' : *(ptr2++) = 0x06; break; + case '7' : *(ptr2++) = 0x07; break; + case '8' : *(ptr2++) = 0x08; break; + case '9' : *(ptr2++) = 0x09; break; + case 'A' : *(ptr2++) = '\n'; break; + case 'B' : *(ptr2++) = '\r'; break; + case 'C' : *(ptr2++) = '^'; break; + case 'D' : *(ptr2++) = 0x7F; break; + case 'E' : *(ptr2++) = 0xFF; break; + } + } + else + *(ptr2++) = ch; + } + + DecodedLen = ptr2 - Decoded; + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else + { + // Plain Text + + UncompressedLen = MSession->MessageLen; + Uncompressed = MSession->Message; + + MSession->Message = NULL; // So we dont try to free again + } + + if (Decoded) + free(Decoded); + + if (Uncompressed) + { + // Write it away and free it + + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + UCHAR * ptr1 = Uncompressed; + + // Make Sure MCAST directory exists + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST", MailDir); + +#ifdef WIN32 + CreateDirectory(MsgFile, NULL); // Just in case +#else + mkdir(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST/%s", MailDir, MSession->FileName); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = (int)fwrite(Uncompressed, 1, UncompressedLen, hFile); + fclose(hFile); + } + + + // if it looks like an export file (Starts SP SB or ST) and ends /ex + // import and delete it. + + if (*(ptr1) == 'S' && ptr1[2] == ' ') + if (_memicmp(&ptr1[UncompressedLen - 5], "/EX", 3) == 0) + ImportMessages(NULL, MsgFile, TRUE); + + free (Uncompressed); + } +} + +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + char Opcode[80]; + unsigned int checksum, len = 0; + char * data; + int headerlen = 0; + unsigned int crcval; + int n; + unsigned int Key; + struct MSESSION * MSession; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + MsgLen --; // Remove the CR we added + + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + +// return; + + n = sscanf(&Buffer[1], "%s %04d %04X", Opcode, &len, &checksum); + + if (n != 3) + return; + + data = strchr(Buffer, '>'); + + if (data) + headerlen = (int)(++data - Buffer); + + if (headerlen + len != MsgLen) + return; + + crcval = CalcCRC(data, len); + + if (checksum != crcval) + return; + + // Extract Session Key + + sscanf(&data[1], "%04X", &Key); + + MSession = FindMSession(Key); + + if (MSession == 0) + return; // ?? couldn't allocate + + MSession->LastUpdated = time(NULL); + + if (MSession->Completed) + return; // We already have it all + + if (strcmp(Opcode, "ID") == 0) + { + strlop(&data[6], ' '); + MSession->ID = _strdup(&data[6]); + + return; + } + + if (strcmp(Opcode, "PROG") == 0) + { + // Ignore for now + return; + } + + if (strcmp(Opcode, "FILE") == 0) + { + // {80BC}20141108142542:debug_log.txt + + char * FN = strchr(&data[6], ':'); + + if (FN) + { + *(FN++) = 0; + + MSession->FileName = _strdup(FN); + MSession->OrigTimeStamp = _strdup(&data[6]); + } + + // We could get whole message without getting the Name, + // so check + + if (MSession->BlockCount && MSession->BlocksReceived == MSession->BlockCount) + { + // We have the whole message. Decode and Save + + if (MSession->MessageLen) // Also need length + SaveMulticastMessage(MSession); + } + + RefreshMCLine(MSession); + return; + } + + if (strcmp(Opcode, "SIZE") == 0) + { + // SIZE 14 2995>{80BC}465 8 64 + + int a, b, c, n = sscanf(&data[6], "%d %d %d", &a, &b, &c); + + if (n == 3) + { + // We may already have some (or even all) the message if we + // missed the SIZE block first time round + + if (MSession->Message) + { + // Already have at least part of it + + if (MSession->BlockSize != c) + { + // We based blocksize on last packet, so need to sort out mess + + // Find where we put the block, and move it + + UCHAR * OldLoc; + + MSession->Message = realloc(MSession->Message, a); + MSession->BlockList = realloc(MSession->BlockList, b); + + OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; + + memmove(&MSession->Message[(MSession->BlockCount - 1) * c], OldLoc, MSession->BlockSize); + + MSession->BlockSize = c; + } + + if (MSession->BlockCount < b) + { + // Dont have it all, so need to extend ; + + MSession->Message = realloc(MSession->Message, a); + MSession->BlockList = realloc(MSession->BlockList, b); + } + } + + MSession->MessageLen = a; + MSession->BlockCount = b; + MSession->BlockSize = c; + + if (MSession->Message == NULL) + { + MSession->Message = zalloc(b * c); + MSession->BlockList = zalloc(b); + } + + // We might have it all now + + if (MSession->BlocksReceived == MSession->BlockCount) + { + // We have the whole message. Decode and Save + + SaveMulticastMessage(MSession); + } + } + + RefreshMCLine(MSession); + return; + } + + if (strcmp(Opcode, "DATA") == 0) + { + // {80BC:1}[b256:start]401 + + int Blockno = atoi(&data[6]); + char * dataptr = strchr(&data[6], '}'); + + if (dataptr == 0) + return; + + dataptr++; + + // What should we do if we don't have Filename or Size?? + + // If we assume this isn't the last block, then we can get + // the block size from this message. This is pretty save, but + // I guess as we will only get one last block, if we subsequently + // get an earlier one that is bigger, we can recalculate the position + // of this block and move it. + + if (MSession->MessageLen == 0) + { + // Haven't received SIZE Message yet. Guess the blocksize + + int blocksize = MsgLen - (int)(dataptr - Buffer); + + if (MSession->BlockSize == 0) + { + MSession->BlockSize = blocksize; + } + else + { + if (MSession->BlockSize < blocksize) + { + // We based blocksize on last packet, so need to sort out mess + + // Find where we put the block, and move it + + UCHAR * OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; + memmove(&MSession->Message[(MSession->BlockCount - 1) * blocksize], OldLoc, MSession->BlockSize); + + MSession->BlockSize = blocksize; + } + } + + // We need to realloc Message and Blocklist if this is a later block + + if (MSession->BlockCount < Blockno) + { + MSession->Message = realloc(MSession->Message, Blockno * MSession->BlockSize); + MSession->BlockList = realloc(MSession->BlockList, Blockno); + + memset(&MSession->BlockList[MSession->BlockCount], 0, Blockno - MSession->BlockCount); + MSession->BlockCount = Blockno; + + } + + } + if (Blockno == 0 || Blockno > MSession->BlockCount) + return; + + Blockno--; + + if (MSession->BlockList[Blockno] == 1) + { + // Already have this block + + return; + } + + + memcpy(&MSession->Message[Blockno * MSession->BlockSize], dataptr, MSession->BlockSize); + MSession->BlockList[Blockno] = 1; + + MSession->BlocksReceived++; + + if (MSession->BlocksReceived == MSession->BlockCount && MSession->MessageLen) + { + // We have the whole message. Decode and Save + + SaveMulticastMessage(MSession); + } + + RefreshMCLine(MSession); + + return; + } + + MsgLen++; + +/* + +QST DE GM8BPQ + +{80BC}FLAMP 2.2.03 +{80BC}20141108142542:debug_log.txt +{80BC}GM8BPQ Skigersta +{80BC}465 8 64 +{80BC:1}[b256:start]401 +:1LZMA:0:0:6-]:0:0:0:4:0$--:7m?8-v\-E-Y-[--- +{80BC:2}rS-)N{j--o--ZMPX-,-l-yD------E--;-o:6-|;--f---q----0<---%-- +{80BC:3}*N-?N--*:Cf{:9--z-J:9-HMd:8-------Q--D---_-a----:C$;A-j---(: +{80BC:4}DWb--K---Qq-uj-_--;i------T-\>-{:6---~-ij~-,-(-O--2--+ +-:8 +{80BC:5}p---:7Gf:E-5o->x---4--K--:3-\:E---gouuH-3'----:A!.:7 +--N:0S +{80BC:6}.---/-~#.-:D:7zg~--m--:8-'---Y%-?--ze\-ho:5-}-:C:A:1u-1-O- +- +{80BC:7}9p-42-w--G:2G:3--g--O---n----c-#----DF-!~--:D-A--|-e------- +{80BC:8}B{--:0 +[b256:end] +{80BC:EOF} +{80BC:EOT} + +DE GM8BPQ K + +*/ + + return; +/* + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = ptr2 - ptr1; + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = ptr1 - conn->MailBuffer; + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + int Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = &conn->MailBuffer[Msg->length] - ptr2; + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding +*/ +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID MCastConTimer(ConnectionInfo * conn) +{ + conn->MCastListenTime--; + + if (conn->MCastListenTime == 0) + Disconnect(conn->BPQStream); +} + +VOID MCastTimer() +{ + struct MSESSION * Sess = MSessions; + struct MSESSION * Prev = NULL; + + time_t Now = time(NULL); + + while (Sess) + { + if (Sess->Completed == FALSE) + RefreshMCLine(Sess); + + if (Now - Sess->LastUpdated > MulticastMaxAge) + { + // remove from list + +#ifndef LINBPQ + ListView_DeleteItem(MCList, Sess->Index); +#endif + if (Prev) + Prev->Next = Sess->Next; + else + MSessions = Sess->Next; + + if (Sess->FileName) + free(Sess->FileName); + + if (Sess->OrigTimeStamp) + free(Sess->OrigTimeStamp); + + if (Sess->Message) + free(Sess->Message); + + if (Sess->BlockList) + free(Sess->BlockList); + + if (Sess->ID) + free(Sess->ID); + + free(Sess); + + return; // Saves messing with chain + + } + Prev = Sess; + Sess = Sess->Next; + } +} + +int MulticastStatusHTML(char * Reply) +{ + char StatusPage [] = + "
ID    From      FileName        Size  %%  Time   Age   Blocklist"
+		"                                                   "
+		"\r\n

"; + int Len = 0; + char Unknown[] = "???"; + + struct MSESSION * Sess = MSessions; + + if (Sess ==NULL) + return 0; + + Len = sprintf(Reply, StatusPage); + + while (Sess) + { + char Percent[16] = "???"; + char BlockList[51] = ""; + int i; + char Time[80]; + char Agestring[80]; + struct tm * TM; + time_t Age; + char * ID = Unknown; + char * FileName = Unknown; + + Age = time(NULL) - Sess->LastUpdated; + + TM = gmtime(&Age); + + sprintf(Agestring, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + TM = gmtime(&Sess->Created); + + sprintf(Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (Sess->MessageLen && Sess->BlockCount) + { + int pcent; + + pcent = (Sess->BlocksReceived * 100) / Sess->BlockCount; + sprintf(Percent, "%d", pcent); + } + + // Flag received blocks. Normalise to 50 wide + + memset(BlockList, '.', 50); + + if (Sess->BlockList) + { + for (i = 0; i < 50; i++) + { + int posn = (i * Sess->BlockCount) / 50; + if (Sess->BlockList[posn] == 1) + BlockList[i] = 'Y'; + } + } + if (Sess->FileName) + FileName = Sess->FileName; + + if (Sess->ID) + ID = Sess->ID; + + Len += sprintf(&Reply[Len], "%04X %-10s%-15s%5d %-3s %s %s %s\r\n", + Sess->Key, ID, FileName, + Sess->MessageLen, Percent, Time, Agestring, BlockList); + + Sess = Sess->Next; + } + + Len += sprintf(&Reply[Len], StatusTail); + + return Len; +} diff --git a/NNTPRoutines.c b/NNTPRoutines.c new file mode 100644 index 0000000..385f6a7 --- /dev/null +++ b/NNTPRoutines.c @@ -0,0 +1,1208 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +VOID __cdecl Debugprintf(const char * format, ...); +VOID ReleaseSock(SOCKET sock); + +struct NNTPRec * FirstNNTPRec = NULL; + +//int NumberofNNTPRecs=0; + +SOCKET nntpsock; + +extern SocketConn * Sockets; // Chain of active sockets + +int NNTPInPort = 0; + +char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID ReleaseNNTPSock(SOCKET sock); + +struct NNTPRec * LookupNNTP(char * Group) +{ + struct NNTPRec * ptr = FirstNNTPRec; + + while (ptr) + { + if (_stricmp(ptr->NewsGroup, Group) == 0) + return ptr; + + ptr = ptr->Next; + } + + return NULL; +} + + +VOID BuildNNTPList(struct MsgInfo * Msg) +{ + struct NNTPRec * REC; + struct NNTPRec * OLDREC; + struct NNTPRec * PREVREC = 0; + + char FullGroup[100]; + + if (Msg->type != 'B' || Msg->status == 'K' || Msg->status == 'H') + return; + + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via); + + REC = LookupNNTP(FullGroup); + + if (REC == NULL) + { + // New Group. Allocate a record, and put at correct place in chain (alpha order) + + GetSemaphore(&AllocSemaphore, 0); + + REC = zalloc(sizeof (struct NNTPRec)); + OLDREC = FirstNNTPRec; + + if (OLDREC == 0) // First record + { + FirstNNTPRec = REC; + goto DoneIt; + } + else + { + // Follow chain till we find one with a later name + + while(OLDREC) + { + if (strcmp(OLDREC->NewsGroup, FullGroup) > 0) + { + // chain in here + + REC->Next = OLDREC; + if (PREVREC) + PREVREC->Next = REC; + else + FirstNNTPRec = REC; + goto DoneIt; + + } + else + { + PREVREC = OLDREC; + OLDREC = OLDREC->Next; + } + } + + // Run off end - chain to PREVREC + + PREVREC->Next = REC; + } +DoneIt: + strcpy(REC->NewsGroup, FullGroup); + REC->FirstMsg = Msg->number; + REC->DateCreated = (time_t)Msg->datecreated; + + FreeSemaphore(&AllocSemaphore); + } + + REC->LastMsg = Msg->number; + REC->Count++; +} + +void RebuildNNTPList() +{ + struct NNTPRec * NNTPREC = FirstNNTPRec; + struct NNTPRec * SaveNNTPREC; + struct MsgInfo * Msg; + int i; + + // Free old list + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + FirstNNTPRec = NULL; + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + BuildNNTPList(Msg); + } +} + + + +char * GetPathFromHeaders(char * MsgBytes) +{ + char * Path = zalloc(10000); + char * ptr1; + + ptr1 = MsgBytes; + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + char * ptr4 = strchr(ptr1, '\r'); + char * ptr5 = strchr(ptr1, '.'); + ptr1 = strchr(ptr1, '@'); + + if (!ptr1) + return Path; + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + *(ptr5) = 0; + + strcat(Path, "|"); + strcat(Path, ptr1); + + *(ptr5) = '.'; + + ptr1 = ptr4; + + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + return Path; +} + +char * FormatNNTPDateAndTime(time_t Datim) +{ + struct tm *tm; + static char Date[30]; + + // Fri, 19 Nov 82 16:14:55 GMT + // A#asctime gives Wed Jan 02 02:03:55 1980\n\0. + + tm = gmtime(&Datim); + + + + if (tm) + sprintf_s(Date, sizeof(Date), "%s, %02d %3s %02d %02d:%02d:%02d Z", + day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return Date; +} + +VOID InitialiseNNTP() + +{ + if (NNTPInPort) + nntpsock = CreateListeningSocket(NNTPInPort); +} + +int CreateNNTPMessage(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * Via; + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + Msg->length = MsgLen; + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + Msg->type = 'B'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (Date) + Msg->datecreated = Date; + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + + TidyString(To); + Via = strlop(To, '@'); + + if (strlen(To) > 6) To[6]=0; + + strcpy(Msg->to, To); + strcpy(Msg->from, From); + strcpy(Msg->title, MsgTitle); + strcpy(Msg->via, Via); + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + BuildNNTPList(Msg); // Build NNTP Groups list + + return CreateSMTPMessageFile(MsgBody, Msg); + +} + + + +VOID ProcessNNTPServerMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + time_t Date = 0; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + if (sockptr->Flags == GETTINGMESSAGE) + { + if(memcmp(Buffer, ".\r\n", 3) == 0) + { + // File Message + + char * ptr1, * ptr2; + int linelen, MsgLen; + char MsgFrom[62], MsgTo[62], Msgtitle[62]; + + // Scan headers for From: To: and Subject: Line (Headers end at blank line) + + ptr1 = sockptr->MailBuffer; + Loop: + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == NULL) + { + SendSock(sockptr, "500 Eh"); + return; + } + + linelen = (int)(ptr2 - ptr1); + + // From: "John Wiseman" + // To: + // + + + if (_memicmp(ptr1, "From:", 5) == 0) + { + if (linelen > 65) linelen = 65; + memcpy(MsgFrom, ptr1, linelen); + MsgFrom[linelen]=0; + } + else + if (_memicmp(ptr1, "Newsgroups:", 3) == 0) + { + char * sep = strchr(ptr1, '.'); + + if (sep) + *(sep) = '@'; + + if (linelen > 63) linelen = 63; + memcpy(MsgTo, &ptr1[11], linelen-11); + MsgTo[linelen-11]=0; + } + else + if (_memicmp(ptr1, "Subject:", 8) == 0) + { + if (linelen > 68) linelen = 68; + memcpy(Msgtitle, &ptr1[9], linelen-9); + Msgtitle[linelen-9]=0; + } + else + if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char * Context; + char seps[] = " ,\t\r"; + char Offset[10] = ""; + int i, HH, MM; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Tue, 9 Jun 2009 20:54:55 +0100 + + ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day + ptr1 = strtok_s(NULL, seps, &Context); // Day + + rtime.tm_mday = atoi(ptr1); + + ptr1 = strtok_s(NULL, seps, &Context); // Month + + for (i=0; i < 12; i++) + { + if (strcmp(month[i], ptr1) == 0) + { + rtime.tm_mon = i; + break; + } + } + + sscanf(Context, "%04d %02d:%02d:%02d%s", + &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = 0; + else + { + if ((Offset[0] == '+') || (Offset[0] == '-')) + { + MM = atoi(&Offset[3]); + Offset[3] = 0; + HH = atoi(&Offset[1]); + MM = MM + (60 * HH); + + if (Offset[0] == '+') + Date -= (60*MM); + else + Date += (60*MM); + + + } + } + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + ptr1 = sockptr->MailBuffer; + + MsgLen = sockptr->MailSize - (int)(ptr2 - ptr1); + + ptr1 = strchr(MsgFrom, '<'); + + if (ptr1) + { + char * ptr3 = strchr(ptr1, '@'); + ptr1++; + if (ptr3) + *(ptr3) = 0; + } + else + ptr1 = MsgFrom; + + CreateNNTPMessage(_strupr(ptr1), _strupr(MsgTo), Msgtitle, Date, ptr2, MsgLen); + + free(sockptr->MailBuffer); + sockptr->MailBufferSize=0; + sockptr->MailBuffer=0; + sockptr->MailSize = 0; + + sockptr->Flags &= ~GETTINGMESSAGE; + + SendSock(sockptr, "240 OK"); + + return; + } + + if ((sockptr->MailSize + Len) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 10000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sock, 0); + return; + } + } + + memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len); + sockptr->MailSize += Len; + + return; + } + + Buffer[Len-2] = 0; + + if(_memicmp(Buffer, "AUTHINFO USER", 13) == 0) + { + if (Len > 22) Buffer[22]=0; + strcpy(sockptr->CallSign, &Buffer[14]); + sockptr->State = GettingPass; + sockprintf(sockptr, "381 More authentication information required"); + return; + } + + if (sockptr->State == GettingUser) + { + sockprintf(sockptr, "480 Authentication required"); + return; + } + + if (sockptr->State == GettingPass) + { + struct UserInfo * user = NULL; + + if(_memicmp(Buffer, "AUTHINFO PASS", 13) == 0) + { + user = LookupCall(sockptr->CallSign); + + if (user) + { + if (strcmp(user->pass, &Buffer[14]) == 0) + { + sockprintf(sockptr, "281 Authentication accepted"); + + sockptr->State = Authenticated; + sockptr->POP3User = user; + return; + } + } + SendSock(sockptr, "482 Authentication rejected"); + sockptr->State = GettingUser; + return; + } + + sockprintf(sockptr, "480 Authentication required"); + return; + } + + + if(_memicmp(Buffer, "GROUP", 5) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + + while (REC) + { + if (_stricmp(REC->NewsGroup, &Buffer[6]) == 0) + { + sockprintf(sockptr, "211 %d %d %d %s", REC->Count, REC->FirstMsg, REC->LastMsg, REC->NewsGroup); + sockptr->NNTPNum = 0; + sockptr->NNTPGroup = REC; + return; + } + REC =REC->Next; + } + + sockprintf(sockptr, "411 no such news group"); + return; + } + + if(_memicmp(Buffer, "LISTGROUP", 9) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo ; + + // Either currently selected, or a param follows + + if (REC == NULL && Buffer[10] == 0) + { + sockprintf(sockptr, "412 No Group Selected"); + return; + } + + if (Buffer[10] == 0) + goto GotGroup; + + REC = FirstNNTPRec; + + while(REC) + { + if (_stricmp(REC->NewsGroup, &Buffer[10]) == 0) + { + GotGroup: + + sockprintf(sockptr, "211 Article Numbers Follows"); + sockptr->NNTPNum = 0; + sockptr->NNTPGroup = REC; + + for (MsgNo = REC->FirstMsg; MsgNo <= REC->LastMsg; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + sockprintf(sockptr, "%d", MsgNo); + } + } + } + SendSock(sockptr,"."); + return; + } + REC = REC->Next; + } + sockprintf(sockptr, "411 no such news group"); + return; + } + + if(_memicmp(Buffer, "MODE READER", 11) == 0) + { + SendSock(sockptr, "200 Hello"); + return; + } + + if(_memicmp(Buffer, "LIST",4) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + + SendSock(sockptr, "215 list of newsgroups follows"); + + while (REC) + { + sockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); + REC = REC->Next; + } + + SendSock(sockptr,"."); + return; + } + + //NEWGROUPS YYMMDD HHMMSS [GMT] [] + + if(_memicmp(Buffer, "NEWGROUPS", 9) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + struct tm rtime; + char Offset[20] = ""; + time_t Time; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(&Buffer[10], "%02d%02d%02d %02d%02d%02d %s", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, + &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year+=100; + rtime.tm_mon--; + + if (_stricmp(Offset, "GMT") == 0) + Time = mktime(&rtime) - (time_t)_MYTIMEZONE; + else + Time = mktime(&rtime); + + SendSock(sockptr, "231 list of new newsgroups follows"); + + while(REC) + { + if (REC->DateCreated > Time) + sockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); + REC = REC->Next; + } + + SendSock(sockptr,"."); + return; + } + + if(_memicmp(Buffer, "HEAD",4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[5]); + + if (REC == NULL) + { + SendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + SendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + sockprintf(sockptr, "221 %d <%s>", MsgNo, Msg->bid); + + sockprintf(sockptr, "From: %s", Msg->from); + sockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); + sockprintf(sockptr, "Newsgroups: %s.s", Msg->to, Msg->via); + sockprintf(sockptr, "Subject: %s", Msg->title); + sockprintf(sockptr, "Message-ID: <%s>", Msg->bid); + sockprintf(sockptr, "Path: %s", BBSName); + + SendSock(sockptr,"."); + return; + } + + SendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "ARTICLE", 7) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[8]); + char * msgbytes; + char * Path; + + if (REC == NULL) + { + SendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + SendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + sockprintf(sockptr, "220 %d <%s>", MsgNo, Msg->bid); + msgbytes = ReadMessageFile(Msg->number); + + Path = GetPathFromHeaders(msgbytes); + + sockprintf(sockptr, "From: %s", Msg->from); + sockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); + sockprintf(sockptr, "Newsgroups: %s.%s", Msg->to, Msg->via); + sockprintf(sockptr, "Subject: %s", Msg->title); + sockprintf(sockptr, "Message-ID: <%s>", Msg->bid); + sockprintf(sockptr, "Path: %s", &Path[1]); + + SendSock(sockptr,""); + + + SendSock(sockptr,msgbytes); + SendSock(sockptr,""); + + SendSock(sockptr,"."); + + free(msgbytes); + free(Path); + + return; + + } + SendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "BODY", 4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[8]); + char * msgbytes; + char * Path; + + if (REC == NULL) + { + SendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + SendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + sockprintf(sockptr, "222 %d <%s>", MsgNo, Msg->bid); + msgbytes = ReadMessageFile(Msg->number); + + Path = GetPathFromHeaders(msgbytes); + + SendSock(sockptr,msgbytes); + SendSock(sockptr,""); + + SendSock(sockptr,"."); + + free(msgbytes); + free(Path); + + return; + + } + SendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "XHDR",4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgStart, MsgEnd, MsgNo, fields; + char Header[100]; + + if (REC == NULL) + { + SendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + // XHDR subject nnnn-nnnn + + fields = sscanf(&Buffer[5], "%s %d-%d", &Header[0], &MsgStart, &MsgEnd); + + if (fields > 1) + MsgNo = MsgStart; + + if (fields == 2) + MsgEnd = MsgStart; + + if (MsgNo == 0) + { + MsgStart = MsgEnd = sockptr->NNTPNum; + + if (MsgStart == 0) + { + SendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgEnd; + } + + sockprintf(sockptr, "221 "); + + for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + if (_stricmp(Header, "subject") == 0) + sockprintf(sockptr, "%d Subject: %s", MsgNo, Msg->title); + else if (_stricmp(Header, "from") == 0) + sockprintf(sockptr, "%d From: %s", MsgNo, Msg->from); + else if (_stricmp(Header, "date") == 0) + sockprintf(sockptr, "%d Date: %s", MsgNo, FormatNNTPDateAndTime((time_t)Msg->datecreated)); + else if (_stricmp(Header, "message-id") == 0) + sockprintf(sockptr, "%d Message-ID: <%s>", MsgNo, Msg->bid); + else if (_stricmp(Header, "lines") == 0) + sockprintf(sockptr, "%d Lines: %d", MsgNo, Msg->length); + } + } + } + + SendSock(sockptr,"."); + return; + + } + + if(_memicmp(Buffer, "XOVER", 5) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgStart, MsgEnd, MsgNo, fields; + + if (REC == NULL) + { + SendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + fields = sscanf(&Buffer[6], "%d-%d", &MsgStart, &MsgEnd); + + if (fields > 0) + MsgNo = MsgStart; + + if (fields == 1) + MsgEnd = MsgStart; + + if (MsgNo == 0) + { + MsgStart = MsgEnd = sockptr->NNTPNum; + + if (MsgStart == 0) + { + SendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgEnd; + } + + sockprintf(sockptr, "224 "); + + for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + // subject, author, date, message-id, references, byte count, and line count. + sockprintf(sockptr, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d", + MsgNo, Msg->title, Msg->from, FormatNNTPDateAndTime((time_t)Msg->datecreated), Msg->bid, + "", Msg->length, Msg->length); + } + } + } + + SendSock(sockptr,"."); + return; + + } + + + /* + 240 article posted ok + 340 send article to be posted. End with . + 440 posting not allowed + 441 posting failed +*/ + if(_memicmp(Buffer, "POST", 4) == 0) + { + if (sockptr->State != Authenticated) + { + sockprintf(sockptr, "480 Authentication required"); + return; + } + + sockptr->MailBuffer=malloc(10000); + sockptr->MailBufferSize=10000; + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to create POP3 Message Buffer"); + SendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + shutdown(sock, 0); + + return; + } + + sockptr->Flags |= GETTINGMESSAGE; + + SendSock(sockptr, "340 OK"); + return; + } + + + + if(_memicmp(Buffer, "QUIT", 4) == 0) + { + SendSock(sockptr, "205 OK"); + Sleep(500); + shutdown(sock, 0); + return; + } + +/* if(memcmp(Buffer, "RSET\r\n", 6) == 0) + { + SendSock(sockptr, "250 Ok"); + sockptr->State = 0; + sockptr->Recipients; + return; + } +*/ + + SendSock(sockptr, "500 command not recognized"); + + return; +} + + + + +int NNTP_Read(SocketConn * sockptr, SOCKET sock) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (sockptr->InputLen > 1000) // Shouldnt have lines longer than this in text mode + { + sockptr->InputLen=0; + } + + InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); + + if (InputLen <= 0) + { + int x = WSAGetLastError(); + + closesocket(sock); + ReleaseSock(sock); + + return 0; // Does this mean closed? + } + + sockptr->InputLen += InputLen; + +loop: + + ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessNNTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + sockptr->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = sockptr->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + ProcessNNTPServerMessage(sockptr, Buffer, MsgLen); + + memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); + + sockptr->InputLen -= MsgLen; + + goto loop; + + } + } + return 0; +} + + +int NNTP_Accept(SOCKET SocketId) +{ + int addrlen; + SocketConn * sockptr; + u_long param = 1; + + SOCKET sock; + + addrlen=sizeof(struct sockaddr); + + // Allocate a Socket entry + + sockptr=zalloc(sizeof(SocketConn)+100); + + sockptr->Next = Sockets; + Sockets=sockptr; + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + Logprintf(LOG_TCP, NULL, '|', "NNTP accept() failed Error %d", WSAGetLastError()); + + // get rid of socket record + + Sockets = sockptr->Next; + free(sockptr); + + return FALSE; + } + + + sockptr->Type = NNTPServer; + + ioctl(sock, FIONBIO, ¶m); + sockptr->socket = sock; + sockptr->State = 0; + + SendSock(sockptr, "200 BPQMail NNTP Server ready"); + Logprintf(LOG_TCP, NULL, '|', "Incoming NNTP Connect Socket = %d", sock); + + return 0; +} +/* +int NNTP_Data(int sock, int error, int eventcode) +{ + SocketConn * sockptr; + + // Find Connection Record + + sockptr=Sockets; + + while (sockptr) + { + if (sockptr->socket == sock) + { + switch (eventcode) + { + case FD_READ: + + return NNTP_Read(sockptr,sock); + + case FD_WRITE: + + // Either Just connected, or flow contorl cleared + + if (sockptr->SendBuffer) + // Data Queued + + SendFromQueue(sockptr); + else + { + SendSock(sockptr, "200 BPQMail NNTP Server ready"); +// sockptr->State = GettingUser; + } + + return 0; + + case FD_OOB: + + return 0; + + case FD_ACCEPT: + + return 0; + + case FD_CONNECT: + + return 0; + + case FD_CLOSE: + + closesocket(sock); + ReleaseNNTPSock(sock); + return 0; + } + return 0; + } + else + sockptr=sockptr->Next; + } + + return 0; +} +*/ +VOID ReleaseNNTPSock(SOCKET sock) +{ + // remove and free the socket record + + SocketConn * sockptr, * lastptr; + + sockptr=Sockets; + lastptr=NULL; + + while (sockptr) + { + if (sockptr->socket == sock) + { + if (lastptr) + lastptr->Next=sockptr->Next; + else + Sockets=sockptr->Next; + + free(sockptr); + return; + } + else + { + lastptr=sockptr; + sockptr=sockptr->Next; + } + } + + return; +} + +VOID SendFromQueue(SocketConn * sockptr) +{ + int bytestosend = sockptr->SendSize - sockptr->SendPtr; + int bytessent; + + Debugprintf("TCP - Sending %d bytes from buffer", bytestosend); + + bytessent = send(sockptr->socket, &sockptr->SendBuffer[sockptr->SendPtr], bytestosend, 0); + + if (bytessent == bytestosend) + { + // All Sent + + free(sockptr->SendBuffer); + sockptr->SendBuffer = NULL; + } + else + { + sockptr->SendPtr += bytessent; + } + + return; +} diff --git a/NodeMapTest.c b/NodeMapTest.c new file mode 100644 index 0000000..bc71c77 --- /dev/null +++ b/NodeMapTest.c @@ -0,0 +1,2381 @@ + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifdef WIN32 +#include "winsock.h" +#define ioctl ioctlsocket +#else + +#define SOCKET int +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +#define strtok_s strtok_r + +#define _memicmp memicmp +#define _stricmp stricmp +#define _strdup strdup +#define _strupr strupr +#define _strlwr strlwr +#define WSAGetLastError() errno +#define GetLastError() errno + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +char * strupr(char* s) +{ + char* p = s; + + if (s == 0) + return 0; + + while (*p = toupper( *p )) p++; + return s; +} + +char * strlwr(char* s) +{ + char* p = s; + while (*p = tolower( *p )) p++; + return s; +} + +#endif + +struct ChatNodeData +{ + char Call[10]; + char NAlias[10]; + double Lat; + double Lon; + double derivedLat; + double derivedLon; + int PopupMode; + char Comment[512]; + int hasLinks; + int derivedPosn; + time_t LastHeard; +}; + +struct ChatLink +{ + struct ChatNodeData * Call1; + struct ChatNodeData * Call2; + int Call1State; // reported state from each end + int Call2State; + time_t LastHeard1; + time_t LastHeard2; +}; + +struct ModeItem +{ + int Mode; + int Interlock; + double Freq; + time_t LastHeard; +}; + +struct ModeEntries +{ + struct ModeItem * Mode[32]; // One per port +}; + +struct FreqItem +{ + int Interlock; + char * Freqs; // Store as text as that is what the web page will want + time_t LastHeard; +}; + +struct FreqEntries +{ + struct FreqItem * Freq[32]; // One per interlock group +}; + +struct ChatNodeData ** ChatNodes = NULL; + +int NumberOfChatNodes = 0; + +struct ChatLink ** ChatLinks = NULL; + +int NumberOfChatLinks = 0; + +struct NodeData +{ + char Call[10]; + double Lat; + double Lon; + int PopupMode; + char Comment[512]; + time_t LastHeard; + int onlyHeard; // Station heard but not reporting + struct HeardItem * HeardBy; + struct HeardItem * Heard; + struct ModeEntries * Modes; + struct FreqEntries * Freqs; +}; + +struct NodeData ** Nodes = NULL; + +int NumberOfNodes = 0; + + +struct NodeLink +{ + struct NodeData * Call1; + struct NodeData * Call2; + int Type; + time_t LastHeard; +}; + +struct NodeLink ** NodeLinks = NULL; + +int NumberOfNodeLinks = 0; + + +struct HeardItem +{ + struct HeardItem * Next; // Chein of heard calls + struct HeardItem * NextBy; // Chain of heardby calls + time_t Time; + struct NodeData * ReportingCall; + struct NodeData * HeardCall; + char Freq[16]; + char Flags[16]; +}; + +/* +struct HeardData +{ +struct NodeData * CallSign; +struct HeardItem * HeardItems; +}; + +struct HeardByData +{ +struct NodeData * CallSign; +struct HeardItem * HeardItems; +}; +*/ +SOCKET sock, chatsock; + +time_t LastUpdate = 0; + + +int ConvFromAX25(unsigned char * incall, char * outcall); +void GenerateOutputFiles(time_t Now); +void UpdateHeardData(struct NodeData * Node, struct NodeData * Call, char * Freq, char * LOC, char * Flags); +char * strlop(char * buf, char delim); +void ProcessChatUpdate(char * From, char * Msg); +void ProcessNodeUpdate(char * From, char * Msg); + +struct NodeData * FindNode(char * Call) +{ + struct NodeData * Node; + int i; + + // Find, and if not found add + + for (i = 0; i < NumberOfNodes; i++) + { + if (strcmp(Nodes[i]->Call, Call) == 0) + return Nodes[i]; + } + + Node = malloc(sizeof(struct NodeData)); + memset (Node, 0, sizeof(struct NodeData)) + ; + Nodes = realloc(Nodes, (NumberOfNodes + 1) * sizeof(void *)); + Nodes[NumberOfNodes++] = Node; + + strcpy(Node->Call, Call); + strcpy(Node->Comment, Call); + + return Node; +} + +struct NodeData * FindBaseCall(char * Call) +{ + // See if we have any other ssid of the requested call + + char FindCall[12]= ""; + char compareCall[12] = ""; + + int i; + + if (strlen(Call) > 9) + return NULL; + + if (strcmp(Call, "PE1NNZ-8") == 0) + i = 0; + + memcpy(FindCall, Call, 10); + strlop(FindCall, '-'); + + for (i = 0; i < NumberOfNodes; i++) + { + memcpy(compareCall, Nodes[i]->Call, 10); + strlop(compareCall, '-'); + if (strcmp(compareCall, FindCall) == 0 && Nodes[i]->Lat != 0.0) + return Nodes[i]; + } + + return NULL; +} + +struct NodeLink * FindLink(char * Call1, char * Call2, int Type) +{ + struct NodeLink * Link; + struct NodeData * Node; + + int i; + + // Find, and if not found add + + for (i = 0; i < NumberOfNodeLinks; i++) + { + // We only want one copy, whichever end it is reported from + + Link = NodeLinks[i]; + + if (strcmp(Link->Call1->Call, Call1) == 0 && Link->Type == Type && strcmp(Link->Call2->Call, Call2) == 0) + return Link; + + if (strcmp(Link->Call1->Call, Call2) == 0 && Link->Type == Type && strcmp(Link->Call2->Call, Call1) == 0) + return Link; + } + + Link = malloc(sizeof(struct NodeLink)); + memset (Link, 0, sizeof(struct NodeLink)) + ; + NodeLinks = realloc(NodeLinks, (NumberOfNodeLinks + 1) * sizeof(void *)); + NodeLinks[NumberOfNodeLinks++] = Link; + + + Node = FindNode(Call1); + Node->onlyHeard = 0; // Reported + Link->Call1 = Node; + Node = FindNode(Call2); + Node->onlyHeard = 0; // Reported + Link->Call2 = Node; + Link->Type = Type; + + return Link; +} + +struct ChatNodeData * FindChatNode(char * Call) +{ + struct ChatNodeData * Node; + int i; + + if (strcmp("KD8MJL-11", Call) == 0) + { + int xx = 1; + } + + + // Find, and if not found add + + for (i = 0; i < NumberOfChatNodes; i++) + { + if (strcmp(ChatNodes[i]->Call, Call) == 0) + return ChatNodes[i]; + } + + Node = malloc(sizeof(struct ChatNodeData)); + memset (Node, 0, sizeof(struct ChatNodeData)) + ; + ChatNodes = realloc(ChatNodes, (NumberOfChatNodes + 1) * sizeof(void *)); + ChatNodes[NumberOfChatNodes++] = Node; + + strcpy(Node->Call, Call); + strcpy(Node->Comment, Call); + return Node; +} + +struct ChatLink * FindChatLink(char * Call1, char * Call2, int State, time_t Time) +{ + struct ChatLink * Link; + struct ChatNodeData * Node; + + int i; + + // Find, and if not found add + + for (i = 0; i < NumberOfChatLinks; i++) + { + // We only want one copy, whichever end it is reported from + + Link = ChatLinks[i]; + + if (strcmp(Link->Call1->Call, Call1) == 0 && strcmp(Link->Call2->Call, Call2) == 0) + { + Link->Call1State = State; + Link->LastHeard1 = Time; + return Link; + } + if (strcmp(Link->Call1->Call, Call2) == 0 && strcmp(Link->Call2->Call, Call1) == 0) + { + Link->Call2State = State; + Link->LastHeard2 = Time; + return Link; + } + } + + Link = malloc(sizeof(struct ChatLink)); + memset (Link, 0, sizeof(struct ChatLink)); + + ChatLinks = realloc(ChatLinks, (NumberOfChatLinks + 1) * sizeof(void *)); + ChatLinks[NumberOfChatLinks++] = Link; + + Node = FindChatNode(Call1); + Link->Call1 = Node; + Node->hasLinks = 1; + Node = FindChatNode(Call2); + Link->Call2 = Node; + Link->Call1State = State; + Link->Call2State = -1; + Node->hasLinks = 1; + + return Link; +} + +int CompareChatCalls(const struct ChatNodeData ** a, const struct ChatNodeData ** b) +{ + return memcmp(a[0]->Call, b[0]->Call, 7); +} + + +int FromLOC(char * Locator, double * pLat, double * pLon) +{ + double i; + double Lat, Lon; + + _strupr(Locator); + + *pLon = 0; + *pLat = 0; // in case invalid + + + // The first pair (a field) encodes with base 18 and the letters "A" to "R". + // The second pair (square) encodes with base 10 and the digits "0" to "9". + // The third pair (subsquare) encodes with base 24 and the letters "a" to "x". + + i = Locator[0]; + + if (i < 'A' || i > 'R') + return 0; + + Lon = (i - 65) * 20; + + i = Locator[2]; + if (i < '0' || i > '9') + return 0; + + Lon = Lon + (i - 48) * 2; + + i = Locator[4]; + if (i < 'A' || i > 'X') + return 0; + + Lon = Lon + (i - 65) / 12; + + i = Locator[1]; + if (i < 'A' || i > 'R') + return 0; + + Lat = (i - 65) * 10; + + i = Locator[3]; + if (i < '0' || i > '9') + return 0; + + Lat = Lat + (i - 48); + + i = Locator[5]; + if (i < 'A' || i > 'X') + return 0; + + Lat = Lat + (i - 65) / 24; + + if (Lon < 0 || Lon > 360) + Lon = 180; + if (Lat < 0 || Lat > 180) + Lat = 90; + + *pLon = Lon - 180; + *pLat = Lat - 90; + + return 1; +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + return ptr; +} + +void * zalloc(int len) +{ + // malloc and clear + + void * ptr; + + ptr = malloc(len); + + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +FILE * logFile; + +time_t Now; + +int main(int argc, char * argv[]) +{ + char RXBUFFER[512]; + struct NodeData * Node; + struct ChatNodeData * ChatNode; + struct NodeData * HeardNode; + struct NodeData * HeardByNode; + struct NodeLink * Link; + struct ChatLink * ChatLink; + char * Msg = &RXBUFFER[16]; + + struct sockaddr_in rxaddr, txaddr, txaddr2; + int addrlen = sizeof(struct sockaddr_in); + u_long param = 1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + int nLength; + + struct hostent * HostEnt1, * HostEnt2; + char * Context; + FILE * pFile; + char Line[65536]; + + char * pCall; + char * pCall2; + char * pComment; + char * pLat, * pLon; + char * pPopupMode; + char * pLastHeard; + char * pType; + char * pHeardOnly; + char * pHeard; + char * pHeardby; + char * pModes; + char * pFreqs; + char * Next; + + char * pHeardCall; + char * pFreq; + char * pFlags; + char * pHeardTime = "0"; + char * pInterLock; + char * pTime; + char * pMode; + char * pPort; + char * pStatus1; + char * pStatus2; + + double Lat, Lon; + + struct HeardItem * Item; + struct HeardItem * lastItem = NULL; + SOCKET maxsock; + fd_set readfd, writefd, exceptfd; + struct timeval timeout; + int retval; + char * ptr; + time_t restartTime = 0; + +#ifdef WIN32 + WSADATA WsaData; // receives data from WSAStartup + WSAStartup(MAKEWORD(2, 0), &WsaData); +#endif + + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + + logFile = fopen("nodelog.txt","ab"); + + // Read Saved Nodes + + pFile = fopen("savenodes.txt","r"); + + while (pFile) + { + if (fgets(Line, 65535, pFile) == NULL) + { + fclose(pFile); + pFile = NULL; + } + else + { + int j; + + pCall = strtok_s(Line, ",", &Context); + + if (strcmp(pCall, "EI5HBB-10") == 0) + { + int i = 1; + } + pLat = strtok_s(NULL, ",", &Context); + pLon = strtok_s(NULL, ",", &Context); + pPopupMode = strtok_s(NULL, ",", &Context); + pComment = strtok_s(NULL, "|", &Context); + pLastHeard = strtok_s(NULL, "|", &Context); + + // Can't use strtok from here as fields may be missing + + pHeardOnly = Context; + pHeard = strlop(pHeardOnly, '|'); + + if (pLastHeard == NULL) + continue; + + Node = FindNode(pCall); + Lat = atof(pLat); + Lon = atof(pLon); + + if (Lat > 90.0 || Lon > 180.0 || Lat < -90.0 || Lon < -180.0) + { + Lat = 0.0; + Lon = 0.0; + } + + Node->Lat = Lat; + Node->Lon = Lon; + + Node->PopupMode = atoi(pPopupMode); + Node->LastHeard = atoi(pLastHeard); + Node->onlyHeard = atoi(pHeardOnly); + if (strlen(pComment) > 510) + pComment[510] = 0; + + strcpy(Node->Comment, pComment); + + pHeardby = strlop(pHeard, '|'); + + // Look for heard/heard by entries + + // Each heard entry is five comma delimited fields, ending with / + + while (pHeard && strlen(pHeard) > 2) + { + Next = strlop(pHeard, '/'); + + pHeardCall = strlop(pHeard, ','); + pFreq = strlop(pHeardCall, ','); + pFlags = strlop(pFreq, ','); + pHeardTime = strlop(pFlags, ','); + + if (pFlags && strlen(pFlags) > 14) + break; + + HeardNode = FindNode(pHeard); + + if (HeardNode->LastHeard == 0) + HeardNode->onlyHeard = 1; // So far not reported + + // See if we already have it + + Item = Node->Heard; + + while (Item) + { + if (Item->HeardCall->Call == HeardNode->Call && strcmp(Item->Freq, pFreq) == 0 && strcmp(Item->Flags, pFlags) == 0) + break; + + Item = Item->Next; + } + + if (Item == NULL) + { + Item = malloc(sizeof(struct HeardItem)); + + Item->ReportingCall = Node; + Item->HeardCall = HeardNode; + strcpy(Item->Freq, pFreq); + strcpy(Item->Flags, pFlags); + + if (pHeardTime) + Item->Time = atoi(pHeardTime); + + Item->Next = 0; + Item->NextBy = 0; + + if (Node->Heard == NULL) // First + Node->Heard = Item; + else + lastItem->Next = Item; + + lastItem = Item; + } + + pHeard = Next; + } + + // Now do heard by + + pHeard = pHeardby; + pModes = strlop(pHeard, '|'); + + while (pHeard && strlen(pHeard) > 2) + { + Next = strlop(pHeard, '/'); + + pHeardCall = strlop(pHeard, ','); + pFreq = strlop(pHeardCall, ','); + pFlags = strlop(pFreq, ','); + pHeardTime = strlop(pFlags, ','); + + if (pFlags && strlen(pFlags) > 14) + break; + + if (pHeardCall == NULL) + break; + + HeardByNode = FindNode(pHeardCall); + + if (HeardByNode->LastHeard == 0) + HeardByNode->onlyHeard = 1; // So far not reported + + HeardNode = FindNode(pHeard); + + // See if we already have it + + Item = Node->HeardBy; + + while (Item) + { + if (Item->HeardCall->Call == HeardNode->Call && strcmp(Item->Freq, pFreq) == 0 && strcmp(Item->Flags, pFlags) == 0) + break; + + Item = Item->NextBy; + } + + if (Item == NULL) + { + + Item = malloc(sizeof(struct HeardItem)); + + Item->ReportingCall = HeardByNode; + Item->HeardCall = HeardNode; + strcpy(Item->Freq, pFreq); + strcpy(Item->Flags, pFlags); + if (pHeardTime) + Item->Time = atoi(pHeardTime); + + Item->Next = 0; + Item->NextBy = 0; + + if (Node->HeardBy == NULL) // First + Node->HeardBy = Item; + else + lastItem->Next = Item; + + lastItem = Item; + } + pHeard = Next; + } + + pFreqs = strlop(pModes, '|'); + + while (pModes && strlen(pModes) > 2) + { + struct ModeEntries * Modes; + struct ModeItem * Mode; + + Next = strlop(pModes, '/'); + + pPort = pModes; + pMode = strlop(pPort, ','); + pInterLock = strlop(pMode, ','); + pHeardTime = strlop(pInterLock, ','); + pFreq = strlop(pHeardTime, ','); + + if (pHeardTime) + { + int Port = atoi(pPort); + + if (Port < 32) + { + Modes = Node->Modes; + + if (Modes == NULL) + Modes = Node->Modes = (struct ModeEntries *)zalloc(sizeof(struct ModeEntries)); + + Mode = Modes->Mode[Port]; + + if (Mode == NULL) + Mode = Modes->Mode[Port] = (struct ModeItem *)zalloc(sizeof(struct ModeItem)); + + Mode->LastHeard = atoi(pHeardTime); + Mode->Mode = atoi(pMode); + Mode->Interlock = atoi(pInterLock); + + if (pFreq) + Mode->Freq = atof(pFreq); + } + } + + pModes = Next; + } + + strlop(pFreqs, '|'); + + j = 0; + + while (pFreqs && strlen(pFreqs) > 2) + { + struct FreqItem * FreqItem; + + Next = strlop(pFreqs, '&'); + + pInterLock = pFreqs; + pTime = strlop(pInterLock, ','); + pHeardTime = strlop(pTime, ','); + + if (pHeardTime) + { + if (Node->Freqs == NULL) + Node->Freqs = (struct FreqEntries *)zalloc(sizeof(struct FreqEntries)); + + FreqItem = Node->Freqs->Freq[j] = (struct FreqItem *)zalloc(sizeof(struct FreqItem)); + + FreqItem->Interlock = atoi(pInterLock); + FreqItem->LastHeard = atoi(pHeardTime); + FreqItem->Freqs = _strdup(pTime); + j++; + } + pFreqs = Next; + } + + if (Node->Heard == 0 && Node->HeardBy && Node->LastHeard == 0) + Node->onlyHeard = 1; + } + } + + + pFile = fopen("savelinks.txt","r"); + + while (pFile) + { + if (fgets(Line, 499, pFile) == NULL) + { + fclose(pFile); + pFile = NULL; + } + else + { + pCall = strtok_s(Line, ",", &Context); + pCall2 = strtok_s(NULL, ",", &Context); + pType = strtok_s(NULL, ",", &Context); + pLastHeard = strtok_s(NULL, ",", &Context); + + if (pLastHeard == NULL) + continue; + + Link = FindLink(pCall, pCall2, atoi(pType)); + Link->LastHeard = atoi(pLastHeard); + } + } + + // Read Chat Files + + pFile = fopen("savechat.txt","r"); + + while (pFile) + { + if (fgets(Line, 16383, pFile) == NULL) + { + fclose(pFile); + pFile = NULL; + } + else + { + pCall = strtok_s(Line, ",", &Context); + pLat = strtok_s(NULL, ",", &Context); + pLon = strtok_s(NULL, ",", &Context); + pPopupMode = strtok_s(NULL, ",", &Context); + + // Can't use strtok from here as fields may be missing + + pComment = Context; + pLastHeard = strlop(pComment, '|'); + + if (pLastHeard == NULL) + continue; + + ChatNode = FindChatNode(pCall); + + Lat = atof(pLat); + Lon = atof(pLon); + + if (Lat > 90.0 || Lon > 180.0 || Lat < -90.0 || Lon < -180.0) + { + Lat = 0.0; + Lon = 0.0; + } + + ChatNode->Lat = Lat; + ChatNode->Lon = Lon; + ChatNode->derivedLat = Lat; + ChatNode->derivedLon = Lon; + + ChatNode->PopupMode = atoi(pPopupMode); + ChatNode->LastHeard = atoi(pLastHeard); + if (strlen(pComment) > 510) + pComment[510] = 0; + + strcpy(ChatNode->Comment, pComment); + } + } + + pFile = fopen("savechatlinks.txt","r"); + + while (pFile) + { + if (fgets(Line, 16383, pFile) == NULL) + { + fclose(pFile); + pFile = NULL; + } + else + { + pCall = strtok_s(Line, ",", &Context); + pCall2 = strtok_s(NULL, ",", &Context); + pStatus1 = strtok_s(NULL, ",", &Context); + pStatus2 = strtok_s(NULL, ",", &Context); + pHeardTime = strtok_s(NULL, ",", &Context); + + if (pHeardTime == NULL) + continue; + + ChatLink = FindChatLink(pCall, pCall2, atoi(pStatus1), 0); + ChatLink->LastHeard1 = atoi(pHeardTime); + + pHeardTime = strtok_s(NULL, ",", &Context); + + if (pHeardTime) + ChatLink->LastHeard2 = atoi(pHeardTime); + } + } + + LastUpdate = Now = time(NULL); + + sock = socket(AF_INET,SOCK_DGRAM,0); + chatsock = socket(AF_INET,SOCK_DGRAM,0); + + maxsock = sock; + + if (chatsock > sock) + maxsock = chatsock; + + ioctl(sock, FIONBIO, ¶m); + ioctl(chatsock, FIONBIO, ¶m); + + // ioctl(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(81); + + txaddr.sin_family = AF_INET; + txaddr.sin_addr.s_addr = INADDR_ANY; + txaddr.sin_port = htons(81); + + txaddr2.sin_family = AF_INET; + txaddr2.sin_addr.s_addr = INADDR_ANY; + txaddr2.sin_port = htons(81); + + // Resolve name to address + + printf("Resolving %s\n", "guardian.no-ip.org."); + HostEnt1 = gethostbyname ("guardian.no-ip.org."); + + if (HostEnt1) + memcpy(&txaddr.sin_addr.s_addr,HostEnt1->h_addr,4); + else + { + perror("Resolve"); + printf("Resolve %s Failed\r\n", "guardian.no-ip.org."); + restartTime = time(NULL) + 120; // Restart in 2 mins + } + + HostEnt2 = gethostbyname ("10.8.0.38"); + + if (HostEnt2) + memcpy(&txaddr2.sin_addr.s_addr,HostEnt2->h_addr,4); + + if (bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = GetLastError(); + printf("Bind Failed for UDP port %d - error code = %d", 81, err); + } + + sinx.sin_port = htons(10090); + + if (bind(chatsock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = GetLastError(); + printf("Bind Failed for UDP port %d - error code = %d", 10090, err); + } + + printf("Map Update Started\n"); + fflush(stdout); + + while (1) + { + int ret = 0; + + if (time(NULL) > restartTime) + return 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + + FD_SET(sock, &readfd); + FD_SET(chatsock, &readfd); + + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("select"); + } + else + { + if (retval) + { + // see who has data + + if (FD_ISSET(sock, &readfd)) + nLength = recvfrom(sock, RXBUFFER, 512, 0, (struct sockaddr *)&rxaddr, &addrlen); + + else if (FD_ISSET(chatsock, &readfd)) + nLength = recvfrom(chatsock, RXBUFFER, 512, 0, (struct sockaddr *)&rxaddr, &addrlen); + } + else + nLength = 0; + + } + + if (nLength < 0) + { + int err = WSAGetLastError(); + // if (err != 11) + // printf("KISS Error %d %d\n", nLength, err); + nLength = 0; + } + + if (nLength == 0) + continue; + + // ret = sendto(sock, RXBUFFER, nLength, 0, (struct sockaddr *)&txaddr2, sizeof(txaddr)); + + if (ret == -1) + perror("sendto 1"); + + RXBUFFER[nLength - 2] = 0; + + if (memcmp(&RXBUFFER[16], "LINK ", 5) != 0 && memcmp(&RXBUFFER[16], "INFO ", 5) != 0 && memcmp(&RXBUFFER[16], "FREQ ", 5) != 0) + { + ptr = &RXBUFFER[16]; + + while (ptr = strchr(Msg, '|')) + *ptr = '/'; + } + + if (memcmp(&RXBUFFER[16], "LINK ", 5) != 0) + { + if (HostEnt1) + { + ret = sendto(sock, RXBUFFER, nLength, 0, (struct sockaddr *)&txaddr, sizeof(txaddr)); + if (ret == -1) + perror("sendto 1"); + } + } + +#ifdef WIN32 + Sleep(10); +#else + usleep(10000); +#endif + + if (strstr(RXBUFFER, "ctl")) + { + // return; + } + + Now = time(NULL); + + if ((Now - LastUpdate) > 60) + GenerateOutputFiles(Now); + + RXBUFFER[nLength - 2] = 0; + + if (RXBUFFER[14] == 3) // UI + { + char From[10], To[10]; + + From[ConvFromAX25((unsigned char *)&RXBUFFER[7], From)] = 0; + To[ConvFromAX25((unsigned char *)&RXBUFFER[0], To)] = 0; + + if (strcmp(To, "DUMMY") == 0) + { + ProcessChatUpdate(From, Msg); + continue; + } + + if (strcmp(To, "DUMMY-1") == 0) + { + ProcessNodeUpdate(From, Msg); + continue; + } + } + } +} + +#define H_WINMOR 1 +#define H_SCS 2 +#define H_KAM 3 +#define H_AEA 4 +#define H_HAL 5 +#define H_TELNET 6 +#define H_TRK 7 +#define H_TRKM 7 +#define H_V4 8 +#define H_UZ7HO 9 +#define H_MPSK 10 +#define H_FLDIGI 11 +#define H_UIARQ 12 +#define H_ARDOP 13 +#define H_VARA 14 +#define H_SERIAL 15 +#define H_KISSHF 16 +#define H_WINRPR 17 +#define H_HSMODEM 18 + +char ModeNames[20][16] = { + "", "WINMOR", "PACTOR", "PACTOR", "PACTOR", "PACTOR", "TELNET", "ROBUST", "V4", + "PACKET", "MPSK", "FLDIGI", "UIARQ", "ARDOP", "VARA", "SERIAL", "PACKET", "ROBUST", "HSMODEM"}; + + +void ProcessNodeUpdate(char * From, char * Msg) +{ + struct NodeData * Node; + + char * ptr, *Context; + + char * pHeardTime = "0"; + char * Comment, * Version; + + double Lat, Lon; + + struct HeardItem * lastItem = NULL; + + if (strcmp("KO0OOO-8", From) == 0) + { + int i = 1; + } + + Node = FindNode(From); + Node->LastHeard = Now; + Node->onlyHeard = 0; // Reported + + // printf("%s %s\n", From, Msg); + // fprintf(logFile, "%s %s %s\n", From, To, Msg); + + + if (memcmp(Msg, "MH ", 3) == 0) + { + // There are 4 fields - Call, Freq, Loc, Flags + + char * Call, * Freq = 0, * LOC = 0, * Flags = 0; + struct NodeData * heardCall; + + // I think strlop will work - cant use strtok with null fields + + Call = &Msg[3]; + Freq = strlop(Call, ','); + LOC = strlop(Freq, ','); + Flags = strlop(LOC, ','); + + if (strstr(Call, " to ") || strlen(Call) > 10) // Node sending duff report + strlop(Call, ' '); + + if (Flags == NULL) + return; // Corrupt + + heardCall = FindNode(Call); + + if (heardCall->LastHeard == 0) // First time, so set onlyHeard + { + // Maybe also add position if LOC supplied + + Node->onlyHeard = 1; + } + + if (LOC[0] && heardCall->Lat == 0.0 && heardCall->Lon == 0.0) + { + double Lat, Lon; + + if (FromLOC(LOC, &Lat, &Lon)) + { + heardCall->Lat = Lat; + heardCall->Lon = Lon; + } + } + + Node->LastHeard = Now; + + UpdateHeardData(Node, heardCall, Freq, LOC, Flags); + return; + } + + if (memcmp(Msg, "LINK ", 3) == 0) + { + // GM8BPQ-2 DUMMY-1 LINK LU1HVK-4,16,YU4ZED-4,16,AE5E-14,16, + + char * Call; + int Type; + struct NodeLink * Link; + + ptr = strtok_s(Msg, " ", &Context); + + while (Context && Context[0]) + { + Call = strtok_s(NULL, ",", &Context); + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == 0) + break; + + Type = atoi(ptr); + + Link = FindLink(From, Call, Type); + + Link->LastHeard = Now; + } + + return; + } + + if (memcmp(Msg, "MODE ", 5) == 0) + { + char * pPort, * pType, * pInterlock, * pFreq; + int Port; + int Type; + int Interlock; + struct ModeEntries * Modes; + struct ModeItem * Mode; + char * nextBit = strlop(Msg, '/'); + + Msg += 5; + + while (strlen(Msg) > 3) + { + pPort = Msg; + pType = strlop(pPort, ','); + pInterlock = strlop(pType, ','); + pFreq = strlop(pInterlock, ','); + + if (pInterlock == NULL) + return; + + Port = atoi(pPort); + Type = atoi(pType); + Interlock = atoi(pInterlock); + + if (Port == 0) + return; + + if (pFreq) + printf("%s %d %s %d %s\n", From, Port, ModeNames[Type], Interlock, pFreq); + else + printf("%s %d %s %d\n", From, Port, ModeNames[Type], Interlock); + + Port--; // Index from zero + + Modes = Node->Modes; + + if (Modes == NULL) + Modes = Node->Modes = (struct ModeEntries *)zalloc(sizeof(struct ModeEntries)); + + Mode = Modes->Mode[Port]; + + if (Mode == NULL) + Mode = Modes->Mode[Port] = (struct ModeItem *)zalloc(sizeof(struct ModeItem)); + + Mode->LastHeard = Now; + Mode->Mode = Type; + Mode->Interlock = Interlock; + if (pFreq) + Mode->Freq = atof(pFreq); + + Msg = nextBit; + nextBit = strlop(Msg, '/'); + } + return; + } + + if (memcmp(Msg, "FREQ ", 5) == 0) + { + int Interlock = 0; + double Freq; + char TimeBand[16]; + char * nextBit; + char * pInterlock, * pTime, *pFreq; + struct FreqEntries * Freqs; + struct FreqItem * FreqItem; + int n; + char Line[16384]; + int lineLen = 0; + char freqString[256]; + + printf("%s %s\r\n", From, Msg); + + if (strcmp("GM8BPQ-2", From) == 0) + n = 0; + + nextBit = strlop(Msg, '|'); + + Msg += 5; + + while (Msg && Msg[0]) + { + char * nextTime; + + lineLen = 0; + + pInterlock = Msg; + pTime = strlop(pInterlock, '/'); + + Interlock = atoi(pInterlock); + + if (Interlock < 0) + { + // Try to convert to new format report + + int Port = -Interlock; + struct ModeItem * Mode; + char * FreqPtr = strlop(pTime, '/'); + + if (Node->Modes) + { + Mode = Node->Modes->Mode[Port - 1]; + + if (Mode && FreqPtr) + Mode->Freq = atof(FreqPtr) / 1000000.0; + } + + Msg = nextBit; + nextBit = strlop(Msg, '|'); + continue; + } + + if (Node->Freqs == NULL) + Node->Freqs = (struct FreqEntries *)zalloc(sizeof(struct FreqEntries)); + + Freqs = Node->Freqs; + + // Do we have a record for this group? + + for (n = 0; n < 32; n++) + { + FreqItem = Node->Freqs->Freq[n]; + + if (FreqItem == NULL || FreqItem->Interlock == Interlock) + break; + } + + // We have either found it or first free slot + + if (FreqItem == NULL) // Not found + { + FreqItem = Node->Freqs->Freq[n] = (struct FreqItem *)zalloc(sizeof(struct FreqItem)); + FreqItem->Interlock = Interlock; + } + + FreqItem->LastHeard = Now; + + nextTime = strlop(pTime, '\\'); + + while (pTime && pTime[0]) + { + pFreq = strlop(pTime, '/'); + + if (pTime) + { + strcpy(TimeBand, pTime); + + lineLen += sprintf(&Line[lineLen], "%s ", TimeBand); + + while (pFreq && pFreq[0]) + { + Freq = atof(pFreq); + pFreq = strlop(pFreq, ','); + sprintf(freqString, "%.6f", Freq/1000000.0); + + while (freqString[strlen(freqString) - 1] == '0') + freqString[strlen(freqString) - 1] = 0; + + lineLen += sprintf(&Line[lineLen], "%s ", freqString); + } + + lineLen += sprintf(&Line[lineLen], "/ "); + } + + pTime = nextTime; + nextTime = strlop(pTime, '\\'); + } + + if (FreqItem->Freqs) + free (FreqItem->Freqs); + + Line[strlen(Line) -2] = 0; + + FreqItem->Freqs = _strdup(Line); + + Msg = nextBit; + nextBit = strlop(Msg, '|'); + } + + return; + } + + // Node Info Message + + // Location Space Version
Comment. Location and version may contain spaces! + + // There is always
between Version and Comment + + Comment = strstr(Msg, "
"); + + if (Comment == 0) + return; // Corrupt + + *(Comment)= 0; + Comment += 4; + + // We now have Location and Version, both of which may contain spaces + + // Actually, looks like node always reformats lat and lon with colon but no spaces + + ptr = strtok_s(Msg, " ,:", &Context); + + if ((strlen(ptr) == 6 || strlen(ptr) == 10) && FromLOC(ptr, &Lat, &Lon)) // Valid Locator + { + // Rest must be version + + Version = Context; + } + else + { + // See if Space Comma or Colon Separated Lat Lon + + char * ptr2 = strtok_s(NULL, " ,:", &Context); + + if (ptr2 == NULL) + return; // Invalid + + Lat = atof(ptr); + Lon = atof(ptr2); + + if (Lat == 0.0 || Lon == 0.0) + return; // Invalid + + if (Lat > 90.0 || Lon > 180.0 || Lat < -90.0 || Lon < -180.0) + return; // Invalid + + // Rest should be version + + Version = Context; + } + + Node->Lat = Lat; + Node->Lon = Lon; + + // Check Comment for Embedded | which will mess up javascript + + while (ptr = strchr(Comment, '|')) + *ptr = '/'; + + sprintf(Node->Comment, "%s %s
%s", From, Version, Comment); + return; +} + + +int ConvFromAX25(unsigned char * incall, char * outcall) +{ + int in,out=0; + unsigned char chr; + + memset(outcall,0x20,10); + + for (in=0;in<6;in++) + { + chr=incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++]=chr; + } + + chr = incall[6]; // ssid + + if (chr == 0x42) + { + outcall[out++]='-'; + outcall[out++]='T'; + return out; + } + + if (chr == 0x44) + { + outcall[out++]='-'; + outcall[out++]='R'; + return out; + } + + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++]='-'; + if (chr > 9) + { + chr-=10; + outcall[out++]='1'; + } + chr+=48; + outcall[out++]=chr; + } + return (out); + +} +void UpdateHeardData(struct NodeData * HeardBy, struct NodeData * Heard, char * Freq, char * LOC, char * Flags) +{ + // I think the logic should be to keep only the latest record for any + // Call, Reporting Call Pair, Freq, Flags combination + + // I think I'll just store that and postprocess to link to nodes when NodeStatus.txt is generated + + // No, on reflection better to chain heard and heard by records off the Node record + // Is a linked list best? Maybe, as we'll have to follow the list anyway to see if + // we aleady have it. Will also remove old entries from front + + + struct HeardItem * Item = HeardBy->Heard; + struct HeardItem * lastItem = Item; + struct HeardItem * NewItem; + + while (Item) + { + if (Item->HeardCall == Heard && strcmp(Item->Freq, Freq) == 0 && strcmp(Item->Flags, Flags) == 0) + { + // Already have it - just update time + + Item->Time = Now; + return; + } + + lastItem = Item; + Item = Item->Next; + } + + // Not found - add + + NewItem = malloc(sizeof(struct HeardItem)); + memset(NewItem, 0, sizeof(struct HeardItem)); + + NewItem->Time = Now; + NewItem->ReportingCall = HeardBy; + NewItem->HeardCall = Heard; + strcpy(NewItem->Freq, Freq); + strcpy(NewItem->Flags, Flags); + NewItem->Next = 0; + NewItem->NextBy = 0; + + if (HeardBy->Heard == NULL) // First + HeardBy->Heard = NewItem; + else + lastItem->Next = NewItem; + + // Now add to heardby + + // Why do we have two copies? + // Shouldn't there just be one record chained to the hearing node as heard and the heard node as heardby + // Which I think means two chains + + // If we only have one copy, then if we find and update on heard, it will also exist on heardby + + + Item = Heard->HeardBy; + + if (Item == NULL) + { + Heard->HeardBy = NewItem; + return; + } + + // Find end of chain + + while (Item->NextBy) + Item = Item->NextBy; + + Item->NextBy = NewItem; + return; + + + /* + // Not found - add + + // Why do we have two copies? + // Shouldn't there just be one record chained to the hearing node as heard and the heard node as heardby + // Which I think means two chains + + + Item = malloc(sizeof(struct HeardItem)); + + Item->Time = Now; + Item->ReportingCall = HeardBy; + Item->HeardCall = Heard; + strcpy(Item->Freq, Freq); + strcpy(Item->Flags, Flags); + Item->Next = 0; + + if (Heard->HeardBy == NULL) // First + Heard->HeardBy = Item; + else + lastItem->Next = Item; +*/ +} + + +/* +5/9/2021 2:36:18 PM - 618 Active Nodes +|N3HYM-5,-77.5036,39.42477,greenmarker.png,0,N3HYM-5 Ver 6.0.21.24
BBS N3HYM,CHAT N3HYM-1,NODE +|RSNOD,27.57229,61.46665,redmarker.png,0,RSNOD KP31SL Ver 6.0.12.1
APRS on 27.235 ,0, +|Link,GM8BPQ-2,PE1RRR-7,58.476,-6.211,51.62416,4.950115,green +|Link,GM8BPQ-2,G8BPQ-2,58.476,-6.211,52.983812,-1.11646,red, +| +*/ + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +char reportHeader[] = + "

Chat Configuration Report

" + "This lists the status of all known Chat nodes. This includes all nodes sending" + " reports and all nodes which a reporting node is configured to link to. " + "The latter may not exist if there is a configuration error. Stations that have no configured" + " links are listed, though this isn't necessarily an error.

"; + + +void GenerateOutputFiles(time_t Now) +{ + struct tm * TM; + struct NodeData * Node; + struct NodeLink * Link; + int i, j; + FILE * pFile; + int activeNodes = 0; + char HeardString[16384]; + char HeardCalls[16384]; + char ModesString[16384]; + char FreqsString[16384]; + int ModesLen; + int FreqsLen; + double Lat, Lon; + char Colour, State; + struct ChatNodeData * ChatNode; + struct ChatLink * ChatLink; + FILE * errFile; + int noPosition; + int Age; + + int hl = 0; + struct HeardItem * Heard; + char * startofHeardBy; + + LastUpdate = Now; + + fclose(logFile); + logFile = fopen("nodelog.txt","ab"); + + pFile = fopen("nodestatus.txt","wb"); + + if (pFile == NULL) + return; + + TM = gmtime(&Now); + + // Count active nodes + + for (i = 0; i < NumberOfNodes; i++) + { + if ((Now - Nodes[i]->LastHeard) < 3600) // Only count green stations + activeNodes++; + } + + fprintf(pFile, "%04d/%02d/%.2d %02d:%02d:%02d - %d Active Nodes\r\n|", + TM->tm_year + 1900, TM->tm_mon + 1, TM->tm_mday, TM->tm_hour, + TM->tm_min, TM->tm_sec, activeNodes); + + for (i = 0; i < NumberOfNodes; i++) + { + Node = Nodes[i]; + + Age = (int)(Now - Node->LastHeard); + + if (strcmp("KF4YMC-3", Node->Call) == 0) + i = i; + + + // if (Node->onlyHeard) + // continue; // Only heard, not reported + + // We need unheard nodes in the file for the MH list. Display on main map in blue + + if (Age < 3600) + Colour = 'G'; + else if (Age < 86400) + Colour ='R'; + else + { + if (Node->Lat != 0.0 && Node->Lon != 0.0 && Node->LastHeard == 0) + Colour = 'B'; + else + Colour ='0'; + } + + HeardString[0] = 0; + hl = 0; + HeardCalls[0] = 0; + + hl = sprintf(&HeardString[0], "%s
", Node->Call); + + if (Node->Heard || Node->HeardBy) + { + Heard = Node->Heard; + + hl += sprintf(&HeardString[hl], "Heard
"); + + while (Heard) + { + if ((Now - Heard->Time) < 4 * 86400) // Less than 4 days old + { + TM = gmtime(&Heard->Time); + + strcat(HeardCalls, Heard->HeardCall->Call); + strcat(HeardCalls, "%"); + hl += sprintf(&HeardString[hl], "%04d/%02d/%.2d %02d:%02d:%02d %s %s %s
", + TM->tm_year + 1900, TM->tm_mon + 1, TM->tm_mday, TM->tm_hour, + TM->tm_min, TM->tm_sec, Heard->HeardCall, Heard->Freq, Heard->Flags); + } + Heard = Heard->Next; + } + + startofHeardBy = &HeardString[hl]; + + hl += sprintf(&HeardString[hl], "Heard By
"); + + Heard = Node->HeardBy; + + while (Heard) + { + if ((Now - Heard->Time) < 4 * 86400) // Less than 4 days old + { + if (strstr(startofHeardBy, Heard->ReportingCall->Call)) + { + Heard = Heard->NextBy; + continue; // only a call add once + } + strcat(HeardCalls, Heard->ReportingCall->Call); + strcat(HeardCalls, "%"); + hl += sprintf(&HeardString[hl], "%s
", Heard->ReportingCall); + } + Heard = Heard->NextBy; + } + } + + // Build Mode List + + ModesString[0] = 0; + ModesLen = 0; + + if (Node->Modes) + { + struct ModeEntries * Modes = Node->Modes; + + for (j = 0; j < 32; j++) + { + if (Modes->Mode[j]) + { + struct ModeItem * Mode = Modes->Mode[j]; + + if (Now - Mode->LastHeard < 2400) + { + if (Mode->Freq) + { + char freqString[256]; + int freqLen = sprintf(freqString, "%.6f", Mode->Freq); + + while (freqString[--freqLen] == '0') + { + freqString[freqLen] = 0; + } + + ModesLen += sprintf(&ModesString[ModesLen], "%02d:%d:%s/", Mode->Mode, Mode->Interlock, freqString); + } + else + ModesLen += sprintf(&ModesString[ModesLen], "%02d:%d/", Mode->Mode, Mode->Interlock); + } + } + } + } + + // Build Freq List + + FreqsString[0] = 0; + FreqsLen = 0; + + for (j = 0; j < 32; j++) + { + if (Node->Freqs) + { + struct FreqEntries * Freqs = Node->Freqs; + + if (Freqs->Freq[j]) + { + struct FreqItem * Freq = Freqs->Freq[j]; + + FreqsLen += sprintf(&FreqsString[FreqsLen], "%02d!%s\\", Freq->Interlock, &Freq->Freqs[6]); + } + else + break; + } + } + + Lat = Node->Lat; + Lon = Node->Lon; + + if (Lat == 0.0 && Lon == 0.0) + { + struct NodeData * x = FindBaseCall(Node->Call); + + if (x) + { + Lat = x->Lat; + Lon = x->Lon; + } + } + fprintf(pFile, "%s,%f,%f,%c,%d,%s,%d,%s,%s,%s,%s\r\n|", + Node->Call, Lon, Lat, Colour, Node->PopupMode, Node->Comment, (HeardCalls[0] != 0), HeardString, HeardCalls, ModesString, FreqsString); + +// printf("%s,%f,%f,%c,%d,%s,%d,%s,%s,%s,%s\r\n|", +// Node->Call, Lon, Lat, Colour, Node->PopupMode, Node->Comment, (HeardCalls[0] != 0), HeardString, HeardCalls, ModesString, FreqsString); + } + + for (i = 0; i < NumberOfNodeLinks; i++) + { + Link = NodeLinks[i]; + + if ((Now - Link->LastHeard) > 86400) // > One day old - don't show + continue; + + if ((Link->Call1->Lat == 0.0 && Link->Call1->Lon == 0.0) || (Link->Call2->Lat == 0.0 && Link->Call2->Lon == 0.0)) + fprintf(pFile, "Link,%s,%s,%f,%f,%f,%f,red,\r\n|", + Link->Call1->Call, Link->Call2->Call, Link->Call1->Lat, Link->Call1->Lon, Link->Call2->Lat, Link->Call2->Lon); + else if (Link->Type == 16) + fprintf(pFile, "Link,%s,%s,%f,%f,%f,%f,green,\r\n|", + Link->Call1->Call, Link->Call2->Call, Link->Call1->Lat, Link->Call1->Lon, Link->Call2->Lat, Link->Call2->Lon); + else + fprintf(pFile, "Link,%s,%s,%f,%f,%f,%f,blue,\r\n|", + Link->Call1->Call, Link->Call2->Call, Link->Call1->Lat, Link->Call1->Lon, Link->Call2->Lat, Link->Call2->Lon); + } + fclose(pFile); + + // Generate Chat File + + // Sort Calls for error report + + qsort((void *)ChatNodes, NumberOfChatNodes, sizeof(void *), CompareChatCalls); + + pFile = fopen("status.txt","wb"); + + if (pFile == NULL) + return; + + errFile = fopen("ChatErrors.html","wb"); + + if (errFile == NULL) + return; + + fprintf(errFile, reportHeader); + + activeNodes = 0; + + // Count active nodes + + for (i = 0; i < NumberOfChatNodes; i++) + { + if ((Now - ChatNodes[i]->LastHeard) < 3600) // Only current + activeNodes++; + } + + TM = gmtime(&Now); + + fprintf(pFile, "%04d/%02d/%.2d %02d:%02d:%02d - %d Active Nodes\r\n|", + TM->tm_year + 1900, TM->tm_mon + 1, TM->tm_mday, TM->tm_hour, + TM->tm_min, TM->tm_sec, activeNodes); + + for (i = 0; i < NumberOfChatNodes; i++) + { + ChatNode = ChatNodes[i]; + noPosition = 0; + Age = Now - ChatNode->LastHeard; + + if (strcmp(ChatNode->Call, "KG4FZR-1") == 0) + i = i; + + // |GM8BPQ-4,-6.21863670150439,58.4901567374667,GM8BPQ.ok.png,0,BPQ Chat Node, Skigersta, | + + // derivedLat/Lon will be overwritten if a real report is received, so if that + // is set use it + + if (Age > 86400 && ChatNode->LastHeard != 0) + continue; + + Lat = ChatNode->Lat; + Lon = ChatNode->Lon; + + // If we don't have a real lat/lon, try to find one from other ssid's of call. + // If still not found generate randomised position around 0, 0 + + if (Lat == 0.0 && Lon == 0.0) + { + struct NodeData * x = FindBaseCall(ChatNode->Call); + + noPosition = 1; + + if (x) + { + Lat = x->Lat; + Lon = x->Lon; + ChatNode->Lat = Lat; + ChatNode->Lon = Lon; + ChatNode->derivedLat = Lat; + ChatNode->derivedLon = Lon; + ChatNode->derivedPosn = 1; + noPosition = 0; + } + + if (Lat == 0.0 && Lon == 0.0) + { + // Radmomise unknown posn around 0, 0 + + Lat += (rand() * 0.1) / RAND_MAX; + Lon += (rand() * 0.1) / RAND_MAX; + ChatNode->derivedLat = Lat; + ChatNode->derivedLon = Lon; + } + } + + // Try with error file as a status file, so include all calls and + // list after any misconfigured links + + fprintf(errFile, "%s", ChatNode->Call); + + if (noPosition) + fprintf(errFile, " Unknown position"); + + + if (ChatNode->LastHeard == 0) + fprintf(errFile, " Not Reporting"); + + if (ChatNode->hasLinks == 0) + fprintf(errFile, " No Links"); + else + { + // Check through links for any mismatches + + struct ChatLink * Link; + char * Call = ChatNode->Call; + char * OtherCall; + int reported = 0; + + int i; + + for (i = 0; i < NumberOfChatLinks; i++) + { + Link = ChatLinks[i]; + + if (strcmp(Link->Call1->Call, Call) == 0) + OtherCall = Link->Call2->Call; + else if (strcmp(Link->Call2->Call, Call) == 0) + OtherCall = Link->Call1->Call; + else + continue; + + if ((Now - Link->LastHeard1) < 3600 && Link->Call1State != Link->Call2State ) + { + if (reported == 0) + { + fprintf(errFile, " Mismatched Links"); + reported = 1; + } + fprintf(errFile, " %s", OtherCall); + } + } + } + + fprintf(errFile, "
"); + + if (ChatNode->LastHeard == 0) // Don't display if not reporting + continue; + + if (ChatNode->hasLinks == 0) + + + + if (noPosition && ChatNode->hasLinks == 0) + { + continue; // Don't display if no posn and no links + } + + if ((Now - ChatNode->LastHeard) < 86400) + { + if ((Now - ChatNode->LastHeard) < 3600) + fprintf(pFile, "%s,%f,%f,xx.ok.,%d,%s,\r\n|", + ChatNode->Call, Lon, Lat, ChatNode->PopupMode, ChatNode->Comment); + else + fprintf(pFile, "%s,%f,%f,xx.down.,%d,%s,\r\n|", + ChatNode->Call, Lon, Lat, ChatNode->PopupMode, ChatNode->Comment); + } + } + + for (i = 0; i < NumberOfChatLinks; i++) + { + ChatLink = ChatLinks[i]; + + // |Line,-122.1225,47.6891666666667, -93.294332,36.620264,#000000,2 + + if ((Now - ChatLink->LastHeard1) < 3600) + { + if (ChatLink->Call1State == 0 && ChatLink->Call2State == -1) + State = 3; // Down Mismatch + else if (ChatLink->Call1State == -1 && ChatLink->Call2State == 0) + State = 3; + else if (ChatLink->Call1State == 2 && ChatLink->Call2State == 2) + State = 2; // Up + else if (ChatLink->Call1State == 2 || (ChatLink->Call2State == 2 && (Now - ChatLink->LastHeard2) < 3600)) + State = 1; + else + State = 0; + + if (State == 1 || State == 2 || State == 0) + { + fprintf(pFile, "Line,%f,%f,%f,%f,%d,%s,%s\r\n|", + ChatLink->Call1->derivedLon, ChatLink->Call1->derivedLat, + ChatLink->Call2->derivedLon, ChatLink->Call2->derivedLat, + State, ChatLink->Call1->Call, ChatLink->Call2->Call); + } +// if (State == 1 || State == 3) +// fprintf(errFile, "Config Mismatch %s > %s
", ChatLink->Call1->Call, ChatLink->Call2->Call); + + } + } + + fprintf(errFile, "\r\n"); + + fclose(pFile); + fclose(errFile); + + + // Save status for restart + + pFile = fopen("savenodes.txt","wb"); + + if (pFile == NULL) + return; + + for (i = 0; i < NumberOfNodes; i++) + { + Node = Nodes[i]; + + if ((Now - Node->LastHeard) < 30 * 86400) // Drop very old records + { + struct HeardItem * Heard = Node->Heard; + + fprintf(pFile, "%s,%f,%f,%d,%s|%d|%d|", Node->Call, Node->Lat, Node->Lon, Node->PopupMode, Node->Comment, Node->LastHeard, Node->onlyHeard); + + while (Heard) + { + if ((Now - Heard->Time) < 4 * 86400) // Less than 4 days old + fprintf(pFile, "%s,%s,%s,%s,%d/", Heard->HeardCall, Heard->ReportingCall, Heard->Freq, Heard->Flags, Heard->Time); + Heard = Heard->Next; + } + fprintf(pFile, "|"); + + Heard = Node->HeardBy; + + while (Heard) + { + if ((Now - Heard->Time) < 4 * 86400) // Less than 4 days old + fprintf(pFile, "%s,%s,%s,%s,%d/", Heard->HeardCall, Heard->ReportingCall, Heard->Freq, Heard->Flags, Heard->Time); + Heard = Heard->NextBy; + } + + fprintf(pFile, "|"); + + // Do Modes + + for (j = 0; j < 32; j++) + { + if (Node->Modes) + { + struct ModeEntries * Modes = Node->Modes; + + if (Modes->Mode[j]) + { + struct ModeItem * Mode = Modes->Mode[j]; + + if (Now - Mode->LastHeard < 2400) + { + if (Mode->Freq) + fprintf(pFile, "%d,%d,%d,%lld,%f/", j, Mode->Mode, Mode->Interlock, Mode->LastHeard, Mode->Freq); + else + fprintf(pFile, "%d,%d,%d,%d/", j, Mode->Mode, Mode->Interlock, Mode->LastHeard); + } + } + } + } + + fprintf(pFile, "|"); + + // Do Frequencies + + if (Node->Freqs) + { + struct FreqEntries * Freqs = Node->Freqs; + + for (j = 0; j < 32; j++) + { + struct FreqItem * Freq = Freqs->Freq[j]; + + if (Freq) + { + if (Now - Freq->LastHeard < 86400) + fprintf(pFile, "%d,%s,%d&", Freq->Interlock, Freq->Freqs, Freq->LastHeard); + else + break; + } + } + } + + fprintf(pFile, "|\r\n"); + } + } + + fclose(pFile); + + pFile = fopen("savelinks.txt","wb"); + + if (pFile == NULL) + return; + + for (i = 0; i < NumberOfNodeLinks; i++) + { + Link = NodeLinks[i]; + if ((Now - Link->LastHeard) < 86400) // Less than a days old + fprintf(pFile, "%s,%s,%d,%d\r\n", Link->Call1->Call, Link->Call2->Call, Link->Type, Link->LastHeard); + } + fclose(pFile); + + // Save Chat Info + + pFile = fopen("savechat.txt","wb"); + + if (pFile == NULL) + return; + + for (i = 0; i < NumberOfChatNodes; i++) + { + ChatNode = ChatNodes[i]; + + if ((Now - ChatNode->LastHeard) < 30 * 86400) // Drop very old records + fprintf(pFile, "%s,%f,%f,%d,%s|%d|\r\n", + ChatNode->Call, ChatNode->Lat, ChatNode->Lon, ChatNode->PopupMode, ChatNode->Comment, ChatNode->LastHeard); + + } + fclose(pFile); + + pFile = fopen("savechatlinks.txt","wb"); + + if (pFile == NULL) + return; + + for (i = 0; i < NumberOfChatLinks; i++) + { + ChatLink = ChatLinks[i]; + + if ((Now - ChatLink->LastHeard1) < 30 * 86400 || (Now - ChatLink->LastHeard2) < 30 * 86400) // Drop very old records + fprintf(pFile, "%s,%s,%d,%d,%lld,%lld\r\n", + ChatLink->Call1->Call, ChatLink->Call2->Call, ChatLink->Call1State, ChatLink->Call2State, ChatLink->LastHeard1, ChatLink->LastHeard2); + } + fclose(pFile); + fflush(NULL); +} + +void ProcessChatUpdate(char * From, char * Msg) +{ + struct ChatNodeData * Node = FindChatNode(From); + struct ChatNodeData * OtherNode; + struct ChatLink * Link; + + char * p1, * p2, * p3, * p4, * context; + int NewState; + + Node->LastHeard = Now; + + if (memcmp(Msg, "INFO", 4) == 0) + { + int i = 0; + + char * latstring; + char * Popup; + char * PopupMode; + double Lat, Lon; + + + // printf("%s %s\r\n", From, Msg); + + if (memcmp(&Msg[5], "MapPosition=", 12) == 0) + Msg += 12; + + latstring = &Msg[5]; + + Popup = strlop(latstring, '|'); + PopupMode = strlop(Popup, '|'); + + if (strlen(latstring) == 6) + { + // Most Likely a Lccator + + _strupr(latstring); + + if (FromLOC(latstring, &Lat, &Lon)) + { + // printf("**%s %s %f %f\r\n", From, latstring, Lat, Lon); + goto gotPos; + } + } + + // Split latstring into up to 4 components + + p1 = strtok_s(latstring, " ,", &context); + p2 = strtok_s(NULL, " ,", &context); + p3 = strtok_s(NULL, " ,", &context); + p4 = strtok_s(NULL, " ,", &context); + + if (p1 == 0 || p2 == 0) + { + return; + } + + if (p3 == 0 || p3[0] == 0) + { + // Only two - Lat and Lon. May have NS or EW on end, and may be aprs format + + char * dot = strchr(p1, '.'); + + if (dot && (dot - p1) == 4) + { + // APRS Format (probably) + + char NS, EW; + char LatDeg[3], LonDeg[4]; + + NS = p1[7]; + EW = p2[8]; + + // Standard format ddmm.mmN/dddmm.mmE? + + memcpy(LatDeg, p1,2); + LatDeg[2]=0; + Lat = atof(LatDeg) + (atof(p1+2) / 60); + + if (NS == 'S') + Lat = -Lat; + + memcpy(LonDeg,p2, 3); + LonDeg[3]=0; + + Lon = atof(LonDeg) + (atof(p2+3) / 60); + + if (EW == 'W') + Lon = -Lon; + + // printf("APRS %s %f %f\r\n", From, Lat, Lon); + goto gotPos; + } + + Lat = atof(p1); + Lon = atof(p2); + + if (Lat > 90.0 || Lon > 180.0 || Lat < -90.0 || Lon < -180.0) + { + printf("**%s Corrupt %s %s \r\n", From, p1, p2); + return; + } + + // See if NS/EW + + if (strchr(p1, 'S')) + Lat -= Lat; + if (strchr(p2, 'W')) + Lon -= Lon; + + // printf("**%s %f %f\r\n", From, Lat, Lon); + goto gotPos; + } + + if (p3 && p4 && strchr(p2, 'N') || strchr(p2, 'S')) + { + char * min; + + // Spaces between lat and n/s, lon e/s + + // Could be decimal degrees or deg min or seg min s + + if (strchr(p1, '\xb0')) + { + min = strlop(p1, '\xb0'); + + Lat = atof(p1) + atof(min) /60; + + if (strchr(p2, 'S')) + Lat -= Lat; + } + + if (strchr(p3, '\xb0')) + { + min = strlop(p3, '\xb0'); + + Lon = atof(p3) + atof(min) /60; + + if (strchr(p4, 'W')) + Lon -= Lon; + } + } + +gotPos: + + if (Lat > 90.0 || Lat < -90.0) + return; + + Node->derivedLat = Node->Lat = Lat; + Node->derivedLon = Node->Lon = Lon; + Node->derivedPosn = 0; + + strcpy(Node->Comment, Popup); + Node->PopupMode = atoi(PopupMode); + + return; + } + + // Link Info + + // Call / State Pairs + +// printf("%s\r\n", Msg); + + p1 = strtok_s(Msg, " \r", &context); + p2 = strtok_s(NULL, " \r", &context); + + while (p1 && p2) + { + // printf("%s %s\r\n", p1, p2); + + if (strcmp(From, "KG4FZR-1") == 0 || strcmp(p1, "KG4FZR-1") == 0) + printf("%s\r\n", Msg); + + NewState = atoi(p2); + + // Ignore down reports if target doesn't exist + + if (NewState != 4) // 4 is setting up + { + OtherNode = FindChatNode(p1); + Link = FindChatLink(From, p1, NewState, Now); + } + p1 = strtok_s(NULL, " \r", &context); + p2 = strtok_s(NULL, " \r", &context); + + } + return; +} diff --git a/NodeMapTest.vcproj b/NodeMapTest.vcproj new file mode 100644 index 0000000..9f9a2e1 --- /dev/null +++ b/NodeMapTest.vcproj @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NodeMapTest.vcproj.SKIGACER.johnw.user b/NodeMapTest.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..b5b0536 --- /dev/null +++ b/NodeMapTest.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/PortMapper.c b/PortMapper.c new file mode 100644 index 0000000..3edd597 --- /dev/null +++ b/PortMapper.c @@ -0,0 +1,1858 @@ +/* +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 +*/ + + +// Module to provide a basic Gateway between IP over AX.25 and the Internet. + +// Uses WinPcap on Windows, TAP Driver on Linux + +// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet. +// ARP frames are also reformatted, and monitored to build a simple routing table. +// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one), +// so the default route must be configured. + +// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router. +// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router. +// The ax.25 applications will appear as single addresses on the Ethernet LAN + +// The code can also switch packets between ax.25 interfaces + +// First Version, July 2008 + +// Version 1.2.1 January 2009 + +// Add IP Address Mapping option + +// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder +// so packets can be routed from RF to/from encapsulated 44 net subnets. +// Routes may also be learned from received RF packets, or added from config file + +/* +TODo ?Multiple Adapters +*/ + + +// ip tuntap add dev bpqtap mode tap +// ifconfig bpqtap 44.131.4.19 mtu 256 up + + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "CHeaders.h" + +#include "ipcode.h" + +#ifdef WIN32 +#include "pcap.h" +#endif + +#ifndef LINBPQ +#include "kernelresource.h" +LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +//#define s_addr S_un.S_addr + +extern BPQVECSTRUC * IPHOSTVECTORPTR; + +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID SENDSABM(struct _LINKTABLE * LINK); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +BOOL ProcessConfig(); +VOID RemoveARP(PARPDATA Arp); + +VOID ProcessTunnelMsg(PIPMSG IPptr); +VOID ProcessRIP44Message(PIPMSG IPptr); +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found); +BOOL ProcessROUTELine(char * buf, BOOL Locked); +VOID DoRouteTimer(); +PROUTEENTRY FindRoute(uint32_t IPADDR); +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap); +USHORT Generate_CHECKSUM(VOID * ptr1, int Len); + +static VOID MapRouteIPMsg(PIPMSG IPptr); +BOOL Check_Checksum(VOID * ptr1, int Len); + +static BOOL Send_ETH(VOID * Block, DWORD len); + +VOID ProcessEthARPMsg(PETHARP arpptr); +static VOID SendARPMsg(PARPDATA Arp); +VOID SendICMPTimeExceeded(PIPMSG IPptr); + +#define ARPTIMEOUT 3600 + + +// ARP REQUEST/REPLY (Eth) + +static ETHARP ETHARPREQMSG = {0}; + +static ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed + +static int NumberofARPEntries = 0; + +static ROUTEENTRY ** RouteRecords = NULL; + +static int NumberofRoutes = 0; + +//HANDLE hBPQNET = INVALID_HANDLE_VALUE; + +static uint32_t OurIPAddr = 0; + +static uint32_t OurIPBroadcast = 0; +static uint32_t OurNetMask = 0xffffffff; + +static BOOL WantTAP = FALSE; +static BOOL WantEncap = 0; // Run RIP44 and Net44 Encap + +static int IPTTL = 128; + +static int FramesForwarded = 0; +static int FramesDropped = 0; +static int ARPTimeouts = 0; +static int SecTimer = 10; + +static BOOL NeedResolver = FALSE; + +static HMENU hMenu; +extern HMENU hWndMenu; +static HMENU hPopMenu; + +extern HKEY REGTREE; + +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu, hBaseMenu; +extern HWND ClientWnd, FrameWnd; + +static int map_table_len = 0; +//int index=0; // pointer for table search +static int ResolveIndex=-1; // pointer to entry being resolved + +static struct map_table_entry map_table[MAX_ENTRIES]; + +static int Windowlength, WindowParam; + + +static time_t ltime,lasttime; + +static char ConfigClassName[]="CONFIG"; + +HWND hIPResWnd = 0; + +BOOL IPMinimized; + +extern char * PortConfig[]; + +static int baseline=0; + +static unsigned char hostaddr[64]; + + +// Following two fields used by stats to get round shared memmory problem + +static ARPDATA Arp={0}; +static int ARPFlag = -1; + +// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer, +// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25 +// Enet Header ia 14 bytes, AX.25 UI is 16 + +// Also used to reassemble NOS Fragmented ax.25 packets + +static UCHAR Buffer[4096] = {0}; + +#define EthOffset 30 // Should be plenty + +static DWORD IPLen = 0; + + +#ifdef WIN32 +static UCHAR ourMACAddr[6] = {02,'B','P','Q',3,48}; +#else +UCHAR ourMACAddr[6] = {02,'B','P','Q',0,1}; +#endif + +static LONG DefaultIPAddr = 0; + +static IPSTATS IPStats = {0}; + +static UCHAR BPQDirectory[260]; + +static char ARPFN[MAX_PATH]; + +static HANDLE handle; + +#ifdef WIN32 +static pcap_t *adhandle = 0; +static pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +static int pcap_reopen_delay; +#endif + +static char Adapter[256]; + +static int Promiscuous = 1; // Default to Promiscuous + +#ifdef WIN32 + +static HINSTANCE PcapDriver=0; + +typedef int (FAR *FARPROCX)(); + +static int (FAR * pcap_sendpacketx)(); + +static FARPROCX pcap_compilex; +static FARPROCX pcap_setfilterx; +static FARPROCX pcap_datalinkx; +static FARPROCX pcap_next_exx; +static FARPROCX pcap_geterrx; + + +static char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); + +#else + +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#endif +VOID __cdecl Debugprintf(const char * format, ...); + +static HANDLE hInstance; + + + + +void OpenTAP(); + +Dll BOOL APIENTRY Init_PM() +{ + ARPDATA * ARPptr; + + if (hIPResWnd) + { + PostMessage(hIPResWnd, WM_CLOSE,0,0); +// DestroyWindow(hIPResWnd); + + Debugprintf("IP Init Destroying IP Resolver"); + } + + hIPResWnd= NULL; + + ARPRecords = NULL; // ARP Table - malloc'ed as needed + NumberofARPEntries=0; + + RouteRecords = NULL; + NumberofRoutes = 0; + + ReadConfigFile(); + + // Clear old packets + + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP + + ETHARPREQMSG.HWTYPE=0x0100; // Eth + ETHARPREQMSG.PID=0x0008; + ETHARPREQMSG.HWADDRLEN = 6; + ETHARPREQMSG.IPADDRLEN = 4; + +#ifdef WIN32 + + // + // Open PCAP Driver + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + char buf[80]; + + if (OpenPCAP()) + sprintf(buf,"Portmapper Using %s\n", Adapter); + else + sprintf(buf," Portmapper Unable to open %s\n", Adapter); + + WritetoConsoleLocal(buf); + + if (adhandle == NULL) + { + WritetoConsoleLocal("Failed to open pcap device - Portmapper Disabled\n"); + return FALSE; + } + + // Allocate ARP Entry for Default Gateway, and send ARP for it + + if (DefaultIPAddr) + { + ARPptr = AllocARPEntry(); + + if (ARPptr != NULL) + { + ARPptr->ARPINTERFACE = 255; + ARPptr->ARPTYPE = 'E'; + ARPptr->IPADDR = DefaultIPAddr; + ARPptr->LOCKED = TRUE; + + SendARPMsg(ARPptr); + } + } + } + +#else + + // Linux - if TAP requested, open it +#ifndef MACBPQ + + if (WantTAP) + OpenTAP(); + +#endif +#endif + + +#ifndef LINBPQ + + if (NeedResolver) + { + WNDCLASS wc; + int i; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey; + char Size[80]; + RECT Rect = {0,0,0,0}; + + retCode = RegOpenKeyEx (REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"IPResSize",0, + (uint32_t *)&Type,(UCHAR *)&Size,(uint32_t *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &IPMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + RegCloseKey(hKey); + } + + // Fill in window class structure with parameters that describe + // the main window. + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)ResWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "IPAppName"; + + // Register the window classes + + RegisterClass(&wc); + + i=GetLastError(); + + Windowlength=(map_table_len)*14+100; + WindowParam=WS_OVERLAPPEDWINDOW | WS_VSCROLL; + + sprintf(WindowTitle,"PortM Resolver"); + + hIPResWnd = CreateMDIWindow("IPAppName", WindowTitle, WindowParam, + Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, + ClientWnd, hInstance, 1234); + + hPopMenu = CreatePopupMenu(); + AppendMenu(hPopMenu, MF_STRING, BPQREREAD, "ReRead Config"); + + SetScrollRange(hIPResWnd,SB_VERT,0, map_table_len,TRUE); + + if (IPMinimized) + ShowWindow(hIPResWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hIPResWnd, SW_RESTORE); + + _beginthread(IPResolveNames, 0, NULL ); + } +#endif + + WritetoConsoleLocal("Portmapper Enabled\n"); + + return TRUE; + +} + +VOID PMClose() +{ +} + +union +{ + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; +} RXaddr; + + +Dll BOOL APIENTRY Poll_PM() +{ + int res; + struct pcap_pkthdr *header; + const u_char *pkt_data; + + // Entered every 100 mS + + // if ARPFlag set, copy requested ARP record (For BPQStatus GUI) + + if (ARPFlag != -1) + { + memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA)); + ARPFlag = -1; + } + + SecTimer--; + + if (SecTimer == 0) + { + SecTimer = 10; + DoARPTimer(); + DoRouteTimer(); + } + +Pollloop: + +#ifdef WIN32 + + if (adhandle) + { + res = pcap_next_exx(adhandle, &header, &pkt_data); + + if (res > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (header->len > 1514) + { +// Debugprintf("Ether Packet Len = %d", header->len); + goto Pollloop; + } + + memcpy(&Buffer[EthOffset],pkt_data, header->len); + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + // PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14]; + // ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255); + goto Pollloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr); + goto Pollloop; + } + + // Ignore anything else + + goto Pollloop; + } + else + { + if (res < 0) + { + char * error = (char *)pcap_geterrx(adhandle) ; + Debugprintf(error); + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; + } + } + } + else + { + // No handle. + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + // Try reopening periodically + + pcap_reopen_delay --; + + if (pcap_reopen_delay < 0) + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; // Retry every 30 seconds + } + } + +#endif + + + return TRUE; +} + + +static BOOL Send_ETH(VOID * Block, DWORD len) +{ +#ifdef WIN32 + if (adhandle) + { +// if (len < 60) len = 60; + + // Send down the packet + + pcap_sendpacketx(adhandle, // Adapter + Block, // buffer with the packet + len); // size + } +#endif + return TRUE; +} + + +static VOID SendIPtoBPQDEV(PIPMSG IPptr, UCHAR * HWADDR) +{ + // AX.25 headers are bigger, so there will always be room in buffer for enet header + + PETHMSG Ethptr = (PETHMSG)IPptr; + int Len; + + (UCHAR *)Ethptr--; + + Len = ntohs(IPptr->IPLENGTH); + + Len+=14; // Add eth Header + + memcpy(Ethptr->DEST, HWADDR, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + + Send_ETH(Ethptr,Len); + + return; +} + +static VOID ProcessEthIPMsg(PETHMSG Buffer) + +{ + PIPMSG ipptr = (PIPMSG)&Buffer[1]; + + if (memcmp(Buffer, ourMACAddr,6 ) != 0) + return; // Not for us + + if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0) + return; // Discard our sends + + // if Checkum offload is active we get the packet before the NIC sees it (from PCAP) + + if (ipptr->IPCHECKSUM == 0) // Windows seems to do this + { + // Generate IP and TCP/UDP checksums + + int Len = ntohs(ipptr->IPLENGTH); + Len-=20; + + ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20); + + if (ipptr->IPPROTOCOL == 6) // TCP + { + PTCPMSG TCP = (PTCPMSG)&ipptr->Data; + PHEADER PH = {0}; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &ipptr->IPDEST, 4); + + TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len); + } + } + ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255); +} + +static VOID ProcessEthARPMsg(PETHARP arpptr) +{ + int i=0; + PARPDATA Arp; + BOOL Found; + + if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 ) + return; // Discard our sends + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + + // We should only accept requests from our subnet - we might have more than one net on iterface + + if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask)) + return; + + if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0 + return; + + // Add to our table, as we will almost certainly want to send back to it + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp == NULL) return; // No point of table full + + Arp->IPADDR = arpptr->SENDIPADDR; + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPTIMER = ARPTIMEOUT; + + SaveARP(); + +AlreadyThere: + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPVALID = TRUE; + + if (arpptr->TARGETIPADDR == OurIPAddr) + { + uint32_t Save = arpptr->TARGETIPADDR; + + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6); + memcpy(arpptr->SENDHWADDR, ourMACAddr ,6); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = Save; + + memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6); + memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6); + + Send_ETH(arpptr,42); + + return; + + } + + break; + + + case 0x0200: + + if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 ) + return; // Not for us + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + SaveARP(); + +SendBack: + + // Send Back to Originator of ARP Request + + if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + break; + + default: + break; + } + return; +} + +static int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 6; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len); + + // No need to do IP checksum as RouteIPMessage doesit + +// CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + MapRouteIPMsg(IPptr); + return 0; +} + + +static VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port) +{ + uint32_t Dest; + PARPDATA Arp; + BOOL Found; + int index, Len; + PTCPMSG TCPptr; + PUDPMSG UDPptr; + + + if (IPptr->VERLEN != 0x45) return; // Only support Type = 4, Len = 20 + + if (!CheckIPChecksum(IPptr)) return; + + // Make sure origin ia in ARP Table + + Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPptr->IPSOURCE.addr; + + if (Type == 'E') + { + memcpy(Arp->HWADDR, MACADDR, 6); + } + else + { + memcpy(Arp->HWADDR, MACADDR, 7); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = Type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + SaveARP(); + } + } + else + Arp->ARPTIMER = ARPTIMEOUT; // Refresh + + // See if for us - if not pass to router + + Dest = IPptr->IPDEST.addr; + + if (Dest == OurIPAddr || Dest == 0xffffffff || Dest == OurIPBroadcast) + goto ForUs; + + return; + +ForUs: + +// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet +// { +// ProcessTunnelMsg(IPptr); +// return; +// } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + ProcessICMPMsg(IPptr); + return; + } + + // Support UDP for SNMP + + if (IPptr->IPPROTOCOL == 17) // UDP + { + UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // See if for a mapped Address + + if (IPptr->IPPROTOCOL != 6) return; // Only TCP + + TCPptr = (PTCPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + for (index=0; index < map_table_len; index++) + { + if ((map_table[index].sourceport == TCPptr->DESTPORT) && + map_table[index].sourceipaddr == IPptr->IPSOURCE.addr) + { + // Outgoing Message - replace Dest IP address and Port. Source Port remains unchanged + + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPDEST.addr = map_table[index].mappedipaddr; + TCPptr->DESTPORT = map_table[index].mappedport; + CheckSumAndSend(IPptr, TCPptr, Len); + return; + } + + if ((map_table[index].mappedport == TCPptr->SOURCEPORT) && + map_table[index].mappedipaddr == IPptr->IPSOURCE.addr) + { + // Incomming Message - replace Dest IP address and Source Port + + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPDEST.addr = map_table[index].sourceipaddr; + TCPptr->SOURCEPORT = map_table[index].sourceport; + CheckSumAndSend(IPptr, TCPptr, Len); + return; + } + } +} + +static VOID ProcessICMPMsg(PIPMSG IPptr) +{ + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(ICMPptr, Len); + + if (ICMPptr->ICMPTYPE == 8) + { + // ICMP_ECHO + + ICMPptr->ICMPTYPE = 0; // Convert to Reply + + // CHECKSUM IT + + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPTTL = IPTTL; + +// IPptr->IPCHECKSUM = 0; +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); // RouteIPMsg redoes checksum + + MapRouteIPMsg(IPptr); // Send Back + } + + if (ICMPptr->ICMPTYPE == 0) + { + // ICMP_REPLY: + + // I don't see how Portmapper should be getting ping responses + +/* + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + TRANSPORTENTRY * Session = L4TABLE; + char IP[20]; + unsigned char work[4]; + + Session += ICMPptr->ICMPID; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[7]; + + memcpy(work, &IPptr->IPSOURCE, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "Ping Response from %s", IP); + + *ptr1++ = 0x0d; // CR + + Len = ptr1 - BUFFER; + + Msg = (struct _MESSAGE *)BUFFER; + Msg->LENGTH = Len; + Msg->CHAIN = NULL; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(Session); +*/ + return; + } +} + + +static VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2) +{ + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + UCHAR * ptr; + + if (OurIPAddr == 0) + return; // Can't do much without one + + if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11) + return; // Don't send Time Exceeded for TimeExceded + + // Copy the Original IP Header and first 8 bytes of packet down the buffer + + ptr = (UCHAR *) ICMPptr; + + memmove(ptr + 8, IPptr, 28); // IP header plus 8 data + +// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data + + memset (ICMPptr, 0, 8); + ICMPptr->ICMPTYPE = Type; + ICMPptr->ICMPCODE = Code; + ICMPptr->ICMPSEQUENCE = htons(P2); + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36); + + MapRouteIPMsg(IPptr); +} + +static VOID MapRouteIPMsg(PIPMSG IPptr) +{ + PARPDATA Arp; + BOOL Found; + + // We rely on the ARP messages generated by either end to route frames. + // If address is not in ARP cache (say call originated by MSYS), send to our default route + + // Decremnent TTL and Recalculate header checksum + + IPptr->IPTTL--; + + if (IPptr->IPTTL == 0) + { + SendICMPTimeExceeded(IPptr); + return; // Should we send time exceeded???? + } + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Look up ARP + + Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found); + + // If enabled, look in Net44 Encap Routes + + if (!Found && DefaultIPAddr) + Arp = LookupARP(DefaultIPAddr, FALSE, &Found); + + if (!Found) + return; // No route or default + + if (Arp == NULL) + return; // Should we try to ARP it? + + if (Arp->ARPVALID) + { + SendIPtoBPQDEV(IPptr, Arp->HWADDR); + } + + return; +} + +static PROUTEENTRY AllocRouteEntry() +{ + PROUTEENTRY Routeptr; + + if (NumberofRoutes == 0) + + RouteRecords = malloc(sizeof(void *)); + else + RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *)); + + Routeptr = zalloc(sizeof(ROUTEENTRY)); + + if (Routeptr == NULL) return NULL; + + RouteRecords[NumberofRoutes++] = Routeptr; + + return Routeptr; +} + + +static PARPDATA AllocARPEntry() +{ + ARPDATA * ARPptr; + + if (NumberofARPEntries == 0) + + ARPRecords = malloc(sizeof(void *)); + else + ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)*sizeof(void *)); + + ARPptr = malloc(sizeof(ARPDATA)); + + if (ARPptr == NULL) return NULL; + + memset(ARPptr, 0, sizeof(ARPDATA)); + + ARPRecords[NumberofARPEntries++] = ARPptr; + + return ARPptr; +} + + static VOID SendARPMsg(PARPDATA Arp) + { + // Send ARP. Initially used only to find default gateway + + Arp->ARPTIMER = 5; // Retry periodically + + ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + + ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(ETHARPREQMSG.TARGETHWADDR, 0, 6); + + ETHARPREQMSG.SENDIPADDR = OurIPAddr; + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + + Send_ETH(ÐARPREQMSG, 42); + + return; + } + +static PROUTEENTRY FindRoute(uint32_t IPADDR) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if ((IPADDR & Route->SUBNET) == Route->NETWORK) + return Route; + } + return NULL; +} + + + +static PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if (Route->NETWORK == IPADDR && Route->SUBNET == Mask) + { + *Found = TRUE; + return Route; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Route = AllocRouteEntry(); + return Route; + } + else + return NULL; +} + +static PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found) +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (Arp->IPADDR == IPADDR) + { + *Found = TRUE; + return Arp; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Arp = AllocARPEntry(); + return Arp; + } + else + return NULL; +} +static VOID RemoveARP(PARPDATA Arp); + +static VOID RemoveRoute(PROUTEENTRY Route) +{ + int i; + + for (i=0; i < NumberofRoutes; i++) + { + if (Route == RouteRecords[i]) + { + while (i < NumberofRoutes) + { + RouteRecords[i] = RouteRecords[i+1]; + i++; + } + + if (Route->ARP) + { + PARPDATA Arp = Route->ARP; + Route->ARP->ARPROUTE = NULL; // Avoid recursion + RemoveARP(Arp); + } + + free(Route); + NumberofRoutes--; + return; + } + } +} + + +static VOID RemoveARP(PARPDATA Arp) +{ + int i; + + if (Arp->IPADDR == DefaultIPAddr) + { + // Dont remove Default Gateway. Set to re-resolve + + Arp->ARPVALID = FALSE; + Arp->ARPTIMER = 5; + return; + } + + for (i=0; i < NumberofARPEntries; i++) + { + if (Arp == ARPRecords[i]) + { + while (i < NumberofARPEntries) + { + ARPRecords[i] = ARPRecords[i+1]; + i++; + } + + // Remove linked route + + if (Arp->ARPROUTE) + { + PROUTEENTRY Route = Arp->ARPROUTE; + Arp->ARPROUTE->ARP = NULL; // Avoid recursion + RemoveRoute(Route); + } + + free(Arp); + NumberofARPEntries--; + return; + } + } +} + + +static BOOL ReadConfigFile() +{ + +// IPAddr 192.168.0.129 +// IPBroadcast 192.168.0.255 +// IPGateway 192.168.0.1 +// IPPorts 1,4 + +// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000 + + char * Config; + char * ptr1, * ptr2; + + char buf[256],errbuf[256]; + + map_table_len = 0; // For reread + + Config = PortConfig[35]; // Config fnom bpq32.cfg + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf)) + { + WritetoConsoleLocal("PortMapper bad config record "); + strcat(errbuf, "\n"); + WritetoConsoleLocal(errbuf); + } + } + } + return (TRUE); +} + + +static int ProcessLine(char * buf) +{ + char * ptr, * p_value, * p_origport, * p_host, * p_port; + int port, mappedport, ipad; + + ptr = strtok(buf, " \t\n\r"); + p_value = strtok(NULL, " \t\n\r"); + + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"ADAPTER") == 0) + { +#ifndef WIN32 + WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n"); + return TRUE; +#endif + strcpy(Adapter,p_value); + return (TRUE); + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + Promiscuous = atoi(p_value); + return (TRUE); + } + + if (_stricmp(ptr,"IPAddr") == 0) + { + OurIPAddr = inet_addr(p_value); + + if (OurIPAddr == INADDR_NONE) return (FALSE); + + return (TRUE); + } + if (_stricmp(ptr,"IPBroadcast") == 0) + { + OurIPBroadcast = inet_addr(p_value); + + if (OurIPBroadcast == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + if (_stricmp(ptr,"IPNetMask") == 0) + { + OurNetMask = inet_addr(p_value); + + if (OurNetMask == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + + if (_stricmp(ptr,"IPGateway") == 0) + { + DefaultIPAddr = inet_addr(p_value); + + if (DefaultIPAddr == INADDR_NONE) return (FALSE); + + return (TRUE); + } + +// ARP 44.131.4.18 GM8BPQ-7 1 D + + if (_stricmp(ptr,"MAP") == 0) + { +#ifdef LINBPQ + + WritetoConsoleLocal("MAP not supported in LinBPQ IP Gateway\n"); + return TRUE; +#endif + if (!p_value) return FALSE; + + p_origport = strtok(NULL, " ,\t\n\r"); + if (!p_origport) return FALSE; + + p_host = strtok(NULL, " ,\t\n\r"); + if (!p_host) return FALSE; + + p_port = strtok(NULL, " ,\t\n\r"); + if (!p_port) return FALSE; + + port=atoi(p_origport); + if (port == 0) return FALSE; + + mappedport=atoi(p_port); + if (mappedport == 0) return FALSE; + + ipad = inet_addr(p_value); + + map_table[map_table_len].sourceipaddr = ipad; + strcpy(map_table[map_table_len].hostname, p_host); + map_table[map_table_len].sourceport = ntohs(port); + map_table[map_table_len++].mappedport = ntohs(mappedport); + + NeedResolver = TRUE; + + return (TRUE); + } + + // + // Bad line + // + return (FALSE); + +} + +static VOID DoARPTimer() +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (!Arp->ARPVALID) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Retry Request + + SendARPMsg(Arp); + } + continue; + } + + // Time out active entries + + if (Arp->LOCKED == 0) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Remove Entry + + RemoveARP(Arp); + SaveARP(); + } + } + } +} + +static VOID DoRouteTimer() +{ + int i; + PROUTEENTRY Route; + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (Route->RIPTIMOUT) + Route->RIPTIMOUT--; + } +} + + +// PCAP Support Code + + +#ifdef WIN32 + +static FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d", Proc,err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + + +static void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +static int OpenPCAP() +{ + u_long param=1; + BOOL bcopt=TRUE; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[64]; + struct bpf_program fcode; + char buf[256]; + int n; + + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) return(FALSE); + + if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live"); + + if (pcap_open_livex == NULL) return FALSE; + + if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; + + /* Open the adapter */ + + adhandle = pcap_open_livex(Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + ); + + if (adhandle == NULL) + return FALSE; + + /* Check the link layer. We support only Ethernet for simplicity. */ + + if(pcap_datalinkx(adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"\nThis program works only on Ethernet networks.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + netmask=0xffffff; + +// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806"); + + sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x", + ourMACAddr[0], ourMACAddr[1], ourMACAddr[2], + ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]); + + //compile the filter + + if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(adhandle, &fcode)<0) + { + n=sprintf(buf,"\nError setting the filter.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + return TRUE; +} +#endif + + +int CompareMasks (const VOID * a, const VOID * b); + + +#ifndef LINBPQ + +extern HFONT hFont; +struct tagMSG Msg; +char buf[1024]; + +static LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + struct hostent * hostptr; + struct in_addr ipad; + char line[100]; + int index,displayline; + MINMAXINFO * mmi; + + int i=1; + + int nScrollCode,nPos; + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 400; + mmi->ptMaxSize.y = Windowlength; + mmi->ptMaxTrackSize.x = 400; + mmi->ptMaxTrackSize.y = Windowlength; + break; + + case WM_USER+199: + + i=WSAGETASYNCERROR(lParam); + + map_table[ResolveIndex].error=i; + + if (i ==0) + { + // resolved ok + + hostptr=(struct hostent *)&buf; + memcpy(&map_table[ResolveIndex].mappedipaddr,hostptr->h_addr,4); + } + + InvalidateRect(hWnd,NULL,FALSE); + + while (ResolveIndex < map_table_len) + { + ResolveIndex++; + + WSAAsyncGetHostByName (hWnd,WM_USER+199, + map_table[ResolveIndex].hostname, + buf,MAXGETHOSTSTRUCT); + + break; + } + break; + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (WPARAM)hPopMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hMainFrameMenu, (LPARAM)NULL); + + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + + + if (wmId == BPQREREAD) + { + if (ProcessConfig()) + { + FreeConfig(); + + ReadConfigFile(); + PostMessage(hIPResWnd, WM_TIMER,0,0); + InvalidateRect(hWnd,NULL,TRUE); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + return 0; + } +/* + if (wmId == BPQADDARP) + { + if (ConfigWnd == 0) + { + ConfigWnd=CreateDialog(hInstance,ConfigClassName,0,NULL); + + if (!ConfigWnd) + { + i=GetLastError(); + return (FALSE); + } + ShowWindow(ConfigWnd, SW_SHOW); + UpdateWindow(ConfigWnd); + } + + SetForegroundWindow(ConfigWnd); + + return(0); + } + return 0; +*/ + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + IPMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + + case SC_MINIMIZE: + + IPMinimized = TRUE; + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + baseline--; + if (baseline <0) + baseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + baseline++; + if (baseline > map_table_len) + baseline = map_table_len; + } + + if (nScrollCode == SB_THUMBTRACK) + { + baseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,baseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + index = baseline; + displayline=0; + + while (index < map_table_len) + { + if (map_table[index].ResolveFlag && map_table[index].error != 0) + { + // resolver error - Display Error Code + sprintf(hostaddr,"Error %d",map_table[index].error); + } + else + { + memcpy(&ipad,&map_table[index].mappedipaddr,4); + strncpy(hostaddr,inet_ntoa(ipad),16); + } + + memcpy(&ipad,&map_table[index].mappedipaddr,4); + + i=sprintf(line,"%.64s = %-.30s", + map_table[index].hostname, + hostaddr); + + TextOut(hdc,0,(displayline++)*14+2,line,i); + + index++; + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + + +// PostQuitMessage(0); + + break; + + + case WM_TIMER: + + for (ResolveIndex=0; ResolveIndex < map_table_len; ResolveIndex++) + { + WSAAsyncGetHostByName (hWnd,WM_USER+199, + map_table[ResolveIndex].hostname, + buf,MAXGETHOSTSTRUCT); + break; + } + + default: + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} + +static void IPResolveNames( void *dummy ) +{ + SetTimer(hIPResWnd,1,15*60*1000,0); + + PostMessage(hIPResWnd, WM_TIMER,0,0); + + while (GetMessage(&Msg, hIPResWnd, 0, 0)) + { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } +} + +#endif + +/* +; DO PSEUDO HEADER FIRST +; + MOV DX,600H ; PROTOCOL (REVERSED) + MOV AX,TCPLENGTH ; TCP LENGTH + XCHG AH,AL + ADD DX,AX + MOV AX,WORD PTR LOCALADDR[BX] + ADC DX,AX + MOV AX,WORD PTR LOCALADDR+2[BX] + ADC DX,AX + MOV AX,WORD PTR REMOTEADDR[BX] + ADC DX,AX + MOV AX,WORD PTR REMOTEADDR+2[BX] + ADC DX,AX + ADC DX,0 + + MOV PHSUM,DX + + PUSH BX + + MOV BX,TXBUFFER ; HEADER + + MOV CX,TCPLENGTH ; PUT LENGTH INTO HEADER + MOV BUFFLEN[BX],CX +; + MOV SI,BUFFPTR[BX] + + INC CX ; ROUND UP + SHR CX,1 ; WORD COUNT + + CALL DO_CHECKSUM + + ADD DX,PHSUM + ADC DX,0 + NOT DX + + MOV SI,BUFFPTR[BX] + MOV CHECKSUM[SI],DX + + +*/ diff --git a/RTKnown.txt b/RTKnown.txt new file mode 100644 index 0000000..2e0220a --- /dev/null +++ b/RTKnown.txt @@ -0,0 +1,25 @@ +G8BPQ-1 1661431645 +GM8BPQ-4 1661431604 +OH5RM-8 1661431604 +G0BMH-4 1661431604 +AE5E-2 1661431604 +MS0HFI-4 1661431604 +PE1RRR-4 1661431604 +WA3WLH-14 1661431604 +WA3WLH-11 1661431604 +W9IKU-11 1661431604 +N2UEM-11 1661431604 +K5DAT-11 1661431604 +N3MEL-3 1661431604 +N0NJY-11 1661431604 +KB9PVH-11 1661431604 +K8OPG-14 1661431604 +PI1LAP-4 1661431604 +PE1NNZ-5 1661431604 +IZ4FVW-10 1661431604 +EI2GYB-4 1661431604 +G8BPQ-4 1661431604 +W8BAP-6 1661431604 +KF8MZ-6 1661431604 +N8BHL-6 1661431604 +KB8UVN-6 1661431604 diff --git a/RigControl.c b/RigControl.c new file mode 100644 index 0000000..42ea28e --- /dev/null +++ b/RigControl.c @@ -0,0 +1,9143 @@ +/* +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 +*/ + +// +// Rig Control Module +// + +// Dec 29 2009 + +// Add Scan Control for SCS + +// August 2010 + +// Fix logic error in Port Initialisation (wasn't always raising RTS and DTR +// Clear RTS and DTR on close + +// Fix Kenwood processing of multiple messages in one packet. + +// Fix reporting of set errors in scan to the wrong session + + +// Yaesu List + +// FT990 define as FT100 + + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + + +#include +#include +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" +#ifdef WIN32 +#include +#else +char *fcvt(double number, int ndigits, int *decpt, int *sign); +#include +#include +#include +#endif +#include "bpq32.h" + +#include "hidapi.h" + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +int Row = -20; + +extern struct PORTCONTROL * PORTTABLE; + +VOID __cdecl Debugprintf(const char * format, ...); + +struct RIGINFO * RigConfig(struct TNCINFO * TNC, char * buf, int Port); +struct RIGPORTINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL RigCloseConnection(struct RIGPORTINFO * PORT); +BOOL RigWriteCommBlock(struct RIGPORTINFO * PORT); +BOOL DestroyTTYInfo(int port); +void CheckRX(struct RIGPORTINFO * PORT); +static int OpenRigCOMMPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed); +VOID ICOMPoll(struct RIGPORTINFO * PORT); +VOID ProcessFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuff, int len); +VOID ProcessICOMFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuffer, int Len); +int SendResponse(int Stream, char * Msg); +VOID ProcessYaesuFrame(struct RIGPORTINFO * PORT); +VOID YaesuPoll(struct RIGPORTINFO * PORT); +VOID ProcessYaesuCmdAck(struct RIGPORTINFO * PORT); +VOID ProcessKenwoodFrame(struct RIGPORTINFO * PORT, int Length); +VOID KenwoodPoll(struct RIGPORTINFO * PORT); +VOID DummyPoll(struct RIGPORTINFO * PORT); +VOID SwitchAntenna(struct RIGINFO * RIG, char Antenna); +VOID DoBandwidthandAntenna(struct RIGINFO *RIG, struct ScanEntry * ptr); +VOID SetupScanInterLockGroups(struct RIGINFO *RIG); +VOID ProcessFT100Frame(struct RIGPORTINFO * PORT); +VOID ProcessFT990Frame(struct RIGPORTINFO * PORT); +VOID ProcessFT1000Frame(struct RIGPORTINFO * PORT); +VOID AddNMEAChecksum(char * msg); +VOID ProcessNMEA(struct RIGPORTINFO * PORT, char * NMEAMsg, int len); +VOID COMSetDTR(HANDLE fd); +VOID COMClearDTR(HANDLE fd); +VOID COMSetRTS(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +void CM108_set_ptt(struct RIGINFO *RIG, int PTTState); +BOOL OpenHIDPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed); +int HID_Read_Block(struct RIGPORTINFO * PORT); +int HID_Write_Block(struct RIGPORTINFO * PORT); +HANDLE rawhid_open(char * Device); +int rawhid_recv(int num, void *buf, int len, int timeout); +int rawhid_send(int num, void *buf, int len, int timeout); +void rawhid_close(int num); +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT); +VOID ConnecttoFLRIG(struct RIGPORTINFO * PORT); +int DecodeHAMLIBAddr(struct RIGPORTINFO * PORT, char * ptr); +void ProcessHAMLIBFrame(struct RIGPORTINFO * PORT, int Length); +VOID HAMLIBPoll(struct RIGPORTINFO * PORT); +void HAMLIBSlaveThread(struct RIGINFO * RIG); +void CheckAndProcessRTLUDP(struct RIGPORTINFO * PORT); +VOID RTLUDPPoll(struct RIGPORTINFO * PORT); +VOID ConnecttoRTLUDP(struct RIGPORTINFO * PORT); +VOID FLRIGPoll(struct RIGPORTINFO * PORT); +void ProcessFLRIGFrame(struct RIGPORTINFO * PORT); +VOID FLRIGSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value); + +VOID SetupPortRIGPointers(); +VOID PTTCATThread(struct RIGINFO *RIG); +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT); + +int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length); +int GetPTCRadioCommand(struct TNCINFO * TNC, char * Block); +int BuildRigCtlPage(char * _REPLYBUFFER); +void SendRigWebPage(); + +extern TRANSPORTENTRY * L4TABLE; +HANDLE hInstance; + +VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset); +BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase); + +char * GetApplCallFromName(char * App); + +char Modes[25][6] = {"LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM", "CW-R", "RTTY-R", + "????","????","????","????","????","????","????","????","DV", "LSBD1", + "USBD1", "LSBD2","USBD2", "LSBD3","USBD3", "????"}; + +/* +DV = 17 +F8101 +(0000=LSB, 0001=USB, 0002=AM, +0003=CW, 0004=RTTY, +0018=LSB D1, 0019=USB D1, +0020=LSB D2, 0021=USB D2, +0022=LSB D3, 0023=USB D3 +*/ +// 0 1 2 3 4 5 6 7 8 9 0A 0B 0C 88 + +char YaesuModes[16][6] = {"LSB", "USB", "CW", "CWR", "AM", "", "", "", "FM", "", "DIG", "", "PKT", "FMN", "????"}; + +char FT100Modes[9][6] = {"LSB", "USB", "CW", "CWR", "AM", "DIG", "FM", "WFM", "????"}; + +char FT990Modes[13][6] = {"LSB", "USB", "CW2k4", "CW500", "AM6k", "AM2k4", "FM", "FM", "RTTYL", "RTTYU", "PKTL", "PKTFM", "????"}; + +char FT1000Modes[13][6] = {"LSB", "USB", "CW", "CWR", "AM", "AMS", "FM", "WFM", "RTTYL", "RTTYU", "PKTL", "PKTF", "????"}; + +char FTRXModes[8][6] = {"LSB", "USB", "CW", "AM", "FM", "RTTY", "PKT", ""}; + +char KenwoodModes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "????"}; + +//char FT2000Modes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "PKT-L", "FSK-R", "PKT-FM", "FM-N", "PKT-U", "????"}; +char FT2000Modes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "CW-R", "PKT-L", "FSK-R", "PKT-FM", "FM-N", "PKT-U", "????"}; + +char FT991AModes[16][9] = {"????", "LSB", "USB", "CW-U", "FM", "AM", "RTTY-LSB", "CW-L", "DATA-LSB", "RTTY-USB", "DATA-FM", "FM-N", "DATA-USB", "AM-N", "C4FM", "????"}; + +char FLEXModes[16][6] = {"LSB", "USB", "DSB", "CWL", "CWU", "FM", "AM", "DIGU", "SPEC", "DIGL", "SAM", "DRM"}; + +char AuthPassword[100] = ""; + +char LastPassword[17]; + +int NumberofPorts = 0; + +BOOL EndPTTCATThread = FALSE; + +int HAMLIBMasterRunning = 0; +int HAMLIBSlaveRunning = 0; +int FLRIGRunning = 0; + +char * RigWebPage = 0; +int RigWebPageLen = 0; + + +struct RIGPORTINFO * PORTInfo[34] = {NULL}; // Records are Malloc'd + +struct RIGINFO * DLLRIG = NULL; // Rig record for dll PTT interface (currently only for UZ7HO); + + +struct TimeScan * AllocateTimeRec(struct RIGINFO * RIG) +{ + struct TimeScan * Band = zalloc(sizeof (struct TimeScan)); + + RIG->TimeBands = realloc(RIG->TimeBands, (++RIG->NumberofBands+2) * sizeof(void *)); + RIG->TimeBands[RIG->NumberofBands] = Band; + RIG->TimeBands[RIG->NumberofBands+1] = NULL; + + return Band; +} + +struct ScanEntry ** CheckTimeBands(struct RIGINFO * RIG) +{ + int i = 0; + time_t NOW = time(NULL) % 86400; + + // Find TimeBand + + while (i < RIG->NumberofBands) + { + if (RIG->TimeBands[i + 1]->Start > NOW) + { + break; + } + i++; + } + + RIG->FreqPtr = RIG->TimeBands[i]->Scanlist; + + return RIG->FreqPtr; +} + +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState) +{ + if (TNC == NULL) return; + + if (TNC->TXRIG) + Rig_PTTEx(TNC->TXRIG, PTTState, TNC); + else + Rig_PTTEx(TNC->RIG, PTTState, TNC); +} + +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC) +{ + struct RIGPORTINFO * PORT; + int i, Len; + char cmd[32]; + char onString[128]; // Actual CAT strings to send. May be modified for QSY on PTT + char offString[128]; + int onLen = 0, offLen = 0; + + if (RIG == NULL) return; + + PORT = RIG->PORT; + + if (PORT == NULL) + return; + + // CAT string defaults to that set up by RIGConfig in RIG->PTTOn and RIG->PTTOff, + // but can be overidden by Port specify strings from TNC->PTTOn and TNC->PTTOff. + // If PTTSetsFreq is set on a Rig, that overrides the RIG->PTTOn but not TNC->PTTOn + + + if (PTTState) + { + MySetWindowText(RIG->hPTT, "T"); + RIG->WEB_PTT = 'T'; + RIG->PTTTimer = PTTLimit; + RIG->repeatPTTOFFTimer = 0; // Cancel repeated off command + + if (TNC && TNC->PTTOn[0]) + { + memcpy(onString, TNC->PTTOn, TNC->PTTOnLen); + onLen = TNC->PTTOnLen; + } + else + { + memcpy(onString, RIG->PTTOn, RIG->PTTOnLen); + onLen = RIG->PTTOnLen; + + // If PTT_SETS_FREQ set calculate TX Freq and see if changed + + // Freq can be set on the TNC, the RIG, or calculated from current rx freq + pttOffset + + if (TNC && RIG->PTTSetsFreq) + { + long long txfreq = 0; + + if (TNC->TXFreq) + txfreq = TNC->TXFreq + TNC->TXOffset; + else if (TNC->RIG && TNC->RIG->txFreq) + txfreq = RIG->txFreq; // Used if not associated with a TNC port - eg HAMLIB + WSJT + else if (TNC->RIG && TNC->RIG->RigFreq != 0.0) + { + // Use RigFreq + pttOffset, so TX Tracks RX + + long long rxfreq = (long long)(TNC->RIG->RigFreq * 1000000.0) - TNC->RIG->rxOffset; + txfreq = rxfreq + RIG->pttOffset + RIG->txError; + txfreq += RIG->rxError; + } + + if (txfreq) + { + + if (RIG->lastSetFreq != txfreq) + { + char FreqString[80]; + char * CmdPtr = onString; + + RIG->lastSetFreq = txfreq; + + // Convert to CAT string + + sprintf(FreqString, "%012d", txfreq); + + switch (PORT->PortType) + { + case ICOM: + + // CI-V must send all commands as one string, or Radio will start to ack them and + // collide with rest of command + + // Set Freq is sent before set PTT, so set up QSY string then copy PTT string to buffer + // Need to convert two chars to bcd digit + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x5; // Set frequency command + + *(CmdPtr++) = (FreqString[11] - 48) | ((FreqString[10] - 48) << 4); + *(CmdPtr++) = (FreqString[9] - 48) | ((FreqString[8] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + onLen = 10; + } + else + { + *(CmdPtr++) = (FreqString[3] - 48); + *(CmdPtr++) = 0xFD; + onLen = 11; + } + + // Now add PTT String + + memcpy(&onString[onLen], RIG->PTTOn, RIG->PTTOnLen); + onLen += RIG->PTTOnLen; + + case HAMLIB: + + // Dont need to save, as we can send strings separately + + Len = sprintf(cmd, "F %lld\n", txfreq); + i = send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + } + } + } + } + } + else + { + // Drop PTT + + MySetWindowText(RIG->hPTT, " "); + RIG->WEB_PTT = ' '; + RIG->PTTTimer = 0; + if (PORT->PortType == ICOM) + RIG->repeatPTTOFFTimer = 300; // set 30 second repeated off command + + if (TNC && TNC->PTTOff[0]) + { + memcpy(offString, TNC->PTTOff, TNC->PTTOffLen); + offLen = TNC->PTTOffLen; + } + else + { + memcpy(offString, RIG->PTTOff, RIG->PTTOffLen); + offLen = RIG->PTTOffLen; + + // If PTT_SETS_FREQ set calculate TX Freq and see if changed + + if (PTTState == 0 && RIG->PTTSetsFreq && RIG->defaultFreq) + { + // Dropped PTT. See if need to set freq back to default + + long long txfreq = RIG->defaultFreq + RIG->txError; + + if (RIG->lastSetFreq != txfreq) + { + char FreqString[80]; + char * CmdPtr = offString; + + RIG->lastSetFreq = txfreq; + + // Convert to CAT string + + sprintf(FreqString, "%012d", txfreq); + + switch (PORT->PortType) + { + case ICOM: + + // CI-V must send all commands as one string, or Radio will start to ack them and + // collide with rest of command + + // Set Freq is sent after drop PTT, so copy PTT string to buffer then set up QSY string + // Need to convert two chars to bcd digit + + // We copied off string earlier, so just append QSY string + + CmdPtr += offLen; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x5; // Set frequency command + + *(CmdPtr++) = (FreqString[11] - 48) | ((FreqString[10] - 48) << 4); + *(CmdPtr++) = (FreqString[9] - 48) | ((FreqString[8] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + offLen += 10; + } + else + { + *(CmdPtr++) = (FreqString[3] - 48); + *(CmdPtr++) = 0xFD; + offLen += 11; + } + + case FLRIG: + + sprintf(cmd, "%lld", txfreq); + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + case HAMLIB: + + // Dont need to save, as we can send strings separately + + Len = sprintf(cmd, "F %lld\n", txfreq); + send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + } + } + } + } + + // Now send the command + + if (RIG->PTTMode & PTTCI_V) + { + UCHAR * Poll = PORT->TXBuffer; + + // Don't read for 10 secs to avoid clash with PTT OFF + // Should do this for all rigs on port + + for (i = 0; i< PORT->ConfiguredRigs; i++) + PORT->Rigs[i].PollCounter = 100; + + PORT->AutoPoll = TRUE; + + switch (PORT->PortType) + { + case ICOM: + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + case NMEA: + + if (PTTState) + { + memcpy(Poll, onString, onLen); + PORT->TXLen = onLen; + } + else + { + memcpy(Poll, offString, offLen); + PORT->TXLen = offLen; + } + + RigWriteCommBlock(PORT); + + if (PORT->PortType == ICOM && !PTTState) + RigWriteCommBlock(PORT); // Send ICOM PTT OFF Twice + + PORT->Retries = 1; + + if (PORT->PortType != ICOM) + PORT->Timeout = 0; + + return; + + case FT100: + case FT990: + case FT1000: + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState; // OFF/ON + *(Poll++) = 15; + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case YAESU: // 897 - maybe others + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState ? 0x08 : 0x88; // CMD = 08 : PTT ON CMD = 88 : PTT OFF + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case FLRIG: + + sprintf(cmd, "%d", PTTState); + FLRIGSendCommand(PORT, "rig.set_ptt", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + return; + + case HAMLIB: + + Len = sprintf(cmd, "T %d\n", PTTState); + send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + return; + } + } + + if (RIG->PTTMode & PTTRTS) + if (PTTState) + COMSetRTS(PORT->hPTTDevice); + else + COMClearRTS(PORT->hPTTDevice); + + if (RIG->PTTMode & PTTDTR) + if (PTTState) + COMSetDTR(PORT->hPTTDevice); + else + COMClearDTR(PORT->hPTTDevice); + + if (RIG->PTTMode & PTTCM108) + CM108_set_ptt(RIG, PTTState); + + if (RIG->PTTMode & PTTHAMLIB) + { + char Msg[16]; + int Len = sprintf(Msg, "T %d\n", PTTState); + + Len = send(PORT->remoteSock, Msg, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + if (RIG->PTTMode & PTTFLRIG) + { + char cmd[32]; + + sprintf(cmd, "%d", PTTState); + FLRIGSendCommand(PORT, "rig.set_ptt", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } +} + +void saveNewFreq(struct RIGINFO * RIG, double Freq, char * Mode) +{ + if (Freq > 0.0) + { + _gcvt((Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); + strcpy(RIG->WEB_FREQ, RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + if (Mode[0]) + { + strcpy(RIG->ModeString, Mode); + MySetWindowText(RIG->hMODE, Mode); + } + +} + +// Need version that doesn't need Port Number + +int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, int Session, char * Command); + +int Rig_Command(int Session, char * Command) +{ + char * ptr; + int i, n, p, Port; + TRANSPORTENTRY * L4 = L4TABLE; + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + + // Only Allow RADIO from Secure Applications + + _strupr(Command); + + ptr = strchr(Command, 13); + if (ptr) *(ptr) = 0; // Null Terminate + + if (memcmp(Command, "AUTH ", 5) == 0) + { + if (AuthPassword[0] && (memcmp(LastPassword, &Command[5], 16) != 0)) + { + if (CheckOneTimePassword(&Command[5], AuthPassword)) + { + L4 += Session; + L4->Secure_Session = 1; + + sprintf(Command, "Ok\r"); + + memcpy(LastPassword, &Command[5], 16); // Save + + return FALSE; + } + } + + sprintf(Command, "Sorry AUTH failed\r"); + return FALSE; + } + + if (Session != -1) // Used for internal Stop/Start + { + L4 += Session; + + if (L4->Secure_Session == 0) + { + sprintf(Command, "Sorry - you are not allowed to use this command\r"); + return FALSE; + } + } + if (NumberofPorts == 0) + { + sprintf(Command, "Sorry - Rig Control not configured\r"); + return FALSE; + } + + // if Port starts with 'R' then select Radio (was Interlock) number, not BPQ Port + + if (Command[0] == 'R') + { + n = sscanf(&Command[1],"%d ", &Port); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->Interlock == Port) + goto portok; + } + } + + sprintf(Command, "Sorry - Port not found\r"); + return FALSE; + } + + n = sscanf(Command,"%d ", &Port); + + // Look for the port + + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->BPQPort & (1 << Port)) + goto portok; + } + } + + sprintf(Command, "Sorry - Port not found\r"); + return FALSE; + +portok: + + return Rig_CommandEx(PORT, RIG, Session, Command); +} + + + +static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +static char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + + + +int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, int Session, char * Command) +{ + int n, ModeNo, Filter, Port = 0; + double Freq = 0.0; + char FreqString[80]="", FilterString[80]="", Mode[80]="", Data[80] = "", Dummy[80] = ""; + struct MSGWITHOUTLEN * buffptr; + UCHAR * Poll; + + int i; + TRANSPORTENTRY * L4 = L4TABLE; + char * ptr; + int Split, DataFlag, Bandwidth, Antenna; + struct ScanEntry * FreqPtr; + char * CmdPtr; + int Len; + char MemoryBank = 0; // For Memory Scanning + int MemoryNumber = 0; + + // Only Allow RADIO from Secure Applications + + _strupr(Command); + + ptr = strchr(Command, 13); + if (ptr) *(ptr) = 0; // Null Terminate + + if (memcmp(Command, "AUTH ", 5) == 0) + { + if (AuthPassword[0] && (memcmp(LastPassword, &Command[5], 16) != 0)) + { + if (CheckOneTimePassword(&Command[5], AuthPassword)) + { + L4 += Session; + L4->Secure_Session = 1; + + sprintf(Command, "Ok\r"); + + memcpy(LastPassword, &Command[5], 16); // Save + + return FALSE; + } + } + + sprintf(Command, "Sorry AUTH failed\r"); + return FALSE; + } + + if (Session != -1) // Used for internal Stop/Start + { + L4 += Session; + + if (L4->Secure_Session == 0) + { + sprintf(Command, "Sorry - you are not allowed to use this command\r"); + return FALSE; + } + } + if (NumberofPorts == 0) + { + sprintf(Command, "Sorry - Rig Control not configured\r"); + return FALSE; + } + + // if Port starts with 'R' then select Radio (was Interlock) number, not BPQ Port + + if (Command[0] == 'R') + n = sscanf(Command,"%s %s %s %s %s", &Dummy, &FreqString[0], &Mode[0], &FilterString[0], &Data[0]); + else + n = sscanf(Command,"%d %s %s %s %s", &Port, &FreqString[0], &Mode[0], &FilterString[0], &Data[0]); + + if (_stricmp(FreqString, "CLOSE") == 0) + { + PORT->Closed = 1; + RigCloseConnection(PORT); + + MySetWindowText(RIG->hSCAN, "C"); + RIG->WEB_SCAN = 'C'; + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (_stricmp(FreqString, "OPEN") == 0) + { + PORT->ReopenDelay = 300; + PORT->Closed = 0; + + MySetWindowText(RIG->hSCAN, ""); + RIG->WEB_SCAN = ' '; + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (RIG->RIGOK == 0) + { + if (Session != -1) + { + if (PORT->Closed) + sprintf(Command, "Sorry - Radio port closed\r"); + else + sprintf(Command, "Sorry - Radio not responding\r"); + } + return FALSE; + } + + if (n == 2 && _stricmp(FreqString, "FREQ") == 0) + { + if (RIG->Valchar[0]) + sprintf(Command, "Frequency is %s MHz\r", RIG->Valchar); + else + sprintf(Command, "Frequency not known\r"); + + return FALSE; + } + + if (n == 2 && _stricmp(FreqString, "PTT") == 0) + { + Rig_PTTEx(RIG, TRUE, NULL); + RIG->PTTTimer = 10; // 1 sec + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (n > 1) + { + if (_stricmp(FreqString, "SCANSTART") == 0) + { + if (RIG->NumberofBands) + { + RIG->ScanStopped &= (0xffffffff ^ (1 << Port)); + + if (Session != -1) // Used for internal Stop/Start + RIG->ScanStopped &= 0xfffffffe; // Clear Manual Stopped Bit + + if (n > 2) + RIG->ScanCounter = atoi(Mode) * 10; //Start Delay + else + RIG->ScanCounter = 10; + + RIG->WaitingForPermission = FALSE; // In case stuck + + if (RIG->ScanStopped == 0) + { + SetWindowText(RIG->hSCAN, "S"); + RIG->WEB_SCAN = 'S'; + } + sprintf(Command, "Ok\r"); + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 SCANSTART Port %d", Port); + } + else + sprintf(Command, "Sorry no Scan List defined for this port\r"); + + return FALSE; + } + + if (_stricmp(FreqString, "SCANSTOP") == 0) + { + RIG->ScanStopped |= (1 << Port); + + if (Session != -1) // Used for internal Stop/Start + RIG->ScanStopped |= 1; // Set Manual Stopped Bit + + MySetWindowText(RIG->hSCAN, ""); + RIG->WEB_SCAN = ' '; + + sprintf(Command, "Ok\r"); + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 SCANSTOP Port %d", Port); + + RIG->PollCounter = 50 / RIG->PORT->ConfiguredRigs; // Dont read freq for 5 secs + + return FALSE; + } + } + + RIG->Session = Session; // BPQ Stream + RIG->PollCounter = 50; // Dont read freq for 5 secs in case clash with Poll + + if (_stricmp(FreqString, "TUNE") == 0) + { + char ReqBuf[256]; + char SendBuff[256]; + char FLPoll[80]; + + switch (PORT->PortType) + { + case ICOM: + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + // IC7100 Tune Fe fe 88 e0 1c 01 02 fd + + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1C; + *(CmdPtr++) = 0x01; + *(CmdPtr++) = 0x02; + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 8; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "AC111;AC;"); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + + case FLRIG: + + strcpy(FLPoll, "rig.tune"); + + Len = sprintf(ReqBuf, Req, FLPoll, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + } + sprintf(Command, "Ok\r"); + return FALSE; + } + + sprintf(Command, "Sorry - TUNE only supported on your radio\r"); + return FALSE; + } + + if (_stricmp(FreqString, "POWER") == 0) + { + char PowerString[8] = ""; + int Power = atoi(Mode); + int len; + char cmd[80]; + + switch (PORT->PortType) + { + case ICOM: + + if (n != 3 || Power > 255) + { + strcpy(Command, "Sorry - Invalid Format - should be POWER Level (0 - 255)\r"); + return FALSE; + } + + sprintf(PowerString, "%04d", Power); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + // IC7100 Set Power Fe fe 88 e0 14 0a xx xx fd + + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x14; + *(CmdPtr++) = 0x0A; + + // Need to convert param to decimal digits + + *(CmdPtr++) = (PowerString[1] - 48) | ((PowerString[0] - 48) << 4); + *(CmdPtr++) = (PowerString[3] - 48) | ((PowerString[2] - 48) << 4); + + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 9; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + + if (n != 3 || Power > 200 || Power < 5) + { + strcpy(Command, "Sorry - Invalid Format - should be POWER Level (5 - 200)\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "PC%03d;PC;", Power); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + + case FLRIG: + + len = sprintf(cmd, "%d", Power); + + FLRIGSendCommand(PORT, "rig.set_power", cmd); + + sprintf(Command, "Ok\r"); + return FALSE; + } + + sprintf(Command, "Sorry - POWER not supported on your Radio\r"); + return FALSE; + } + + if (_stricmp(FreqString, "CMD") == 0) + { + // Send arbitrary command to radio + + char c; + int val; + char * ptr1; + int Len; + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be HEX Hexstring\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return FALSE; + + ptr1 = strstr(Command, "CMD"); + + if (ptr1 == NULL) + return FALSE; + + ptr1 += 4; + + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + + switch (PORT->PortType) + { + case ICOM: + + // String is in Hex + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + while (c = *(ptr1++)) + { + if (c == ' ') continue; // Allow space between pairs + + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(CmdPtr++) = val; + } + + *(CmdPtr++) = 0xFD; + + + *(CmdPtr) = 0; + + Len = (int)(CmdPtr - (char *)&buffptr[30]); + break; + + case KENWOOD: + case FT991A: + case FT2000: + case FLEX: + case NMEA: + + // use text command + + Len = sprintf(CmdPtr, ptr1); + break; + + case YAESU: + + // String is in Hex (5 values) + + while (c = *(ptr1++)) + { + if (c == ' ') continue; // Allow space between pairs + + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(CmdPtr++) = val; + } + + *(CmdPtr) = 0; + + Len = 5; + break; + + default: + sprintf(Command, "Sorry - CMD not supported on your Radio\r"); + return FALSE; + } + + FreqPtr[0].Cmd1Len = Len; // for ICOM + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + } + + if (_memicmp(FreqString, "Chan", 4) == 0) + { + if (strchr(FreqString, '/') ) // Bank/Chan + { + MemoryBank = FreqString[4]; + MemoryNumber = atoi(&FreqString[6]); + } + else + MemoryNumber = atoi(&FreqString[4]); // Just Chan + + Freq = 0.0; + } + else + { + Freq = atof(FreqString); + + if (Freq < 0.1 && PORT->PortType != FLRIG) + { + strcpy(Command, "Sorry - Invalid Frequency\r"); + return FALSE; + } + } + + Freq = Freq * 1000000.0; + + sprintf(FreqString, "%09.0f", Freq); + + if (PORT->PortType != ICOM) + strcpy(Data, FilterString); // Others don't have a filter. + + Split = DataFlag = Bandwidth = Antenna = 0; + + _strupr(Data); + + if (strchr(Data, '+')) + Split = '+'; + else if (strchr(Data, '-')) + Split = '-'; + else if (strchr(Data, 'S')) + Split = 'S'; + else if (strchr(Data, 'D')) + DataFlag = 1; + + if (strchr(Data, 'W')) + Bandwidth = 'W'; + else if (strchr(Data, 'N')) + Bandwidth = 'N'; + + if (strstr(Data, "A1")) + Antenna = '1'; + else if (strstr(Data, "A2")) + Antenna = '2'; + else if (strstr(Data, "A3")) + Antenna = '3'; + else if (strstr(Data, "A4")) + Antenna = '4'; + else if (strstr(Data, "A5")) + Antenna = '5'; + else if (strstr(Data, "A6")) + Antenna = '6'; + + switch (PORT->PortType) + { + case ICOM: + + if (n == 2) + // Set Freq Only + + ModeNo = -1; + else + { + if (n < 4 && RIG->ICF8101 == 0) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode Filter Width\r"); + return FALSE; + } + + Filter = atoi(FilterString); + + for (ModeNo = 0; ModeNo < 24; ModeNo++) + { + if (_stricmp(Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 24) + { + sprintf(Command, "Sorry - Invalid Mode\r"); + return FALSE; + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + FreqPtr->Dwell = 51; + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + + if (MemoryNumber) + { + // Set Memory Channel instead of Freq, Mode, etc + + char ChanString[5]; + + // Send Set Memory, then Channel + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xFD; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + sprintf(ChanString, "%04d", MemoryNumber); + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = (ChanString[1] - 48) | ((ChanString[0] - 48) << 4); + *(CmdPtr++) = (ChanString[3] - 48) | ((ChanString[2] - 48) << 4); + *(CmdPtr++) = 0xFD; + + FreqPtr[0].Cmd1Len = 14; + + if (MemoryBank) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xA0; + *(CmdPtr++) = MemoryBank - 0x40; + *(CmdPtr++) = 0xFD; + + FreqPtr[0].Cmd1Len += 8; + } + } + else + { + if (RIG->ICF8101) + { + // Set Freq is 1A 35 and set Mode 1A 36 + + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x35; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 12; + + if (ModeNo != -1) // Dont Set + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + FreqPtr[0].Cmd2Len = 9; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x36; // Set mode command + *(CmdPtr++) = 0; + if (ModeNo > 10) + *(CmdPtr++) = ModeNo + 6; + else + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 0xFD; + } + } + else + { + + *(CmdPtr++) = 0x5; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 10; + } + else + { + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 11; + } + + // Send Set VFO in case last chan was memory + + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = RIG->RigAddr; + // *(CmdPtr++) = 0xE0; + + // *(CmdPtr++) = 0x07; + // *(CmdPtr++) = 0xFD; + + // FreqPtr[0].Cmd1Len = 17; + + if (ModeNo != -1) // Dont Set + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x6; // Set Mode + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = Filter; + *(CmdPtr++) = 0xFD; + + FreqPtr->Cmd2Len = 8; + + if (Split) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + FreqPtr->Cmd3Len = 7; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0xF; // Set Mode + if (Split == 'S') + *(CmdPtr++) = 0x10; + else + if (Split == '+') + *(CmdPtr++) = 0x12; + else + if (Split == '-') + *(CmdPtr++) = 0x11; + + *(CmdPtr++) = 0xFD; + } + else if (DataFlag) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1a; + + if ((strcmp(RIG->RigName, "IC7100") == 0) || + (strcmp(RIG->RigName, "IC7410") == 0) || + (strcmp(RIG->RigName, "IC7600") == 0) || + (strcmp(RIG->RigName, "IC7610") == 0) || + (strcmp(RIG->RigName, "IC7300") == 0)) + { + FreqPtr[0].Cmd3Len = 9; + *(CmdPtr++) = 0x6; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter; //Filter + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + FreqPtr[0].Cmd3Len = 9; + *(CmdPtr++) = 0x4; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter; // Filter + } + else + { + FreqPtr[0].Cmd3Len = 8; + *(CmdPtr++) = 0x6; // Set Data + *(CmdPtr++) = 0x1; //On + } + + *(CmdPtr++) = 0xFD; + } + } + + if (Antenna == '5' || Antenna == '6') + { + // Antenna select for 746 and maybe others + + // Could be going in cmd2 3 or 4 + + if (FreqPtr[0].Cmd2 == NULL) + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + FreqPtr[0].Cmd2Len = 7; + } + else if (FreqPtr[0].Cmd3 == NULL) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + FreqPtr[0].Cmd3Len = 7; + } + else + { + CmdPtr = FreqPtr->Cmd4 = FreqPtr->Cmd4Msg; + FreqPtr[0].Cmd4Len = 7; + } + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x12; // Set Antenna + *(CmdPtr++) = Antenna - '5'; // 0 for A5 1 for A6 + *(CmdPtr++) = 0xFD; + } + } + } + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case YAESU: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + for (ModeNo = 0; ModeNo < 15; ModeNo++) + { + if (_stricmp(YaesuModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + // Send Mode then Freq - setting Mode seems to change frequency + + *(Poll++) = ModeNo; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 7; // Set Mode + + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = 1; // Set Freq + + FreqPtr->Cmd1Len = 10; + + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 3; // Status Poll + + FreqPtr->Cmd1Len = 15; + } + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + + case FT100: + case FT990: + case FT1000: + + if (n == 2) // Set Freq Only + ModeNo = -1; + else + { + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + if (PORT->PortType == FT100) + { + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (_stricmp(FT100Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 8) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + else if (PORT->PortType == FT990) + { + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (_stricmp(FT990Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 12) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + else + { + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (_stricmp(FT1000Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 12) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + // Send Mode then Freq - setting Mode seems to change frequency + + if (ModeNo == -1) // Don't set Mode + { + // Changing the length messes up a lot of code, + // so set freq twice instead of omitting entry + + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = 10; // Set Freq + } + else + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = ModeNo; + *(Poll++) = 12; // Set Mode + } + + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = 10; // Set Freq + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + if (PORT->PortType == FT990 || PORT->YaesuVariant == FT1000D) + *(Poll++) = 3; + else + *(Poll++) = 2; // 100 or 1000MP + + *(Poll++) = 16; // Status Poll + + FreqPtr->Cmd1Len = 15; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (PORT->PortType == FT2000) + if (_stricmp(FT2000Modes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == FT991A) + if (_stricmp(FT991AModes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == KENWOOD) + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + if (PORT->PortType == FLEX) + if (_stricmp(FLEXModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo > 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + if (PORT->PortType == FT2000) + FreqPtr->Cmd1Len = sprintf(Poll, "FA%s;MD0%X;FA;MD;", &FreqString[1], ModeNo); + else + if (PORT->PortType == FT991A) + FreqPtr->Cmd1Len = sprintf(Poll, "FA%s;MD0%X;FA;MD;", &FreqString, ModeNo); + else + if (PORT->PortType == FLEX) + FreqPtr->Cmd1Len = sprintf(Poll, "ZZFA00%s;ZZMD%02d;ZZFA;ZZMD;", &FreqString[1], ModeNo); + else + { + if (Antenna == '5' || Antenna == '6') + FreqPtr->Cmd1Len = sprintf(Poll, "FA00%s;MD%d;AN%c;FA;MD;", FreqString, ModeNo, Antenna - 4); + else + FreqPtr->Cmd1Len = sprintf(Poll, "FA00%s;MD%d;FA;MD;", FreqString, ModeNo); + } + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case NMEA: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + i = sprintf(Poll, "$PICOA,90,%02x,RXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(Poll); + Len = i; + i = sprintf(Poll + Len, "$PICOA,90,%02x,TXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(Poll + Len); + Len += i; + i = sprintf(Poll + Len, "$PICOA,90,%02x,MODE,%s*xx\r\n", RIG->RigAddr, Mode); + AddNMEAChecksum(Poll + Len); + + FreqPtr->Cmd1Len = i + Len; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case HAMLIB: + { + char cmd[80]; + + int len = sprintf(cmd, "F %s\n+f\nM %s %d\n+m\n", + FreqString, Mode, atoi(Data)); + + send(PORT->remoteSock, cmd, len, 0); + sprintf(Command, "Ok\r"); + + saveNewFreq(RIG, Freq, Mode); + + return FALSE; + } + + case FLRIG: + { + char cmd[80]; + + int len = sprintf(cmd, "%.0f", Freq); + + strcpy(PORT->ScanEntry.Cmd2Msg, Mode); + strcpy(PORT->ScanEntry.Cmd3Msg, FilterString); + + if (Freq > 0.0) + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + + else if (PORT->ScanEntry.Cmd2Msg[0] && Mode[0] != '*') + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } + else + { + sprintf(Command, "Sorry - Nothing to do\r"); + return FALSE; + } + + PORT->AutoPoll = 0; + + saveNewFreq(RIG, Freq, Mode); + return TRUE; + } + + + case RTLUDP: + { + char cmd[80]; + int len = 5; + int FreqInt = (int)Freq; + int FM = 0; + int AM = 1; + int USB = 2; + int LSB = 3; + + cmd[0] = 0; + cmd[1] = FreqInt & 0xff; + cmd[2] = (FreqInt >> 8) & 0xff; + cmd[3] = (FreqInt >> 16) & 0xff; + cmd[4] = (FreqInt >> 24) & 0xff; + + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + if (Mode[0]) + { + if (strcmp(Mode, "FM") == 0) + memcpy(&cmd[1], &FM, 4); + else if (strcmp(Mode, "AM") == 0) + memcpy(&cmd[1], &AM, 4); + else if (strcmp(Mode, "USB") == 0) + memcpy(&cmd[1], &USB, 4); + else if (strcmp(Mode, "LSB") == 0) + memcpy(&cmd[1], &LSB, 4); + + cmd[0] = 1; + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + } + + saveNewFreq(RIG, Freq, Mode); + + sprintf(Command, "Ok\r"); + return FALSE; + } + + + + + + } + return TRUE; +} + +int BittoInt(UINT BitMask) +{ + // Returns bit position of first 1 bit in BitMask + + int i = 0; + while ((BitMask & 1) == 0) + { + BitMask >>= 1; + i ++; + } + return i; +} + +extern char * RadioConfigMsg[36]; + +struct TNCINFO RIGTNC; // Dummy TNC Record for Rigcontrol without a corresponding TNC + +DllExport BOOL APIENTRY Rig_Init() +{ + struct RIGPORTINFO * PORT; + int i, p, port; + struct RIGINFO * RIG; + struct TNCINFO * TNC = &RIGTNC; + HWND hDlg; +#ifndef LINBPQ + int RigRow = 0; +#endif + int NeedRig = 0; + int Interlock = 0; + + memset(&RIGTNC, 0, sizeof(struct TNCINFO)); + + TNCInfo[40] = TNC; + + // Get config info + + NumberofPorts = 0; + + for (port = 0; port < 32; port++) + PORTInfo[port] = NULL; + + // See if any rigcontrol defined (either RADIO or RIGCONTROL lines) + + for (port = 0; port < 32; port++) + { + if (RadioConfigMsg[port]) + NeedRig++; + } + + if (NeedRig == 0) + { + SetupPortRIGPointers(); // Sets up Dummy Rig Pointer + return FALSE; + } + + +#ifndef LINBPQ + + TNC->Port = 40; + CreatePactorWindow(TNC, "RIGCONTROL", "RigControl", 10, PacWndProc, 550, NeedRig * 20 + 60, NULL); + hDlg = TNC->hDlg; + +#endif + + TNC->ClientHeight = NeedRig * 20 + 60; + TNC->ClientWidth = 550; + + for (port = 0; port < 33; port++) + { + if (RadioConfigMsg[port]) + { + char msg[1000]; + + char * SaveRigConfig = _strdup(RadioConfigMsg[port]); + char * RigConfigMsg1 = _strdup(RadioConfigMsg[port]); + + Interlock = atoi(&RigConfigMsg1[5]); + + RIG = RigConfig(TNC, RigConfigMsg1, port); + + if (RIG == NULL) + { + // Report Error + + sprintf(msg,"Port %d Invalid Rig Config %s", port, SaveRigConfig); + WritetoConsole(msg); + free(SaveRigConfig); + free(RigConfigMsg1); + continue; + } + + RIG->Interlock = Interlock; + + if (RIG->PORT->IOBASE[0] == 0) + { + // We have to find the SCS TNC in this scan group as TNC is now a dummy + + struct TNCINFO * PTCTNC; + int n; + + for (n = 1; n < 33; n++) + { + PTCTNC = TNCInfo[n]; + + if (PTCTNC == NULL || (PTCTNC->Hardware != H_SCS && PTCTNC->Hardware != H_ARDOP)) + continue; + + if (PTCTNC->RXRadio != Interlock) + continue; + + RIG->PORT->PTC = PTCTNC; + } + } + +#ifndef LINBPQ + + // Create line for this rig + + RIG->hLabel = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 10, RigRow, 80,20, hDlg, NULL, hInstance, NULL); + + // RIG->hCAT = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + // 90, RigRow, 40,20, hDlg, NULL, hInstance, NULL); + + RIG->hFREQ = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 95, RigRow, 100,20, hDlg, NULL, hInstance, NULL); + + RIG->hMODE = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 200, RigRow, 100,20, hDlg, NULL, hInstance, NULL); + + RIG->hSCAN = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 300, RigRow, 20,20, hDlg, NULL, hInstance, NULL); + + RIG->hPTT = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 320, RigRow, 20,20, hDlg, NULL, hInstance, NULL); + + RIG->hPORTS = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 340, RigRow, 210, 20, hDlg, NULL, hInstance, NULL); + + RigRow += 20; + + //if (PORT->PortType == ICOM) + //{ + // sprintf(msg,"%02X", PORT->Rigs[i].RigAddr); + // SetWindowText(RIG->hCAT, msg); + //} + SetWindowText(RIG->hLabel, RIG->RigName); +#endif + + RIG->WEB_Label = _strdup(RIG->RigName); + // RIG->WEB_CAT; + RIG->WEB_FREQ = zalloc(80); + RIG->WEB_MODE = zalloc(80); + RIG->WEB_PORTS = zalloc(80); + strcpy(RIG->WEB_FREQ, "-----------"); + strcpy(RIG->WEB_MODE, "------"); + + RIG->WEB_PTT = ' '; + RIG->WEB_SCAN = ' '; + } + } + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == HAMLIB) + { + HAMLIBMasterRunning = 1; + ConnecttoHAMLIB(PORT); + } + else if (PORT->PortType == FLRIG) + { + FLRIGRunning = 1; + ConnecttoFLRIG(PORT); + } + else if (PORT->PortType == RTLUDP) + ConnecttoRTLUDP(PORT); + else if (PORT->HIDDevice) // This is RAWHID, Not CM108 + OpenHIDPort(PORT, PORT->IOBASE, PORT->SPEED); + else if (PORT->PTC == 0 && _stricmp(PORT->IOBASE, "CM108") != 0) + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + + if (PORT->PTTIOBASE[0]) // Using separate port for PTT? + { + if (PORT->PTTIOBASE[3] == '=') + PORT->hPTTDevice = OpenCOMPort(&PORT->PTTIOBASE[4], PORT->SPEED, FALSE, FALSE, FALSE, 0); + else + PORT->hPTTDevice = OpenCOMPort(&PORT->PTTIOBASE[3], PORT->SPEED, FALSE, FALSE, FALSE, 0); + } + else + PORT->hPTTDevice = PORT->hDevice; // Use same port for PTT + } + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i < PORT->ConfiguredRigs; i++) + { + int j; + int k = 0; + int BitMask; + struct _EXTPORTDATA * PortEntry; + + RIG = &PORT->Rigs[i]; + + SetupScanInterLockGroups(RIG); + + // Get record for each port in Port Bitmap + + // The Scan "Request Permission to Change" code needs the Port Records in order - + // Those with active connect lock (eg SCS) first, then those with just a connect pending lock (eg WINMOR) + // then those with neither + + BitMask = RIG->BPQPort; + for (j = 0; j < 32; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == CONLOCK) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + BitMask = RIG->BPQPort; + for (j = 0; j < 32; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == SIMPLE) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + BitMask = RIG->BPQPort; + for (j = 0; j < 32; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == NONE) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + RIG->PORT = PORT; // For PTT + + if (RIG->NumberofBands) + CheckTimeBands(RIG); // Set initial timeband + +#ifdef WIN32 + if (RIG->PTTCATPort[0]) // Serial port RTS to CAT + _beginthread(PTTCATThread,0,RIG); +#endif + if (RIG->HAMLIBPORT) + { + // Open listening socket + + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + SOCKET sock = 0; + u_long param=1; + + char szBuff[80]; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + sock = socket(AF_INET, SOCK_STREAM, 0); + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + psin->sin_port = htons(RIG->HAMLIBPORT); // Convert to network ordering + + if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket(sock); + } + else + { + if (listen(sock, 5) < 0) + { + sprintf(szBuff, "listen(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + } + else + { + ioctl(sock, FIONBIO, ¶m); + RIG->ListenSocket = sock; + _beginthread(HAMLIBSlaveThread, 0, RIG); + } + } + } + Rig_PTTEx(RIG, 0, NULL); // Send initial PTT Off (Mainly to set input mux on soundcard rigs) + } + } + + // MoveWindow(hDlg, Rect.left, Rect.top, Rect.right - Rect.left, Row + 100, TRUE); + + SetupPortRIGPointers(); + + RigWebPage = zalloc(10); + + WritetoConsole("\nRig Control Enabled\n"); + + return TRUE; +} + +DllExport BOOL APIENTRY Rig_Close() +{ + struct RIGPORTINFO * PORT; + struct TNCINFO * TNC; + int n, p; + + HAMLIBMasterRunning = 0; // Close HAMLIB thread(s) + HAMLIBSlaveRunning = 0; // Close HAMLIB thread(s) + FLRIGRunning = 0; // Close FLRIG thread(s) + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == NMEA) + { + // Send Remote OFF + + int i; + char REMOFF[80]; + + i = sprintf(REMOFF, "$PICOA,90,%02x,REMOTE,OFF*xx\r\n", PORT->Rigs[0].RigAddr); + AddNMEAChecksum(REMOFF); + + WriteCOMBlock(PORT->hDevice, REMOFF, i); + Sleep(200); + } + + if (PORT->PortType != HAMLIB && PORT->PortType != RTLUDP && PORT->PortType != FLRIG && strcmp(PORT->IOBASE, "RAWHID") != 0) + { + if (PORT->hPTTDevice != PORT->hDevice) + CloseCOMPort(PORT->hPTTDevice); + + CloseCOMPort(PORT->hDevice); + } + + PORT->hDevice = 0; + PORT->hPTTDevice = 0; + + // Free the RIG and Port Records + + for (n = 0; n < PORT->ConfiguredRigs; n++) + { + struct RIGINFO * RIG = &PORT->Rigs[n]; + struct HAMLIBSOCK * Entry = RIG->Sockets; + struct HAMLIBSOCK * Save; + + // if RIG has any HAMLIB slave connections, close them + + Entry = RIG->Sockets; + + while (Entry) + { + closesocket(Entry->Sock); + Save = Entry; + Entry = Entry->Next; + free(Save); + } + + RIG->Sockets = 0; + + if (RIG->TimeBands) + free (RIG->TimeBands[1]->Scanlist); + + if (RIG->PTTCATPort[0]) + { + Rig_PTTEx(RIG, FALSE, NULL); // Make sure PTT is down + EndPTTCATThread = TRUE; + } + } + + free (PORT); + PORTInfo[p] = NULL; + } + + NumberofPorts = 0; // For possible restart + + // And free the TNC config info + + for (p = 1; p < 33; p++) + { + TNC = TNCInfo[p]; + + if (TNC == NULL) + continue; + + TNC->RIG = NULL; + + // memset(TNC->WL2KInfoList, 0, sizeof(TNC->WL2KInfoList)); + + } + + return TRUE; +} + + + +BOOL Rig_Poll() +{ + int p, i, Len; + char RigWeb[16384]; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == DUMMY) + { + DummyPoll(PORT); + return TRUE; + } + + if (PORT->hDevice == 0) + { + // Try to reopen every 15 secs + + PORT->ReopenDelay++; + + if (PORT->ReopenDelay > 150) + { + PORT->ReopenDelay = 0; + + if (PORT->PortType == HAMLIB) + ConnecttoHAMLIB(PORT); + else if (PORT->PortType == FLRIG) + ConnecttoFLRIG(PORT); + else if (PORT->PortType == RTLUDP) + ConnecttoRTLUDP(PORT); + else if (PORT->HIDDevice) + OpenHIDPort(PORT, PORT->IOBASE, PORT->SPEED); + else if (PORT->PTC == 0 + && _stricmp(PORT->IOBASE, "REMOTE") != 0 + && _stricmp(PORT->IOBASE, "CM108") != 0) + { + if (PORT->Closed == 0) + { + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + if (PORT->IOBASE && PORT->PTTIOBASE[0] == 0) // Using separate port for PTT? + PORT->hPTTDevice = PORT->hDevice; + } + } + } + } + + if (PORT == NULL || (PORT->hDevice == 0 && PORT->PTC == 0 && PORT->remoteSock == 0)) + continue; + + // Check PTT Timers + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + // Active PTT command + + if (RIG->PTTTimer) + { + RIG->PTTTimer--; + if (RIG->PTTTimer == 0) + Rig_PTTEx(RIG, FALSE, NULL); + } + + // repeated off timer + + if (RIG->repeatPTTOFFTimer) + { + RIG->repeatPTTOFFTimer--; + if (RIG->repeatPTTOFFTimer == 0) + { + Rig_PTTEx(RIG, FALSE, NULL); + RIG->repeatPTTOFFTimer = 0; // Don't repeat repeat! + } + } + } + + CheckRX(PORT); + + switch (PORT->PortType) + { + case ICOM: + + ICOMPoll(PORT); + break; + + case YAESU: + case FT100: + case FT990: + case FT1000: + + YaesuPoll(PORT); + break; + + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + case NMEA: + + KenwoodPoll(PORT); + break; + + case HAMLIB: + HAMLIBPoll(PORT); + break; + + case RTLUDP: + RTLUDPPoll(PORT); + break; + + case FLRIG: + FLRIGPoll(PORT); + break; + + + } + } + + // Build page for Web Display + + Len = BuildRigCtlPage(RigWeb); + + if (strcmp(RigWebPage, RigWeb) == 0) + return TRUE; // No change + + free(RigWebPage); + + RigWebPage = _strdup(RigWeb); + + SendRigWebPage(); + + return TRUE; +} + + +BOOL RigCloseConnection(struct RIGPORTINFO * PORT) +{ + // disable event notification and wait for thread + // to halt + + CloseCOMPort(PORT->hDevice); + PORT->hDevice = 0; + return TRUE; + +} // end of CloseConnection() + +#ifndef WIN32 +#define ONESTOPBIT 0 +#define ONE5STOPBITS 1 +#define TWOSTOPBITS 2 +#endif + +int OpenRigCOMMPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed) +{ + if (PORT->remoteSock) // Using WINMORCONTROL + return TRUE; + + if (PORT->PortType == FT2000 || PORT->PortType == FT991A || strcmp(PORT->Rigs[0].RigName, "FT847") == 0) // FT2000 and similar seem to need two stop bits + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, TWOSTOPBITS); + else if (PORT->PortType == NMEA) + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, ONESTOPBIT); + else + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, TWOSTOPBITS); + + if (PORT->hDevice == 0) + { + PORT->Alerted = TRUE; + return FALSE; + } + + PORT->Alerted = FALSE; + + if (PORT->PortType == PTT || (PORT->Rigs[0].PTTMode & PTTRTS)) + COMClearRTS(PORT->hDevice); + else + COMSetRTS(PORT->hDevice); + + if (PORT->PortType == PTT || (PORT->Rigs[0].PTTMode & PTTDTR)) + COMClearDTR(PORT->hDevice); + else + COMSetDTR(PORT->hDevice); + + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + // Looks like FT847 Needa a "Cat On" Command + + UCHAR CATON[6] = {0,0,0,0,0}; + + WriteCOMBlock(PORT->hDevice, CATON, 5); + } + + if (PORT->PortType == NMEA) + { + // Looks like NMEA Needs Remote ON + + int i; + char REMON[80]; + + i = sprintf(REMON, "$PICOA,90,%02x,REMOTE,ON*xx\r\n", PORT->Rigs[0].RigAddr); + AddNMEAChecksum(REMON); + + WriteCOMBlock(PORT->hDevice, REMON, i); + } + + + return TRUE; +} + +void CheckRX(struct RIGPORTINFO * PORT) +{ + int Length; + char NMEAMsg[100]; + unsigned char * ptr; + int len; + + if (PORT->PortType == FLRIG) + { + // Data received in thread + + return; + } + + if (PORT->PortType == HAMLIB) + { + // Data received in thread (do we need thread?? + + Length = PORT->RXLen; + PORT->RXLen = 0; + } + + else if (PORT->PortType == RTLUDP) + { + CheckAndProcessRTLUDP(PORT); + Length = 0; + PORT->RXLen = 0; + return; + } + + else if (PORT->HIDDevice) + Length = HID_Read_Block(PORT); + else if (PORT->PTC) + Length = GetPTCRadioCommand(PORT->PTC, &PORT->RXBuffer[PORT->RXLen]); + else if (PORT->remoteSock) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Length = recvfrom(PORT->remoteSock, PORT->RXBuffer, 500, 0, (struct sockaddr *)&rxaddr, &addrlen); + if (Length == -1) + Length = 0; + } + else + { + if (PORT->hDevice == 0) + return; + + // only try to read number of bytes in queue + + if (PORT->RXLen == 500) + PORT->RXLen = 0; + + Length = 500 - (DWORD)PORT->RXLen; + + Length = ReadCOMBlock(PORT->hDevice, &PORT->RXBuffer[PORT->RXLen], Length); + } + + if (Length == 0) + return; // Nothing doing + + PORT->RXLen += Length; + + Length = PORT->RXLen; + + switch (PORT->PortType) + { + case ICOM: + + if (Length < 6) // Minimum Frame Sise + return; + + if (PORT->RXBuffer[Length-1] != 0xfd) + return; + + ProcessICOMFrame(PORT, PORT->RXBuffer, Length); // Could have multiple packets in buffer + + PORT->RXLen = 0; // Ready for next frame + return; + + case YAESU: + + // Possible responses are a single byte ACK/NAK or a 5 byte info frame + + if (Length == 1 && PORT->CmdSent > 0) + { + ProcessYaesuCmdAck(PORT); + return; + } + + if (Length < 5) // Frame Sise + return; + + if (Length > 5) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessYaesuFrame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case FT100: + + // Only response should be a 16 byte info frame + + if (Length < 32) // Frame Sise why??????? + return; + + if (Length > 32) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT100Frame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case FT990: + + // Only response should be a 32 byte info frame + + if (Length < 32) // Frame Sise + return; + + if (Length > 32) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT990Frame(PORT); + PORT->RXLen = 0; // Ready for next frame + return; + + + case FT1000: + + // Only response should be a 16 byte info frame + + ptr = PORT->RXBuffer; + + if (Length < 16) // Frame Sise + return; + + if (Length > 16) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT1000Frame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case KENWOOD: + case FT2000: + case FT991A: + case FLEX: + + if (Length < 2) // Minimum Frame Sise + return; + + if (Length > 50) // Garbage + { + PORT->RXLen = 0; // Ready for next frame + return; + } + + if (PORT->RXBuffer[Length-1] != ';') + return; + + ProcessKenwoodFrame(PORT, Length); + + PORT->RXLen = 0; // Ready for next frame + return; + + case NMEA: + + ptr = memchr(PORT->RXBuffer, 0x0a, Length); + + while (ptr != NULL) + { + ptr++; // include lf + len = (int)(ptr - &PORT->RXBuffer[0]); + + memcpy(NMEAMsg, PORT->RXBuffer, len); + + NMEAMsg[len] = 0; + +// if (Check0183CheckSum(NMEAMsg, len)) + ProcessNMEA(PORT, NMEAMsg, len); + + Length -= len; // bytes left + + if (Length > 0) + { + memmove(PORT->RXBuffer, ptr, Length); + ptr = memchr(PORT->RXBuffer, 0x0a, Length); + } + else + ptr=0; + } + + PORT->RXLen = Length; + return; + + case HAMLIB: + + ProcessHAMLIBFrame(PORT, Length); + PORT->RXLen = 0; + return; + } +} + +VOID ProcessICOMFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + int NewLen; + + // Split into Packets. By far the most likely is a single frame + // so treat as special case + + FendPtr = memchr(rxbuffer, 0xfd, Len); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessFrame(PORT, rxbuffer, Len); + return; + } + + // Process the first Packet in the buffer + + NewLen = (int)(FendPtr - rxbuffer + 1); + + ProcessFrame(PORT, rxbuffer, NewLen); + + // Loop Back + + ProcessICOMFrame(PORT, FendPtr+1, Len - NewLen); + return; +} + + +BOOL RigWriteCommBlock(struct RIGPORTINFO * PORT) +{ + // if using a PTC radio interface send to the SCSPactor Driver, else send to COM port + + int ret; + + if (PORT->HIDDevice) + HID_Write_Block(PORT); + else + if (PORT->PTC) + SendPTCRadioCommand(PORT->PTC, PORT->TXBuffer, PORT->TXLen); + else if (PORT->remoteSock) + ret = sendto(PORT->remoteSock, PORT->TXBuffer, PORT->TXLen, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + else + { + BOOL fWriteStat; + DWORD BytesWritten; + +#ifndef WIN32 + BytesWritten = write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#else + fWriteStat = WriteFile(PORT->hDevice, PORT->TXBuffer, PORT->TXLen, &BytesWritten, NULL ); +#endif + if (PORT->TXLen != BytesWritten) + { + if (PORT->hDevice) + CloseCOMPort(PORT->hDevice); + + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + + if (PORT->IOBASE && PORT->PTTIOBASE[0] == 0) // Using separate port for PTT? + PORT->hPTTDevice = PORT->hDevice; + + if (PORT->hDevice) + { + // Try Again + +#ifndef WIN32 + BytesWritten = write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#else + fWriteStat = WriteFile(PORT->hDevice, PORT->TXBuffer, PORT->TXLen, &BytesWritten, NULL ); +#endif + } + } + } + + ret = GetLastError(); + + PORT->Timeout = 100; // 2 secs + return TRUE; +} + +VOID ReleasePermission(struct RIGINFO *RIG) +{ + int i = 0; + struct _EXTPORTDATA * PortRecord; + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 3); // Release Perrmission + i++; + } +} + +int GetPermissionToChange(struct RIGPORTINFO * PORT, struct RIGINFO *RIG) +{ + struct ScanEntry ** ptr; + struct _EXTPORTDATA * PortRecord; + int i = 0; + + // Get Permission to change + + if (RIG->RIG_DEBUG) + Debugprintf("GetPermissionToChange - WaitingForPermission = %d", RIG->WaitingForPermission); + + // See if any two stage (CONLOCK) ports + + if (RIG->PortRecord[0] && RIG->PortRecord[0]->SCANCAPABILITIES != CONLOCK) + goto CheckOtherPorts; + + + if (RIG->WaitingForPermission) + { + // This code should only be called if port needs two stage + // eg Request then confirm. only SCS Pactor at the moment. + + // TNC has been asked for permission, and we are waiting respoonse + // Only SCS pactor returns WaitingForPrmission, so check shouldn't be called on others + + RIG->OKtoChange = (int)(intptr_t)RIG->PortRecord[0]->PORT_EXT_ADDR(6, RIG->PortRecord[0]->PORTCONTROL.PORTNUMBER, 2); // Get Ok Flag + + if (RIG->OKtoChange == 1) + { + if (RIG->RIG_DEBUG) + Debugprintf("Check Permission returned OK to change"); + + i = 1; + goto CheckOtherPorts; + } + + if (RIG->OKtoChange == -1) + { + // Permission Refused. Wait Scan Interval and try again + + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s Refused permission - waiting ScanInterval %d", + RIG->PortRecord[0]->PORT_DLL_NAME, PORT->FreqPtr->Dwell ); + + RIG->WaitingForPermission = FALSE; + SetWindowText(RIG->hSCAN, "-"); + RIG->WEB_SCAN = '='; + + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 50; + + return FALSE; + } + + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s Still Waiting", RIG->PortRecord[0]->PORT_DLL_NAME); + + + return FALSE; // Haven't got reply yet. Will re-enter next tick + } + else + { + // not waiting for permission, so must be first call of a cycle + + if (RIG->PortRecord[0] && RIG->PortRecord[0]->PORT_EXT_ADDR) + RIG->WaitingForPermission = (int)(intptr_t)RIG->PortRecord[0]->PORT_EXT_ADDR(6, RIG->PortRecord[0]->PORTCONTROL.PORTNUMBER, 1); // Request Perrmission + + // If it returns zero there is no need to wait. + // Normally SCS Returns True for first call, but returns 0 if Link not running + + if (RIG->WaitingForPermission) + return FALSE; // Wait till driver has status + + if (RIG->RIG_DEBUG) + Debugprintf("First call to %s returned OK to change", RIG->PortRecord[0]->PORT_DLL_NAME); + + i = 1; + } + +CheckOtherPorts: + + // Either first TNC gave permission or there are no SCS like ports. + // Ask any others (these are assumed to give immediate yes/no + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + + if (PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 1)) + { + // 1 means can't change - release all + + if (RIG->RIG_DEBUG && PORT->FreqPtr) + Debugprintf("Scan Debug %s Refused permission - waiting ScanInterval %d", + PortRecord->PORT_DLL_NAME, PORT->FreqPtr->Dwell); + + RIG->WaitingForPermission = FALSE; + MySetWindowText(RIG->hSCAN, "-"); + RIG->WEB_SCAN = '-'; + + if (PORT->FreqPtr) + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 50; + + ReleasePermission(RIG); + return FALSE; + } + else + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s gave permission", PortRecord->PORT_DLL_NAME); + + i++; + } + + + RIG->WaitingForPermission = FALSE; + + // Update pointer to next frequency + + RIG->FreqPtr++; + + ptr = RIG->FreqPtr; + + if (ptr == NULL) + { + Debugprintf("Scan Debug - No freqs - quitting"); + return FALSE; // No Freqs + } + + if (ptr[0] == (struct ScanEntry *)0) // End of list - reset to start + { + ptr = CheckTimeBands(RIG); + + if (ptr[0] == (struct ScanEntry *)0) + { + // Empty Timeband - delay 60 secs (we need to check for timeband change sometimes) + + RIG->ScanCounter = 600; + ReleasePermission(RIG); + return FALSE; + } + } + + PORT->FreqPtr = ptr[0]; // Save Scan Command Block + + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 150; + + MySetWindowText(RIG->hSCAN, "S"); + RIG->WEB_SCAN = 'S'; + + // Do Bandwidth and antenna switches (if needed) + + DoBandwidthandAntenna(RIG, ptr[0]); + + return TRUE; +} + +VOID DoBandwidthandAntenna(struct RIGINFO *RIG, struct ScanEntry * ptr) +{ + // If Bandwidth Change needed, do it + + int i; + struct _EXTPORTDATA * PortRecord; + + if (ptr->Bandwidth || ptr->RPacketMode || ptr->HFPacketMode + || ptr->PMaxLevel || ptr->ARDOPMode[0] || ptr->VARAMode) + { + i = 0; + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + + RIG->CurrentBandWidth = ptr->Bandwidth; + + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, ptr); + +/* if (ptr->Bandwidth == 'R') // Robust Packet + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 6); // Set Robust Packet + else + + if (ptr->Bandwidth == 'W') + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 4); // Set Wide Mode + else + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 5); // Set Narrow Mode +*/ + i++; + } + } + + // If Antenna Change needed, do it + + // 5 and 6 are used for CI-V switching so ignore here + + if (ptr->Antenna && ptr->Antenna < '5') + { + SwitchAntenna(RIG, ptr->Antenna); + } + + return; +} + +VOID ICOMPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + int i; + + struct RIGINFO * RIG; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + } + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + RIG = &PORT->Rigs[PORT->CurrentRig]; + + + SetWindowText(RIG->hFREQ, "-----------"); + strcpy(RIG->WEB_FREQ, "-----------"); + SetWindowText(RIG->hMODE, "------"); + strcpy(RIG->WEB_MODE, "------"); + +// SetWindowText(RIG->hFREQ, "145.810000"); +// SetWindowText(RIG->hMODE, "RTTY/1"); + + PORT->Rigs[PORT->CurrentRig].RIGOK = FALSE; + + return; + + } + + // Send Data if avail, else send poll + + PORT->CurrentRig++; + + if (PORT->CurrentRig >= PORT->ConfiguredRigs) + PORT->CurrentRig = 0; + + RIG = &PORT->Rigs[PORT->CurrentRig]; + +/* + RIG->DebugDelay ++; + + if (RIG->DebugDelay > 600) + { + RIG->DebugDelay = 0; + Debugprintf("Scan Debug %d %d %d %d %d %d", PORT->CurrentRig, + RIG->NumberofBands, RIG->RIGOK, RIG->ScanStopped, RIG->ScanCounter, + RIG->WaitingForPermission); + } +*/ + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq/1000000.0); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + if (PORT->FreqPtr->Freq > 0.0) + { + _gcvt((PORT->FreqPtr->Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); // For MH + strcpy(RIG->WEB_FREQ, RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Manual Change Freq to %9.4f", PORT->FreqPtr->Freq/1000000.0); + + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + if (PORT->FreqPtr->Freq > 0.0) + { + _gcvt((PORT->FreqPtr->Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); // For MH + strcpy(RIG->WEB_FREQ, RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; // First send the set Freq + RigWriteCommBlock(PORT); + PORT->Retries = 2; + + ReleaseBuffer(buffptr); + + PORT->AutoPoll = FALSE; + + return; + } + + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) // no point in reading freq if we are about to change + return; + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter) + return; + } + + // Need to make sure we don't poll multiple rigs on port at the same time + + if (RIG->RIGOK) + { + PORT->Retries = 2; + RIG->PollCounter = 10 / PORT->ConfiguredRigs; // Once Per Sec + } + else + { + PORT->Retries = 1; + RIG->PollCounter = 100 / PORT->ConfiguredRigs; // Slow Poll if down + } + + RIG->PollCounter += PORT->CurrentRig * 3; + + PORT->AutoPoll = TRUE; + + // Read Frequency + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 10; + + return; +} + + +VOID ProcessFrame(struct RIGPORTINFO * PORT, UCHAR * Msg, int framelen) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG; + int i; + int cmdSent = PORT->TXBuffer[4]; + + if (Msg[0] != 0xfe || Msg[1] != 0xfe) + + // Duff Packet - return + + return; + + if (Msg[2] != 0xe0 && Msg[2] != 0) + { + // Echo - Proves a CI-V interface is attached + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + Debugprintf("Cat Port OK"); + } + return; + } + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + if (Msg[3] == RIG->RigAddr) + goto ok; + } + + return; + +ok: + + if (Msg[4] == 0xFB) + { + // Accept + + if (cmdSent == 0x1C && PORT->TXBuffer[5] == 1) // Tune + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Tune OK"); + + PORT->Timeout = 0; + return; + } + + if (cmdSent == 0x14 && PORT->TXBuffer[5] == 0x0A) // Power + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Power OK"); + + PORT->Timeout = 0; + return; + } + + // if it was the set freq, send the set mode + + if (RIG->ICF8101) + { + if (cmdSent == 0x1A && PORT->TXBuffer[5] == 0x35) + { + if (PORT->FreqPtr->Cmd2) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd2, PORT->FreqPtr->Cmd2Len); + PORT->TXLen = PORT->FreqPtr->Cmd2Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency Set OK"); + + PORT->Timeout = 0; + } + return; + } + + if (cmdSent == 0x1a && PORT->TXBuffer[5] == 0x36) + { + // Set Mode Response - if scanning read freq, else return OK to user + + if (RIG->ScanStopped == 0) + { + ReleasePermission(RIG); // Release Perrmission + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency and Mode Set OK"); + + RIG->PollCounter = 30 / RIG->PORT->ConfiguredRigs; // Dont read freq for 3 secs + } + } + + PORT->Timeout = 0; + return; + } + + if (cmdSent == 5 || cmdSent == 7) // Freq or VFO + { + if (PORT->FreqPtr && PORT->FreqPtr->Cmd2) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd2, PORT->FreqPtr->Cmd2Len); + PORT->TXLen = PORT->FreqPtr->Cmd2Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency Set OK"); + + PORT->Timeout = 0; + } + return; + } + + if (cmdSent == 6) // Set Mode + { + if (PORT->FreqPtr && PORT->FreqPtr->Cmd3) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd3, PORT->FreqPtr->Cmd3Len); + PORT->TXLen = PORT->FreqPtr->Cmd3Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + goto SetFinished; + } + + if (cmdSent == 0x08) + { + // Memory Chan + + cmdSent = 0; // So we only do it once + + goto SetFinished; + } + + if (cmdSent == 0x12) + goto SetFinished; // Antenna always sent last + + + if (cmdSent == 0x0f || cmdSent == 0x01a) // Set DUP or Data + { + // These are send from cmd3. There may be a cmd4 + // for antenna switching + + if (PORT->FreqPtr && PORT->FreqPtr->Cmd4) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd4, PORT->FreqPtr->Cmd4Len); + PORT->TXLen = PORT->FreqPtr->Cmd4Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + +SetFinished: + + // Set Mode Response - if scanning read freq, else return OK to user + + if (RIG->ScanStopped == 0) + { + ReleasePermission(RIG); // Release Perrmission + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency and Mode Set OK"); + + RIG->PollCounter = 30 / RIG->PORT->ConfiguredRigs; // Dont read freq for 3 secs + } + } + + PORT->Timeout = 0; + return; + } + + if (Msg[4] == 0xFA) + { + // Reject + + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + if (cmdSent == 5) + SendResponse(RIG->Session, "Sorry - Set Frequency Command Rejected"); + else + if (cmdSent == 6) + SendResponse(RIG->Session, "Sorry - Set Mode Command Rejected"); + else + if (cmdSent == 0x0f) + SendResponse(RIG->Session, "Sorry - Set Shift Command Rejected"); + else + if (cmdSent == 0x12) + SendResponse(RIG->Session, "Sorry - Set Antenna Command Rejected"); + else + if (cmdSent == 0x1C && PORT->TXBuffer[5] == 1) // Tune + SendResponse(RIG->Session, "Sorry - Tune Command Rejected"); + else + if (cmdSent == 0x14 && PORT->TXBuffer[5] == 0x0a) // Power + SendResponse(RIG->Session, "Sorry - Power Command Rejected"); + else + SendResponse(RIG->Session, "Sorry - Command Rejected"); + + } + return; + } + + if (Msg[4] == PORT->TXBuffer[4]) + { + // Response to our command + + // Any valid frame is an ACK + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + // Manual response probably to CMD Query. Return response + + char reply[256] = "CMD Response "; + char * p1 = &reply[13]; + UCHAR * p2 = &Msg[4]; + int n = framelen - 4; + + while (n > 0) + { + sprintf(p1, "%02X ", *(p2)); + p1 += 3; + p2++; + n--; + } + + SendResponse(RIG->Session, reply); + + } + + } + else + return; // What does this mean?? + + + if (PORT->PORTOK == FALSE) + { + // Just come up +// char Status[80]; + + PORT->PORTOK = TRUE; +// sprintf(Status,"COM%d PORT link OK", PORT->IOBASE); +// SetWindowText(PORT->hStatus, Status); + } + + if (Msg[4] == 3) + { + // Rig Frequency + int n, j, decdigit; + long long Freq = 0; + int start = 9; + long long MHz, Hz; + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + + + if (RIG->IC735) + start = 8; // shorted msg + + for (j = start; j > 4; j--) + { + n = Msg[j]; + decdigit = (n >> 4); + decdigit *= 10; + decdigit += n & 0xf; + Freq = (Freq *100 ) + decdigit; + } + + Freq += RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + + // Now get Mode + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + + if (RIG->ICF8101) + { + Poll[4] = 0x1A; // Get Mode + Poll[5] = 0x34; + Poll[6] = 0xFD; + PORT->TXLen = 7; + } + else + { + Poll[4] = 0x4; // Get Mode + Poll[5] = 0xFD; + + PORT->TXLen = 6; + } + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + if (RIG->ICF8101) + { + if (cmdSent == 0x1A && PORT->TXBuffer[5] == 0x34) + { + // Get Mode + + unsigned int Mode; + + Mode = (Msg[7] >> 4); + Mode *= 10; + Mode += Msg[7] & 0xf; + + if (Mode > 24) Mode = 24; + + sprintf(RIG->WEB_MODE,"%s", Modes[Mode]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + return; + } + } + + + if (Msg[4] == 4) + { + // Mode + + unsigned int Mode; + + Mode = (Msg[5] >> 4); + Mode *= 10; + Mode += Msg[5] & 0xf; + + if (Mode > 24) Mode = 24; + + if (RIG->IC735) + sprintf(RIG->WEB_MODE,"%s", Modes[Mode]); + else + sprintf(RIG->WEB_MODE,"%s/%d", Modes[Mode], Msg[6]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } +} + +int SendResponse(int Session, char * Msg) +{ + PDATAMESSAGE Buffer; + BPQVECSTRUC * VEC; + TRANSPORTENTRY * L4 = L4TABLE; + + if (Session == -1) + return 0; + + Buffer = GetBuff(); + + L4 += Session; + + Buffer->PID = 0xf0; + Buffer->LENGTH = MSGHDDRLEN + 1; // Includes PID + Buffer->LENGTH += sprintf(Buffer->L2DATA, "%s\r", Msg); + + VEC = L4->L4TARGET.HOST; + + C_Q_ADD(&L4->L4TX_Q, (UINT *)Buffer); + +#ifndef LINBPQ + + if (VEC) + PostMessage(VEC->HOSTHANDLE, BPQMsg, VEC->HOSTSTREAM, 2); +#endif + return 0; +} + +VOID ProcessFT100Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // Bytes 0 is Band + // 1 - 4 is Freq in binary in units of 1.25 HZ (!) + // Byte 5 is Mode + + Freq = (Msg[1] << 24) + (Msg[2] << 16) + (Msg[3] << 8) + Msg[4]; + + FreqF = (Freq * 1.25) / 1000000; + + if (PORT->YaesuVariant == FT1000MP) + FreqF = FreqF / 2; // No idea why! + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + if (PORT->PortType == FT100) + { + Mode = Msg[5] & 15; + if (Mode > 8) Mode = 8; + sprintf(RIG->WEB_MODE,"%s", FT100Modes[Mode]); + } + else // FT1000 + { + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + } + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 Change is more + ReleasePermission(RIG); // Release Perrmission to change +} + + + +VOID ProcessFT990Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // Bytes 0 is Band + // 1 - 4 is Freq in units of 10Hz (I think!) + // Byte 5 is Mode + + Freq = (Msg[1] << 16) + (Msg[2] << 8) + Msg[3]; + + FreqF = (Freq * 10.0) / 1000000; + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 change is more + ReleasePermission(RIG); // Release Perrmission to change +} + +VOID ProcessFT1000Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // I think the FT1000/1000D is same as 990 + // FT1000MP is similar to FT100, but steps on .625 Hz (despite manual) + // Bytes 0 is Band + // 1 - 4 is Freq in binary in units of 1.25 HZ (!) + // Byte 5 is Mode + + if (PORT->YaesuVariant == FT1000MP) + { + Freq = (Msg[1] << 24) + (Msg[2] << 16) + (Msg[3] << 8) + Msg[4]; + FreqF = (Freq * 1.25) / 1000000; + FreqF = FreqF / 2; // No idea why! + } + else + { + Freq = (Msg[1] << 16) + (Msg[2] << 8) + Msg[3]; + FreqF = (Freq * 10.0) / 1000000; + } + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + if (PORT->PortType == FT100) + { + Mode = Msg[5] & 15; + if (Mode > 8) Mode = 8; + sprintf(RIG->WEB_MODE,"%s", FT100Modes[Mode]); + } + else // FT1000 + { + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + } + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 change is more + ReleasePermission(RIG); // Release Perrmission to change +} + + + + + +VOID ProcessYaesuCmdAck(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + PORT->Timeout = 0; + PORT->RXLen = 0; // Ready for next frame + + if (PORT->CmdSent == 1) // Set Freq + { + ReleasePermission(RIG); // Release Perrmission + + if (Msg[0]) + { + // I think nonzero is a Reject + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Sorry - Set Frequency Rejected"); + + return; + } + else + { + if (RIG->ScanStopped == 0) + { + // Send a Get Freq - We Don't Poll when scanning + + Poll[0] = Poll[1] = Poll[2] = Poll[3] = 0; + Poll[4] = 0x3; // Get frequency amd mode command + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->CmdSent = 0; + } + else + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + return; + } + } + + if (PORT->CmdSent == 7) // Set Mode + { + if (Msg[0]) + { + // I think nonzero is a Reject + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Sorry - Set Mode Rejected"); + + return; + } + else + { + // Send the Frequency + + memcpy(Poll, &Poll[5], 5); + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Retries = 2; + + return; + } + } + +} +VOID ProcessYaesuFrame(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int n, j, Freq = 0, decdigit; + double FreqF; + unsigned int Mode; + + // I'm not sure we get anything but a Command Response, + // and the only command we send is Get Rig Frequency and Mode + + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + for (j = 0; j < 4; j++) + { + n = Msg[j]; + decdigit = (n >> 4); + decdigit *= 10; + decdigit += n & 0xf; + Freq = (Freq *100 ) + decdigit; + } + + FreqF = Freq / 100000.0; + + RIG->RigFreq = FreqF; + +// Valchar = _fcvt(FreqF, 6, &dec, &sign); + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + Mode = Msg[4]; + + if (Mode > 15) Mode = 15; + + sprintf(RIG->WEB_MODE,"%s", YaesuModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + // FT847 Manual Freq Change response ends up here + + if (strcmp(RIG->RigName, "FT847") == 0) + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + if (PORT->CmdSent == -1) + ReleasePermission(RIG); // Release Perrmission to change + } +} + +VOID YaesuPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + + PORT->Rigs[PORT->CurrentRig].RIGOK = FALSE; + + return; + + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, 24); + + if (PORT->PortType == YAESU) + { + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + PORT->TXLen = 15; // No Cmd ACK, so send Mode, Freq and Poll + PORT->CmdSent = -1; + } + else + { + PORT->TXLen = 5; + PORT->CmdSent = Poll[4]; + } + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + return; + } + + // FT100 + + PORT->TXLen = 15; // Set Mode, Set Freq, Poll + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + if (PORT->PortType == YAESU) + { + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + PORT->TXLen = 15; // Send all + PORT->CmdSent = -1; + } + else + { + PORT->TXLen = 5; // First send the set Freq + PORT->CmdSent = Poll[4]; + } + } + else + PORT->TXLen = 15; // Send all + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) // no point in reading freq if we are about to change + return; + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter) + return; + } + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + Poll[0] = 0; + Poll[1] = 0; + Poll[2] = 0; + + if (PORT->PortType == FT990 || PORT->PortType == YAESU || PORT->YaesuVariant == FT1000D) + Poll[3] = 3; + else + Poll[3] = 2; + + if (PORT->PortType == YAESU) + Poll[4] = 0x3; // Get frequency amd mode command + else + Poll[4] = 16; // FT100/990/1000 Get frequency amd mode command + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID ProcessNMEA(struct RIGPORTINFO * PORT, char * Msg, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + } + + if (memcmp(&Msg[13], "RXF", 3) == 0) + { + if (Length < 24) + return; + + RIG->RigFreq = atof(&Msg[17]); + + sprintf(RIG->WEB_FREQ,"%f", RIG->RigFreq); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + return; + } + + if (memcmp(&Msg[13], "MODE", 3) == 0) + { + char * ptr; + + if (Length < 24) + return; + + ptr = strchr(&Msg[18], '*'); + if (ptr) *(ptr) = 0; + + SetWindowText(RIG->hMODE, &Msg[18]); + strcpy(RIG->WEB_MODE, &Msg[18]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + return; + } + + +} + + +//FA00014103000;MD2; + +VOID ProcessKenwoodFrame(struct RIGPORTINFO * PORT, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + UCHAR * ptr; + int CmdLen; + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + RIG->RIGOK = TRUE; + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else if (Msg[0] == 'A' && Msg[1] == 'C') + SendResponse(RIG->Session, "TUNE OK"); + else if (Msg[0] == 'P' && Msg[1] == 'C') + SendResponse(RIG->Session, "Power Set OK"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + return; + } + + +Loop: + + if (PORT->PortType == FLEX) + { + Msg += 2; // Skip ZZ + Length -= 2; + } + + ptr = strchr(Msg, ';'); + CmdLen = (int)(ptr - Msg + 1); + + if (Msg[0] == 'F' && Msg[1] == 'A' && CmdLen > 9) + { + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + long long Freq; + long long MHz, Hz; + + Freq = strtoll(&Msg[2], NULL, 10) + RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + +/* + if (PORT->PortType == FT2000) + { + memcpy(FreqDecimal,&Msg[4], 6); + Msg[4] = 0; + } + else if (PORT->PortType == FT991A) + { + memcpy(FreqDecimal,&Msg[5], 6); + Msg[5] = 0; + } + + else + { + memcpy(FreqDecimal,&Msg[7], 6); + Msg[7] = 0; + } + FreqDecimal[6] = 0; +*/ + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + PORT->Timeout = 0; + } + else if (Msg[0] == 'M' && Msg[1] == 'D') + { + int Mode; + + if (PORT->PortType == FT2000) + { + Mode = Msg[3] - 48; + if (Mode > 13) Mode = 13; + SetWindowText(RIG->hMODE, FT2000Modes[Mode]); + strcpy(RIG->WEB_MODE, FT2000Modes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FT991A) + { + Mode = Msg[3] - 48; + if (Mode > 16) + Mode -= 7; + + if (Mode > 15) Mode = 13; + SetWindowText(RIG->hMODE, FT991AModes[Mode]); + strcpy(RIG->WEB_MODE, FT991AModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FLEX) + { + Mode = atoi(&Msg[3]); + if (Mode > 12) Mode = 12; + SetWindowText(RIG->hMODE, FLEXModes[Mode]); + strcpy(RIG->WEB_MODE, FLEXModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else + { + Mode = Msg[2] - 48; + if (Mode > 7) Mode = 7; + SetWindowText(RIG->hMODE, KenwoodModes[Mode]); + strcpy(RIG->WEB_MODE, KenwoodModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + } + + if (CmdLen < Length) + { + // Another Message in Buffer + + ptr++; + Length -= (int)(ptr - Msg); + + if (Length <= 0) + return; + + memmove(Msg, ptr, Length +1); + + goto Loop; + } +} + + +VOID KenwoodPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Kenwood + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Timeout = 0; + RIG->PollCounter = 10; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + PORT->TXLen = RIG->PollLen; + strcpy(Poll, RIG->Poll); + + RigWriteCommBlock(PORT); + PORT->Retries = 1; + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID DummyPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; +/* + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; +*/ + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + return; +} + +VOID SwitchAntenna(struct RIGINFO * RIG, char Antenna) +{ + struct RIGPORTINFO * PORT; + char Ant[3]=" "; + + if (RIG == NULL) return; + + PORT = RIG->PORT; + + Ant[1] = Antenna; + + SetWindowText(RIG->hPTT, Ant); + + switch (Antenna) + { + case '1': + COMClearDTR(PORT->hDevice); + COMClearRTS(PORT->hDevice); + break; + case '2': + COMSetDTR(PORT->hDevice); + COMClearRTS(PORT->hDevice); + break; + case '3': + COMClearDTR(PORT->hDevice); + COMSetRTS(PORT->hDevice); + break; + case '4': + COMSetDTR(PORT->hDevice); + COMSetRTS(PORT->hDevice); + break; + } +} + +BOOL DecodeModePtr(char * Param, double * Dwell, double * Freq, char * Mode, + char * PMinLevel, char * PMaxLevel, char * PacketMode, + char * RPacketMode, char * Split, char * Data, char * WinmorMode, + char * Antenna, BOOL * Supress, char * Filter, char * Appl, + char * MemoryBank, int * MemoryNumber, char * ARDOPMode, char * VARAMode, int * BandWidth, int * Power) +{ + char * Context; + char * ptr; + + *Filter = '1'; // Defaults + *PMinLevel = 1; + *MemoryBank = 0; + *MemoryNumber = 0; + *Mode = 0; + *ARDOPMode = 0; + *VARAMode = 0; + *Power = 0; + + + ptr = strtok_s(Param, ",", &Context); + + if (ptr == NULL) + return FALSE; + + // "New" format - Dwell, Freq, Params. + + // Each param is a 2 char pair, separated by commas + + // An - Antenna + // Pn - Pactor + // Wn - Winmor + // Pn - Packet + // Fn - Filter + // Sx - Split + + // 7.0770/LSB,F1,A3,WN,P1,R1 + + *Dwell = atof(ptr); + + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == NULL) + return FALSE; + + + // May be a frequency or a Memory Bank/Channel + + if (_memicmp(ptr, "Chan", 4) == 0) + { + if (strchr(ptr, '/')) // Bank/Chan + { + memcpy(MemoryBank, &ptr[4], 1); + *MemoryNumber = atoi(&ptr[6]); + } + else + *MemoryNumber = atoi(&ptr[4]); // Just Chan + + *Freq = 0.0; + } + else + *Freq = atof(ptr); + + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == NULL || strlen(ptr) > 8) + return FALSE; + + // If channel, dont need mode + + if (*MemoryNumber == 0) + { + strcpy(Mode, ptr); + ptr = strtok_s(NULL, ",", &Context); + } + + while (ptr) + { + if (isdigit(ptr[0])) + *BandWidth = atoi(ptr); + + else if (memcmp(ptr, "APPL=", 5) == 0) + strcpy(Appl, ptr + 5); + + else if (memcmp(ptr, "POWER=", 6) == 0) + *Power = atoi(ptr + 6); + + else if (ptr[0] == 'A' && (ptr[1] == 'S' || ptr[1] == '0') && strlen(ptr) < 7) + strcpy(ARDOPMode, "S"); + + else if (ptr[0] == 'A' && strlen(ptr) > 2 && strlen(ptr) < 7) + strcpy(ARDOPMode, &ptr[1]); + + else if (ptr[0] == 'A' && strlen(ptr) == 2) + *Antenna = ptr[1]; + + else if (ptr[0] == 'F') + *Filter = ptr[1]; + + else if (ptr[0] == 'R') + *RPacketMode = ptr[1]; + + else if (ptr[0] == 'H') + *PacketMode = ptr[1]; + + else if (ptr[0] == 'N') + *PacketMode = ptr[1]; + + else if (ptr[0] == 'P') + { + *PMinLevel = ptr[1]; + *PMaxLevel = ptr[strlen(ptr) - 1]; + } + else if (ptr[0] == 'W') + { + *WinmorMode = ptr[1]; + if (*WinmorMode == '0') + *WinmorMode = 'X'; + else if (*WinmorMode == '1') + *WinmorMode = 'N'; + else if (*WinmorMode == '2') + *WinmorMode = 'W'; + } + + else if (ptr[0] == 'V') + { + *VARAMode = ptr[1]; + // W N S T (skip) or 0 (also Skip) + if (ptr[1] == '0') + *VARAMode = 'S'; + } + else if (ptr[0] == '+') + *Split = '+'; + else if (ptr[0] == '-') + *Split = '-'; + else if (ptr[0] == 'S') + *Split = 'S'; + else if (ptr[0] == 'D') + *Data = 1; + else if (ptr[0] == 'X') + *Supress = TRUE; + + ptr = strtok_s(NULL, ",", &Context); + } + return TRUE; +} + +VOID AddNMEAChecksum(char * msg) +{ + UCHAR CRC = 0; + + msg++; // Skip $ + + while (*(msg) != '*') + { + CRC ^= *(msg++); + } + + sprintf(++msg, "%02X\r\n", CRC); +} + +void DecodeRemote(struct RIGPORTINFO * PORT, char * ptr) +{ + // Param is IPHOST:PORT for use with WINMORCONTROL + + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + UCHAR param = 1; + u_long ioparam = 1; + + char * port = strlop(ptr, ':'); + + PORT->remoteSock = socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + return; + + setsockopt (PORT->remoteSock, SOL_SOCKET, SO_BROADCAST, ¶m, 1); + + if (port == NULL) + return; + + ioctl (PORT->remoteSock, FIONBIO, &ioparam); + + destaddr->sin_family = AF_INET; + destaddr->sin_addr.s_addr = inet_addr(ptr); + destaddr->sin_port = htons(atoi(port)); + + if (destaddr->sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname(ptr); + + if (!HostEnt) + return; // Resolve failed + + memcpy(&destaddr->sin_addr.s_addr,HostEnt->h_addr,4); + } + + return; +} + + +char * CM108Device = NULL; + +void DecodeCM108(int Port, char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + int32_t VID = 0, PID = 0; + char product[256]; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate(0,0); // so we list devices(USHORT)VID, (USHORT)PID); + cur_dev = devs; + while (cur_dev) + { + wcstombs(product, cur_dev->product_string, 255); + + if (product) + Debugprintf("HID Device %s VID %X PID %X %s", product, cur_dev->vendor_id, cur_dev->product_id, cur_dev->path); + else + Debugprintf("HID Device %s VID %X PID %X %s", "Missing Product", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path); + + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + path_to_open = cur_dev->path; + + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + char msg[128]; + sprintf(msg,"Port %d Unable to open CM108 device %x %x", Port, VID, PID); + WritetoConsole(msg); + } + } + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif +} + + + +// Called by Port Driver .dll to add/update rig info + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + +struct RIGINFO * RigConfig(struct TNCINFO * TNC, char * buf, int Port) +{ + int i; + char * ptr; + char * COMPort = NULL; + char * RigName; + int RigAddr; + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + struct ScanEntry ** FreqPtr; + char * CmdPtr; + char * Context; + struct TimeScan * SaveBand; + char PTTRigName[] = "PTT"; + double ScanFreq; + double Dwell; + char MemoryBank; // For Memory Scanning + int MemoryNumber; + BOOL RIG_DEBUG = FALSE; + + BOOL PTTControlsInputMUX = FALSE; + BOOL DataPTT = FALSE; + int DataPTTOffMode = 1; // ACC + int ICF8101Mode = 8; // USB (as in upper sideband) + + char onString[256] = ""; + char offString[256] = ""; + int onLen = 0; + int offLen = 0; + + CM108Device = NULL; + Debugprintf("Processing RIG line %s", buf); + + // Starts RADIO Interlockgroup + + ptr = strtok_s(&buf[5], " \t\n\r", &Context); // Skip Interlock + if (ptr == NULL) return FALSE; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return FALSE; + + if (_memicmp(ptr, "DEBUG", 5) == 0) + { + ptr = strtok_s(NULL, " \t\n\r", &Context); + RIG_DEBUG = TRUE; + } + + if (_memicmp(ptr, "AUTH", 4) == 0) + { + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return FALSE; + if (strlen(ptr) > 100) return FALSE; + + strcpy(AuthPassword, ptr); + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (ptr == NULL || ptr[0] == 0) + return FALSE; + + if (_memicmp(ptr, "DUMMY", 5) == 0) + { + // Dummy to allow PTC application scanning + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = DUMMY; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + goto CheckScan; + } + + if (_memicmp(ptr, "FLRIG", 5) == 0) + { + // Use FLRIG + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = FLRIG; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "FLRIG"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for other config statementes and scan params + + goto CheckOtherParams; + } + + if (_memicmp(ptr, "HAMLIB", 5) == 0) + { + // Use rigctld + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = HAMLIB; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "HAMLIB"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } + + if (_memicmp(ptr, "rtludp", 5) == 0) + { + // rtl_fm with udp freq control + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = RTLUDP; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "RTLUDP"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } + + + if ((_memicmp(ptr, "VCOM", 4) == 0) && TNC->Hardware == H_SCS) // Using Radio Port on PTC + COMPort = 0; + else if (_memicmp(ptr, "PTCPORT", 7) == 0) + COMPort = 0; + else + COMPort = ptr; + + // See if port is already defined. We may be adding another radio (ICOM only) or updating an existing one + + // Unless CM108 - they must be on separate Ports + + if (COMPort && _stricmp("CM108", COMPort) != 0) + { + for (i = 0; i < NumberofPorts; i++) + { + PORT = PORTInfo[i]; + + if (COMPort) + if (strcmp(PORT->IOBASE, COMPort) == 0) + goto PortFound; + + if (COMPort == 0) + if (PORT->IOBASE == COMPort) + goto PortFound; + } + } + + // Allocate a new one + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + + if (COMPort) + strcpy(PORT->IOBASE, COMPort); + +PortFound: + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return (FALSE); + + if (_stricmp(PORT->IOBASE, "RAWHID") == 0) // HID Addr instead of Speed + { + DecodeCM108(Port, ptr); + if (CM108Device) + PORT->HIDDevice = CM108Device; + else + PORT->HIDDevice = _strdup ("MissingHID"); + + CM108Device = 0; + } + + if ( _stricmp(PORT->IOBASE, "CM108") == 0) // HID Addr instead of Speed + { + DecodeCM108(Port, ptr); + } + + if (_stricmp(PORT->IOBASE, "REMOTE") == 0) // IP Addr/Port + DecodeRemote(PORT, ptr); + else + PORT->SPEED = atoi(ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return (FALSE); + + if (_memicmp(ptr, "PTTCOM", 6) == 0 || _memicmp(ptr, "PTT=", 4) == 0) + { + if (_stricmp(ptr, "PTT=CM108") == 0) + { + // PTT on CM108 GPIO + + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return (FALSE); + + DecodeCM108(Port, ptr); + } + else + { + strcpy(PORT->PTTIOBASE, ptr); + } + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return (FALSE); + + } + + // if (strcmp(ptr, "ICOM") == 0 || strcmp(ptr, "YAESU") == 0 + // || strcmp(ptr, "KENWOOD") == 0 || strcmp(ptr, "PTTONLY") == 0 || strcmp(ptr, "ANTENNA") == 0) + + // RADIO IC706 4E 5 14.103/U1 14.112/u1 18.1/U1 10.12/l1 + // Read RADIO Lines + + _strupr(ptr); + + + if (strcmp(ptr, "ICOM") == 0) + PORT->PortType = ICOM; + else if (strcmp(ptr, "YAESU") == 0) + PORT->PortType = YAESU; + else if (strcmp(ptr, "KENWOOD") == 0) + PORT->PortType = KENWOOD; + else if (strcmp(ptr, "FLEX") == 0) + PORT->PortType = FLEX; + else if (strcmp(ptr, "NMEA") == 0) + PORT->PortType = NMEA; + else if (strcmp(ptr, "PTTONLY") == 0) + PORT->PortType = PTT; + else if (strcmp(ptr, "ANTENNA") == 0) + PORT->PortType = ANT; + else + return FALSE; + + Debugprintf("Port type = %d", PORT->PortType); + + _strupr(Context); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr && memcmp(ptr, "HAMLIB=", 7) == 0) + { + // HAMLIB Emulator - param is port to listen on + + if (PORT->PortType == PTT) + { + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + strcpy(RIG->RigName, PTTRigName); + + RIG->HAMLIBPORT = atoi(&ptr[7]); + ptr = strtok_s(NULL, " \t\n\r", &Context); + + } + } + + if (ptr && strcmp(ptr, "PTTMUX") == 0) + { + if (PORT->PortType == PTT) + { + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + strcpy(RIG->RigName, PTTRigName); + goto domux; // PTTONLY with PTTMUX + } + } + + + if (ptr == NULL) + { + if (PORT->PortType == PTT) + ptr = PTTRigName; + else + return FALSE; + } + + if (strlen(ptr) > 9) return FALSE; + + RigName = ptr; + + Debugprintf("Rigname = *%s*", RigName); + + // FT100 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT100") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT100; + } + + // FT990 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT990") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT990; + } + + // FT1000 seems to be different to most other YAESU types + + if (strstr(RigName, "FT1000") && PORT->PortType == YAESU) + { + PORT->PortType = FT1000; + + // Subtypes need different code. D and no suffix are same + + if (strstr(RigName, "FT1000MP")) + PORT->YaesuVariant = FT1000MP; + else + PORT->YaesuVariant = FT1000D; + } + + // FT2000 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT2000") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT2000; + } + + // FT991A seems to be different to most other YAESU types + + if (strcmp(RigName, "FT991A") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT991A; + } + + + // If ICOM, we may be adding a new Rig + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (PORT->PortType == ICOM || PORT->PortType == NMEA) + { + if (ptr == NULL) return (FALSE); + sscanf(ptr, "%x", &RigAddr); + + // See if already defined + + for (i = 0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->RigAddr == RigAddr) + goto RigFound; + } + + // Allocate a new one + + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + RIG->RigAddr = RigAddr; + +RigFound: + + ptr = strtok_s(NULL, " \t\n\r", &Context); + // if (ptr == NULL) return (FALSE); + } + else + { + // Only allows one RIG + + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + } + + RIG->RIG_DEBUG = RIG_DEBUG; + RIG->PORT = PORT; + + strcpy(RIG->RigName, RigName); + + RIG->TSMenu = 63; // Default to TS590S + + // IC735 uses shorter freq message + + if (strcmp(RigName, "IC735") == 0 && PORT->PortType == ICOM) + RIG->IC735 = TRUE; + + // IC-F8101 uses a different set of commands + + if (strstr(RigName, "F8101") && PORT->PortType == ICOM) + RIG->ICF8101 = TRUE; + + if (PORT->PortType == KENWOOD) + { + RIG->TSMenu = 63; // Default to TS590S + + if (strcmp(RigName, "TS590SG") == 0) + RIG->TSMenu = 69; + } + + if (PORT->PortType == FT991A) + RIG->TSMenu = 72; //Menu for Data/USB siwtching + +domux: + + RIG->CM108Device = CM108Device; + +CheckOtherParams: + + while (ptr) + { + if (strcmp(ptr, "PTTMUX") == 0) + { + // Ports whose RTS/DTR will be converted to CAT commands (for IC7100/IC7200 etc) + + int PIndex = 0; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + while (memcmp(ptr, "COM", 3) == 0) + { + char * tncport = strlop(ptr, '/'); + + strcpy(RIG->PTTCATPort[PIndex], &ptr[3]); + + if (tncport) + RIG->PTTCATTNC[PIndex] = TNCInfo[atoi(tncport)]; + + if (PIndex < 3) + PIndex++; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + } + if (ptr == NULL) + break; + else + continue; + } + else if (strcmp(ptr, "PTT_SETS_INPUT") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + PTTControlsInputMUX = TRUE; + + // See if following param is an PTT Off Mode + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + + if (strcmp(ptr, "MIC") == 0) + DataPTTOffMode = 0; + else if (strcmp(ptr, "AUX") == 0) + DataPTTOffMode = 1; + else if (strcmp(ptr, "MICAUX") == 0) + DataPTTOffMode = 2; + else if (RIG->ICF8101 && strcmp(ptr, "LSB") == 0) + ICF8101Mode = 0x7; + else if (RIG->ICF8101 && strcmp(ptr, "USB") == 0) + ICF8101Mode = 0x8; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD1") == 0) + ICF8101Mode = 0x18; + else if (RIG->ICF8101 && strcmp(ptr, "USBD1") == 0) + ICF8101Mode = 0x19; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD2") == 0) + ICF8101Mode = 0x20; + else if (RIG->ICF8101 && strcmp(ptr, "USBD2") == 0) + ICF8101Mode = 0x21; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD3") == 0) + ICF8101Mode = 0x22; + else if (RIG->ICF8101 && strcmp(ptr, "USBD3") == 0) + ICF8101Mode = 0x23; + else + continue; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + continue; + + } + else if (strcmp(ptr, "PTT_SETS_FREQ") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + RIG->PTTSetsFreq = TRUE; + ptr = strtok_s(NULL, " \t\n\r", &Context); + + continue; + + } + + else if (strcmp(ptr, "DATAPTT") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + DataPTT = TRUE; + } + + else if (memcmp(ptr, "PTTONHEX=", 9) == 0) + { + // Hex String to use for PTT on + + char * ptr1 = ptr; + char * ptr2 = onString ; + int i, j, len; + + ptr1 += 9; + onLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + } + + else if (memcmp(ptr, "PTTOFFHEX=", 10) == 0) + { + // Hex String to use for PTT off + + char * ptr2 = offString ; + int i, j, len; + + ptr += 10; + offLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + + } + + else if (memcmp(ptr, "HAMLIB=", 7) == 0) + { + // HAMLIB Emulator - param is port to listen on + + RIG->HAMLIBPORT = atoi(&ptr[7]); + } + + else if (memcmp(ptr, "TXOFFSET", 8) == 0) + { + RIG->txOffset = strtoll(&ptr[9], NULL, 10); + } + + else if (memcmp(ptr, "RXOFFSET", 8) == 0) + { + RIG->rxOffset = strtoll(&ptr[9], NULL, 10); + } + + else if (memcmp(ptr, "PTTOFFSET", 9) == 0) + { + RIG->pttOffset = strtoll(&ptr[10], NULL, 10); + } + + else if (memcmp(ptr, "RXERROR", 7) == 0) + { + RIG->rxError = atoi(&ptr[8]); + } + + else if (memcmp(ptr, "TXERROR", 7) == 0) + { + RIG->txError = atoi(&ptr[8]); + } + + else if (memcmp(ptr, "REPORTFREQS", 11) == 0) + { + RIG->reportFreqs = _strdup(&ptr[12]); + } + + else if (memcmp(ptr, "DEFAULTFREQ", 11) == 0) + { + RIG->defaultFreq = atoi(&ptr[12]); + } + + else if (atoi(ptr)) + break; // Not scan freq oe timeband, so see if another param + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (PORT->PortType == PTT || PORT->PortType == ANT) + return RIG; + + // Set up PTT and Poll Strings + + if (PORT->PortType == ICOM) + { + char * Poll; + Poll = &RIG->PTTOn[0]; + + if (onLen && offLen) + { + memcpy(RIG->PTTOn, onString, onLen); + RIG->PTTOnLen = onLen; + memcpy(RIG->PTTOff, offString, offLen); + RIG->PTTOffLen = offLen; + } + + else if (RIG->ICF8101) + { + // Need to send ACC PTT command (1A 37 0002), not normal ICOM IC 00 + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + *(Poll++) = 0x05; + *(Poll++) = ICF8101Mode; + *(Poll++) = 0x03; // USB Data Mode Source + *(Poll++) = 0x00; + *(Poll++) = 0x01; // Soundcard + *(Poll++) = 0xFD; + } + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1A; + *(Poll++) = 0x37; // Send/read the TX status + *(Poll++) = 0x00; + *(Poll++) = 0x02; // ACC PTT + *(Poll++) = 0xFD; + + RIG->PTTOnLen = (int)(Poll - &RIG->PTTOn[0]); + + Poll = &RIG->PTTOff[0]; + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1A; + *(Poll++) = 0x37; // Send/read the TX status + *(Poll++) = 0x00; + *(Poll++) = 0x00; // RX + *(Poll++) = 0xFD; + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + *(Poll++) = 0x05; + *(Poll++) = ICF8101Mode; + *(Poll++) = 0x03; // USB Data Mode Source + *(Poll++) = 0x00; + *(Poll++) = 0x02; // ACC + *(Poll++) = 0xFD; + } + RIG->PTTOffLen = (int)(Poll - &RIG->PTTOff[0]); + } + else + { + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + if (strcmp(RIG->RigName, "IC7100") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x91; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x24; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7300") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x67; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7600") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x31; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7610") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x92; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7410") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x39; // Data Mode Source + } + + *(Poll++) = 0x03; // USB Soundcard + *(Poll++) = 0xFD; + } + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1C; // RIG STATE + *(Poll++) = 0x00; // PTT + *(Poll++) = 1; // ON + *(Poll++) = 0xFD; + + RIG->PTTOnLen = (int)(Poll - &RIG->PTTOn[0]); + + Poll = &RIG->PTTOff[0]; + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1C; // RIG STATE + *(Poll++) = 0x00; // PTT + *(Poll++) = 0; // OFF + *(Poll++) = 0xFD; + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + if (strcmp(RIG->RigName, "IC7100") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x91; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x24; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7300") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x67; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7410") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x39; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7600") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x31; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7610") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x92; // Data1 Mode Source + } + + *(Poll++) = DataPTTOffMode; + *(Poll++) = 0xFD; + } + RIG->PTTOffLen = (int)(Poll - &RIG->PTTOff[0]); + } + } + else if (PORT->PortType == KENWOOD) + { + RIG->PollLen = 6; + strcpy(RIG->Poll, "FA;MD;"); + + if (PTTControlsInputMUX) + { + sprintf(RIG->PTTOn, "EX%03d00001;TX1;", RIG->TSMenu); // Select USB before PTT + sprintf(RIG->PTTOff, "RX;EX%03d00000;", RIG->TSMenu); // Select ACC after dropping PTT + } + else + { + strcpy(RIG->PTTOff, "RX;"); + + if (DataPTT) + strcpy(RIG->PTTOn, "TX1;"); + else + strcpy(RIG->PTTOn, "TX;"); + } + + RIG->PTTOnLen = (int)strlen(RIG->PTTOn); + RIG->PTTOffLen = (int)strlen(RIG->PTTOff); + + } + else if (PORT->PortType == FLEX) + { + RIG->PollLen = 10; + strcpy(RIG->Poll, "ZZFA;ZZMD;"); + + strcpy(RIG->PTTOn, "ZZTX1;"); + RIG->PTTOnLen = 6; + strcpy(RIG->PTTOff, "ZZTX0;"); + RIG->PTTOffLen = 6; + } + else if (PORT->PortType == FT2000) + { + RIG->PollLen = 6; + strcpy(RIG->Poll, "FA;MD;"); + + strcpy(RIG->PTTOn, "TX1;"); + RIG->PTTOnLen = 4; + strcpy(RIG->PTTOff, "TX0;"); + RIG->PTTOffLen = 4; + } + else if (PORT->PortType == FT991A) + { + RIG->PollLen = 7; + strcpy(RIG->Poll, "FA;MD0;"); + + if (PTTControlsInputMUX) + { + RIG->PTTOnLen = sprintf(RIG->PTTOn, "EX0721;TX1;"); // Select USB before PTT + RIG->PTTOffLen = sprintf(RIG->PTTOff, "TX0;EX0720;"); // Select DATA after dropping PTT + } + else + { + strcpy(RIG->PTTOn, "TX1;"); + RIG->PTTOnLen = 4; + strcpy(RIG->PTTOff, "TX0;"); + RIG->PTTOffLen = 4; + } + } + else if (PORT->PortType == NMEA) + { + int Len; + + i = sprintf(RIG->Poll, "$PICOA,90,%02x,RXF*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll); + Len = i; + i = sprintf(RIG->Poll + Len, "$PICOA,90,%02x,MODE*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll + Len); + RIG->PollLen = Len + i; + + i = sprintf(RIG->PTTOn, "$PICOA,90,%02x,TRX,TX*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->PTTOn); + RIG->PTTOnLen = i; + + i = sprintf(RIG->PTTOff, "$PICOA,90,%02x,TRX,RX*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->PTTOff); + RIG->PTTOffLen = i; + } + + if (ptr == NULL) return RIG; // No Scanning, just Interactive control + + if (strchr(ptr, ',') == 0 && strchr(ptr, ':') == 0) // Old Format + { + ScanFreq = atof(ptr); + +#pragma warning(push) +#pragma warning(disable : 4244) + + RIG->ScanFreq = ScanFreq * 10; + +#pragma warning(push) + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + // Frequency List + +CheckScan: + + if (ptr) + if (ptr[0] == ';' || ptr[0] == '#') + ptr = NULL; + + if (ptr != NULL) + { + // Create Default Timeband + + struct TimeScan * Band; + + RIG->TimeBands = zalloc(sizeof(void *)); + + Band = AllocateTimeRec(RIG); + SaveBand = Band; + + Band->Start = 0; + Band->End = 84540; //23:59 + FreqPtr = Band->Scanlist = RIG->FreqPtr = malloc(1000); + memset(FreqPtr, 0, 1000); + } + + while(ptr) + { + int ModeNo; + BOOL Supress; + double Freq = 0.0; + int FreqInt = 0; + char FreqString[80]=""; + char * Modeptr = NULL; + char Split, Data, PacketMode, RPacketMode, PMinLevel, PMaxLevel, Filter; + char Mode[10] = ""; + char WinmorMode, Antenna; + char ARDOPMode[6] = ""; + char VARAMode[6] = ""; + char Appl[13]; + char * ApplCall; + int BandWidth; + int Power; + + if (ptr[0] == ';' || ptr[0] == '#') + break; + + Filter = PMinLevel = PMaxLevel = PacketMode = RPacketMode = Split = + Data = WinmorMode = Antenna = ModeNo = Supress = + MemoryBank = MemoryNumber = BandWidth = 0; + + Appl[0] = 0; + ARDOPMode[0] = 0; + VARAMode[0] = 0; + Dwell = 0.0; + + while (strchr(ptr, ':')) + { + // New TimeBand + + struct TimeScan * Band; + + Band = AllocateTimeRec(RIG); + + *FreqPtr = (struct ScanEntry *)0; // Terminate Last Band + + Band->Start = (atoi(ptr) * 3600) + (atoi(&ptr[3]) * 60); + Band->End = 84540; //23:59 + SaveBand->End = Band->Start - 60; + + SaveBand = Band; + + FreqPtr = Band->Scanlist = RIG->FreqPtr = malloc(1000); + memset(FreqPtr, 0, 1000); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (strchr(ptr, ',')) // New Format + { + DecodeModePtr(ptr, &Dwell, &Freq, Mode, &PMinLevel, &PMaxLevel, &PacketMode, + &RPacketMode, &Split, &Data, &WinmorMode, &Antenna, &Supress, &Filter, &Appl[0], + &MemoryBank, &MemoryNumber, ARDOPMode, VARAMode, &BandWidth, &Power); + } + else + { + Modeptr = strchr(ptr, '/'); + + if (Modeptr) + *Modeptr++ = 0; + + Freq = atof(ptr); + + if (Modeptr) + { + // Mode can include 1/2/3 for Icom Filers. W/N for Winmor/Pactor Bandwidth, and +/-/S for Repeater Shift (S = Simplex) + // First is always Mode + // First char is Mode (USB, LSB etc) + + Mode[0] = Modeptr[0]; + Filter = Modeptr[1]; + + if (strchr(&Modeptr[1], '+')) + Split = '+'; + else if (strchr(&Modeptr[1], '-')) + Split = '-'; + else if (strchr(&Modeptr[1], 'S')) + Split = 'S'; + else if (strchr(&Modeptr[1], 'D')) + Data = 1; + + if (strchr(&Modeptr[1], 'W')) + { + WinmorMode = 'W'; + PMaxLevel = '3'; + PMinLevel = '1'; + } + else if (strchr(&Modeptr[1], 'N')) + { + WinmorMode = 'N'; + PMaxLevel = '2'; + PMinLevel = '1'; + } + + if (strchr(&Modeptr[1], 'R')) // Robust Packet + RPacketMode = '2'; // R600 + else if (strchr(&Modeptr[1], 'H')) // HF Packet on Tracker + PacketMode = '1'; // 300 + + if (strchr(&Modeptr[1], 'X')) // Dont Report to WL2K + Supress = 1; + + if (strstr(&Modeptr[1], "A1")) + Antenna = '1'; + else if (strstr(&Modeptr[1], "A2")) + Antenna = '2'; + else if (strstr(&Modeptr[1], "A3")) + Antenna = '3'; + else if (strstr(&Modeptr[1], "A4")) + Antenna = '4'; + else if (strstr(&Modeptr[1], "A5")) + Antenna = '5'; + else if (strstr(&Modeptr[1], "A6")) + Antenna = '6'; + } + } + + switch(PORT->PortType) + { + case ICOM: + + for (ModeNo = 0; ModeNo < 24; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case YAESU: + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (YaesuModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(YaesuModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case KENWOOD: + + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (KenwoodModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FLEX: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FLEXModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FLEXModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT2000: + + if (Modeptr) + { + if (strstr(Modeptr, "PL")) + { + ModeNo = 8; + break; + } + if (strstr(Modeptr, "PU")) + { + ModeNo = 12; + break; + } + } + for (ModeNo = 0; ModeNo < 14; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT2000Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT2000Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT991A: + +/* if (Modeptr) + { + if (strstr(Modeptr, "PL")) + { + ModeNo = 8; + break; + } + if (strstr(Modeptr, "PU")) + { + ModeNo = 12; + break; + } + } +*/ + for (ModeNo = 0; ModeNo < 15; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT991AModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT991AModes[ModeNo], Mode) == 0) + break; + } + } + break; + + + case FT100: + + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT100Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT100Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT990: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT990Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT990Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT1000: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT1000Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT1000Modes[ModeNo], Mode) == 0) + break; + } + } + break; + } + + Freq = Freq * 1000000.0; + + sprintf(FreqString, "%09.0f", Freq); + + FreqInt = Freq; + + FreqPtr[0] = malloc(sizeof(struct ScanEntry)); + memset(FreqPtr[0], 0, sizeof(struct ScanEntry)); + +#pragma warning(push) +#pragma warning(disable : 4244) + + if (Dwell == 0.0) + FreqPtr[0]->Dwell = ScanFreq * 10; + else + FreqPtr[0]->Dwell = Dwell * 10; + +#pragma warning(pop) + + FreqPtr[0]->Freq = Freq; + FreqPtr[0]->Bandwidth = WinmorMode; + FreqPtr[0]->RPacketMode = RPacketMode; + FreqPtr[0]->HFPacketMode = PacketMode; + FreqPtr[0]->PMaxLevel = PMaxLevel; + FreqPtr[0]->PMinLevel = PMinLevel; + FreqPtr[0]->Antenna = Antenna; + strcpy(FreqPtr[0]->ARDOPMode, ARDOPMode); + FreqPtr[0]->VARAMode = VARAMode[0]; + + strcpy(FreqPtr[0]->APPL, Appl); + + ApplCall = GetApplCallFromName(Appl); + + if (strcmp(Appl, "NODE") == 0) + { + memcpy(FreqPtr[0]->APPLCALL, TNC->NodeCall, 9); + strlop(FreqPtr[0]->APPLCALL, ' '); + } + else + { + if (ApplCall && ApplCall[0] > 32) + { + memcpy(FreqPtr[0]->APPLCALL, ApplCall, 9); + strlop(FreqPtr[0]->APPLCALL, ' '); + } + } + + CmdPtr = FreqPtr[0]->Cmd1 = malloc(100); + + if (PORT->PortType == ICOM) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + if (RIG->ICF8101) + { + // Set Freq is 1A 35 and set Mode 1A 36 + + + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x35; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 12; + + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 9; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x36; // Set mode command + *(CmdPtr++) = 0; + if (ModeNo > 10) + *(CmdPtr++) = ModeNo + 6; + else + *(CmdPtr++) = ModeNo; + + *(CmdPtr++) = 0xFD; + } + else + { + if (MemoryNumber) + { + // Set Memory Channel instead of Freq, Mode, etc + + char ChanString[5]; + + // Send Set Memory, then Channel + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xFD; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + sprintf(ChanString, "%04d", MemoryNumber); + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = (ChanString[1] - 48) | ((ChanString[0] - 48) << 4); + *(CmdPtr++) = (ChanString[3] - 48) | ((ChanString[2] - 48) << 4); + *(CmdPtr++) = 0xFD; + + FreqPtr[0]->Cmd1Len = 14; + + if (MemoryBank) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xA0; + *(CmdPtr++) = MemoryBank - 0x40; + *(CmdPtr++) = 0xFD; + + FreqPtr[0]->Cmd1Len += 8; + } + } + else + { + *(CmdPtr++) = 0x5; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 10; + } + else + { + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 11; + } + + // Send Set VFO in case last chan was memory + + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = RIG->RigAddr; + // *(CmdPtr++) = 0xE0; + + // *(CmdPtr++) = 0x07; + // *(CmdPtr++) = 0xFD; + + // FreqPtr[0]->Cmd1Len = 17; + + if (Filter) + { + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 8; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x6; // Set Mode + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = Filter - '0'; //Filter + *(CmdPtr++) = 0xFD; + + if (Split) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + FreqPtr[0]->Cmd3Len = 7; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0xF; // Set Mode + if (Split == 'S') + *(CmdPtr++) = 0x10; + else + if (Split == '+') + *(CmdPtr++) = 0x12; + else + if (Split == '-') + *(CmdPtr++) = 0x11; + + *(CmdPtr++) = 0xFD; + } + else + { + if (Data) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1a; + + + if ((strcmp(RIG->RigName, "IC7100") == 0) || + (strcmp(RIG->RigName, "IC7410") == 0) || + (strcmp(RIG->RigName, "IC7600") == 0) || + (strcmp(RIG->RigName, "IC7610") == 0) || + (strcmp(RIG->RigName, "IC7300") == 0)) + { + FreqPtr[0]->Cmd3Len = 9; + *(CmdPtr++) = 0x6; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter - '0'; //Filter + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + FreqPtr[0]->Cmd3Len = 9; + *(CmdPtr++) = 0x4; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter - '0'; //Filter + } + else + { + FreqPtr[0]->Cmd3Len = 8; + *(CmdPtr++) = 0x6; // Set Data + *(CmdPtr++) = 0x1; //On + } + + *(CmdPtr++) = 0xFD; + } + } + } + + if (Antenna == '5' || Antenna == '6') + { + // Antenna select for 746 and maybe others + + // Could be going in cmd2 3 or 4 + + if (FreqPtr[0]->Cmd2 == NULL) + { + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 7; + } + else if (FreqPtr[0]->Cmd3 == NULL) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + FreqPtr[0]->Cmd3Len = 7; + } + else + { + CmdPtr = FreqPtr[0]->Cmd4 = malloc(10); + FreqPtr[0]->Cmd4Len = 7; + } + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x12; // Set Antenna + *(CmdPtr++) = Antenna - '5'; // 0 for A5 1 for A6 + *(CmdPtr++) = 0xFD; + } + } + } + } + else if (PORT->PortType == YAESU) + { + //Send Mode first - changing mode can change freq + + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 7; + + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = 1; + + // FT847 Needs a Poll Here. Set up anyway, but only send if 847 + + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 3; + + + } + else if (PORT->PortType == KENWOOD) + { + if (Power == 0) + { + if (Antenna == '5' || Antenna == '6') + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;AN%c;FA;MD;", FreqString, ModeNo, Antenna - 4); + else + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;FA;MD;", FreqString, ModeNo); + } + else + { + if (Antenna == '5' || Antenna == '6') + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;AN%c;PC%03d;FA;MD;PC;", FreqString, ModeNo, Antenna - 4, Power); + else + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;PC%03d;FA;MD;PC;", FreqString, ModeNo, Power); + } + } + else if (PORT->PortType == FLEX) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "ZZFA00%s;ZZMD%02d;ZZFA;ZZMD;", FreqString, ModeNo); + } + else if (PORT->PortType == FT2000) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA%s;MD0%X;FA;MD;", &FreqString[1], ModeNo); + } + else if (PORT->PortType == FT991A) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA%s;MD0%X;FA;MD0;", &FreqString, ModeNo); + } + else if (PORT->PortType == FT100 || PORT->PortType == FT990 + || PORT->PortType == FT1000) + { + // Allow Mode = "LEAVE" to suppress mode change + + //Send Mode first - changing mode can change freq + + if (strcmp(Mode, "LEAVE") == 0) + { + // we can't easily change the string length, + // so just set freq twice + + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = 10; + } + else + { + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 12; + } + + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = 10; + + // Send Get Status, as these types doesn't ack commands + + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + + if (PORT->PortType == FT990 || PORT->YaesuVariant == FT1000D) + *(CmdPtr++) = 3; + else + *(CmdPtr++) = 2; // F100 or FT1000MP + + *(CmdPtr++) = 16; // Get Status + } + else if (PORT->PortType == NMEA) + { + int Len; + + i = sprintf(CmdPtr, "$PICOA,90,%02x,RXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(CmdPtr); + Len = i; + i = sprintf(CmdPtr + Len, "$PICOA,90,%02x,TXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(CmdPtr + Len); + Len += i; + i = sprintf(CmdPtr + Len, "$PICOA,90,%02x,MODE,%s*xx\r\n", RIG->RigAddr, Mode); + AddNMEAChecksum(CmdPtr + Len); + FreqPtr[0]->Cmd1Len = Len + i; + + i = sprintf(RIG->Poll, "$PICOA,90,%02x,RXF*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll); + Len = i; + i = sprintf(RIG->Poll + Len, "$PICOA,90,%02x,MODE*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll + Len); + RIG->PollLen = Len + i; + } + else if (PORT->PortType == HAMLIB) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "F %s\n+f\nM %s %d\n+m\n", FreqString, Mode, BandWidth); + } + + else if (PORT->PortType == FLRIG) + { + sprintf(FreqPtr[0]->Cmd1Msg, "%.0f", Freq); + sprintf(FreqPtr[0]->Cmd2Msg, "%s", Mode); + sprintf(FreqPtr[0]->Cmd3Msg, "%d", BandWidth); + } + + else if (PORT->PortType == RTLUDP) + { + int FM = 0; + int AM = 1; + int USB = 2; + int LSB = 3; + + CmdPtr[0] = 0; + memcpy(&CmdPtr[1], &FreqInt, 4); + + CmdPtr[1] = FreqInt & 0xff; + CmdPtr[2] = (FreqInt >> 8) & 0xff; + CmdPtr[3] = (FreqInt >> 16) & 0xff; + CmdPtr[4] = (FreqInt >> 24) & 0xff; + + FreqPtr[0]->Cmd1Len = 5; + + if (Mode[0]) + { + CmdPtr[5] = 1; + FreqPtr[0]->Cmd1Len = 10; + + if (strcmp(Mode, "FM") == 0) + memcpy(&CmdPtr[6], &FM, 4); + else if (strcmp(Mode, "AM") == 0) + memcpy(&CmdPtr[6], &AM, 4); + else if (strcmp(Mode, "USB") == 0) + memcpy(&CmdPtr[6], &USB, 4); + else if (strcmp(Mode, "LSB") == 0) + memcpy(&CmdPtr[6], &LSB, 4); + } + + } + + FreqPtr++; + + RIG->ScanCounter = 20; + + ptr = strtok_s(NULL, " \t\n\r", &Context); // Next Freq + } + + + + if (RIG->NumberofBands) + { + CheckTimeBands(RIG); // Set initial FreqPtr; + PORT->FreqPtr = RIG->FreqPtr[0]; + } + + return RIG; +} + +VOID SetupScanInterLockGroups(struct RIGINFO *RIG) +{ + struct PORTCONTROL * PortRecord; + struct TNCINFO * TNC; + int port; + int Interlock = RIG->Interlock; + char PortString[128] = ""; + char TxPortString[128] = ""; + + // Find TNC ports in this Rig's scan group + + for (port = 1; port < 33; port++) + { + TNC = TNCInfo[port]; + + if (TNC == NULL) + continue; + + PortRecord = &TNC->PortRecord->PORTCONTROL; + + if (TNC->RXRadio == Interlock) + { + int p = PortRecord->PORTNUMBER; + RIG->BPQPort |= (1 << p); + sprintf(PortString, "%s,%d", PortString, p); + TNC->RIG = RIG; + + if (RIG->PTTMode == 0 && TNC->PTTMode) + RIG->PTTMode = TNC->PTTMode; + } + if (TNC->TXRadio == Interlock && TNC->TXRadio != TNC->RXRadio) + { + int p = PortRecord->PORTNUMBER; + RIG->BPQPort |= (1 << p); + sprintf(TxPortString, "%s,%d", TxPortString, p); + TNC->TXRIG = RIG; + + if (RIG->PTTMode == 0 && TNC->PTTMode) + RIG->PTTMode = TNC->PTTMode; + } + } + + if (RIG->PTTMode == 0 && (RIG->PTTCATPort[0] || RIG->HAMLIBPORT)) // PTT Mux Implies CAT + RIG->PTTMode = PTTCI_V; + + if (PortString[0] && TxPortString[0]) // Have both + sprintf(RIG->WEB_PORTS, "Rx: %s Tx: %s", &PortString[1], &TxPortString[1]); + else if (PortString[0]) + strcpy(RIG->WEB_PORTS, &PortString[1]); + else if (TxPortString[0]) + sprintf(RIG->WEB_PORTS, "Tx: %s", &TxPortString[1]); + + SetWindowText(RIG->hPORTS, RIG->WEB_PORTS); +} + +VOID SetupPortRIGPointers() +{ + struct TNCINFO * TNC; + int port; + + for (port = 1; port < 33; port++) + { + TNC = TNCInfo[port]; + + if (TNC == NULL) + continue; + + if (TNC->RIG == NULL) + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + } +} + +#ifdef WIN32 + +VOID PTTCATThread(struct RIGINFO *RIG) +{ + DWORD dwLength = 0; + int Length, ret, i; + UCHAR * ptr1; + UCHAR * ptr2; + UCHAR c; + UCHAR Block[4][80]; + UCHAR CurrentState[4] = {0}; +#define RTS 2 +#define DTR 4 + HANDLE Event; + HANDLE Handle[4]; + DWORD EvtMask[4]; + OVERLAPPED Overlapped[4]; + char Port[32]; + int PIndex = 0; + int HIndex = 0; + int rc; + + EndPTTCATThread = FALSE; + + while (PIndex < 4 && RIG->PTTCATPort[PIndex][0]) + { + RIG->RealMux[HIndex] = 0; + + sprintf(Port, "\\\\.\\pipe\\BPQCOM%s", RIG->PTTCATPort[PIndex]); + + Handle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (Handle[HIndex] == (HANDLE) -1) + { + int Err = GetLastError(); + Consoleprintf("PTTMUX port BPQCOM%s Open failed code %d - trying real com port", RIG->PTTCATPort[PIndex], Err); + + // See if real com port + + sprintf(Port, "\\\\.\\\\COM%s", RIG->PTTCATPort[PIndex]); + + Handle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + RIG->RealMux[HIndex] = 1; + + if (Handle[HIndex] == (HANDLE) -1) + { + int Err = GetLastError(); + Consoleprintf("PTTMUX port COM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); + } + else + { + rc = SetCommMask(Handle[HIndex], EV_CTS | EV_DSR); // Request notifications + HIndex++; + } + } + else + HIndex++; + + PIndex++; + + } + + if (PIndex == 0) + return; // No ports + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + for (i = 0; i < HIndex; i ++) + { + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + if (RIG->RealMux[i]) + { + // Request Interface change notifications + + rc = WaitCommEvent(Handle[i], &EvtMask[i], &Overlapped[i]); + rc = GetLastError(); + + } + else + { + + // Prime a read on each handle + + ReadFile(Handle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + + while (EndPTTCATThread == FALSE) + { + +WaitAgain: + + ret = WaitForSingleObject(Event, 1000); + + if (ret == WAIT_TIMEOUT) + { + if (EndPTTCATThread) + { + for (i = 0; i < HIndex; i ++) + { + CancelIo(Handle[i]); + CloseHandle(Handle[i]); + Handle[i] = INVALID_HANDLE_VALUE; + } + CloseHandle(Event); + return; + } + goto WaitAgain; + } + + ResetEvent(Event); + + // See which request(s) have completed + + for (i = 0; i < HIndex; i ++) + { + ret = GetOverlappedResult(Handle[i], &Overlapped[i], &Length, FALSE); + + if (ret) + { + if (RIG->RealMux[i]) + { + // Request Interface change notifications + + DWORD Mask; + + GetCommModemStatus(Handle[i], &Mask); + + if (Mask & MS_CTS_ON) + Rig_PTTEx(RIG, TRUE, RIG->PTTCATTNC[i]); + else + Rig_PTTEx(RIG, FALSE, RIG->PTTCATTNC[i]); + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + WaitCommEvent(Handle[i], &EvtMask[i], &Overlapped[i]); + + } + else + { + + ptr1 = Block[i]; + ptr2 = Block[i]; + + while (Length > 0) + { + c = *(ptr1++); + + Length--; + + if (c == 0xff) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + Length--; + } + else + { + // This is connection / RTS/DTR statua from other end + // Convert to CAT Command + + if (c == CurrentState[i]) + continue; + + if (c & RTS) + Rig_PTTEx(RIG, TRUE, RIG->PTTCATTNC[i]); + else + Rig_PTTEx(RIG, FALSE, RIG->PTTCATTNC[i]); + + CurrentState[i] = c; + continue; + } + } + } + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + ReadFile(Handle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + } + } + EndPTTCATThread = FALSE; +} + +/* + memset(&Overlapped, 0, sizeof(Overlapped)); + Overlapped.hEvent = Event; + ResetEvent(Event); + + ret = ReadFile(Handle, Block, 80, &Length, &Overlapped); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret != ERROR_IO_PENDING) + { + if (ret == ERROR_BROKEN_PIPE || ret == ERROR_INVALID_HANDLE) + { + CloseHandle(Handle); + RIG->PTTCATHandles[0] = INVALID_HANDLE_VALUE; + return; + } + } + } +*/ +#endif + +// HAMLIB Support Code + +VOID HAMLIBPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on HAMLIB + char cmd[80]; + int len; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + send(PORT->remoteSock, PORT->TXBuffer, PORT->TXLen, 0); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + len = sprintf(cmd, "+f\n+m\n"); + + send(PORT->remoteSock, cmd, len, 0); + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + + +void HAMLIBProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 500 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; +} + +void FLRIGProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 500 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; + ProcessFLRIGFrame(PORT); +} + +void ProcessHAMLIBFrame(struct RIGPORTINFO * PORT, int Length) +{ + char * msg = PORT->RXBuffer; + char * rest; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + msg[Length] = 0; + + PORT->Timeout = 0; + RIG->RIGOK = 1; + + // extract lines from input + + while (msg && msg[0]) + { + rest = strlop(msg, 10); + + if (memcmp(msg, "Frequency:", 10) == 0) + { + RIG->RigFreq = atof(&msg[11]) / 1000000.0; + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + else if (memcmp(msg, "Mode:", 5) == 0) + { + if (strlen(&msg[6]) < 15) + strcpy(RIG->ModeString, &msg[6]); + } + + else if (memcmp(msg, "Passband:", 9) == 0) + { + RIG->Passband = atoi(&msg[10]); + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, RIG->Passband); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + + msg = rest; + } +} + + +void ProcessFLRIGFrame(struct RIGPORTINFO * PORT) +{ + char * msg; + int Length; + + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one + char * ptr1, * ptr2, * val; + int Len, TotalLen; + char cmd[80]; + char ReqBuf[256]; + char SendBuff[256]; + + while (PORT->RXLen > 0) + { + int b1 = 0, b2 = 0; + + msg = PORT->RXBuffer; + Length = PORT->RXLen; + + msg[Length] = 0; + ptr1 = strstr(msg, "Content-length:"); + + if (ptr1 == NULL) + return; + + Len = atoi(&ptr1[15]); + ptr2 = strstr(ptr1, "\r\n\r\n"); + if (ptr2) + { + TotalLen = ptr2 +4 + Len - msg; + + if (TotalLen > Length) // Don't have it all + return; + } + else + return; + + val = strstr(ptr2, ""); + + if (val) + { + val += 7; + + RIG->RIGOK = 1; + PORT->RXLen -= TotalLen; + + memmove(PORT->RXBuffer, &PORT->RXBuffer[TotalLen], PORT->RXLen); + + // It is quite difficult to corrolate responses with commands, but we only poll for freq, mode and bandwidth + // and the responses can be easily identified + + if (strstr(val, "") || memcmp(val, "", 8) == 0) + { + // Reply to set command - may need to send next in set or use to send OK if interactive + + if (PORT->ScanEntry.Cmd2Msg[0]) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + PORT->ScanEntry.Cmd2Msg[0] = 0; + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + PORT->ScanEntry.Cmd3Msg[0] = 0; + } + + else if (!PORT->AutoPoll) + { + GetSemaphore(&Semaphore, 61); + SendResponse(RIG->Session, "Set OK"); + FreeSemaphore(&Semaphore); + PORT->AutoPoll = 1; // So we dond send another + } + } + else if(strstr(val, "")) + { + // Reply to get BW + + char * p1, * p2; + + p1 = strstr(val, ""); + + if (p1) + { + p1 += 7; + b1 = atoi(p1); + + p2 = strstr(p1, ""); + + if (p2) + { + p2 +=7; + b2 = atoi(p2); + } + } + + if (b1) + { + if (b2) + sprintf(RIG->WEB_MODE, "%s/%d:%d", RIG->ModeString, b1, b2); + else + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, b1); + + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + } + else + { + // Either freq or mode. See if numeric + + double freq; + + strlop(val, '<'); + + freq = atof(val) / 1000000.0; + + if (freq > 0.0) + { + RIG->RigFreq = freq; + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + // Read Mode + + Len = sprintf(ReqBuf, Req, "rig.get_mode", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + + } + else + { + if (strlen(val)> 1) + { + strcpy(RIG->ModeString, val); + + if (b1) + { + if (b2) + sprintf(RIG->WEB_MODE, "%s/%d:%d", RIG->ModeString, b1, b2); + else + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, b1); + } + else + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + } + } + } + + + + // We now send all setting commands at once + + /* + + + if (memcmp(PORT->TXBuffer, "rig.set_vfo", 11) == 0) + { + // Set Freq - Send Set Mode if needed + + if (PORT->ScanEntry.Cmd2Msg[0]) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + if (strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } + strcpy(RIG->ModeString, PORT->ScanEntry.Cmd2Msg); + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Freq OK"); + + strcpy(PORT->TXBuffer, "rig.get_vfo"); + Len = sprintf(ReqBuf, Req, "rig.get_vfo", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + strcpy(PORT->TXBuffer, "rig.get_bw"); + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + } + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.set_mode", 11) == 0) + { + strcpy(PORT->TXBuffer, "rig.get_vfo"); + Len = sprintf(ReqBuf, Req, "rig.get_vfo", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Freq and Mode OK"); + + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.get_vfo", 11) == 0) + { + RIG->RigFreq = atof(val) / 1000000.0; + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + strcpy(PORT->TXBuffer, "rig.get_mode"); + Len = sprintf(ReqBuf, Req, "rig.get_mode", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.get_mode", 11) == 0) + { + strlop(val, '<'); + sprintf(RIG->WEB_MODE, "%s", val); + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + } + + */ + } + + PORT->Timeout = 0; + } + + /* + POST /RPC2 HTTP/1.1 + User-Agent: XMLRPC++ 0.8 + Host: 127.0.0.1:12345 + Content-type: text/xml + Content-length: 89 + + + rig.get_vfoA + + HTTP/1.1 200 OK + Server: XMLRPC++ 0.8 + Content-Type: text/xml + Content-length: 118 + + + + 14070000 + + */ + + +} + + + + + +void HLSetMode(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + char mode[80] = ""; + int filter = 0; + int n = sscanf(&Msg[1], "%s %d", mode, &filter); + + // Send to RIGCommand. Can't set Mode without Freq so need to use current + + // ?? Should be try to convert bandwidth to filter ?? + + RIG->Passband = filter; + + if (RIG->PORT->PortType == ICOM) // Needs a Filter + sprintf(Resp, "%d %s %s 1\n", 0, RIG->Valchar, mode); + else + sprintf(Resp, "%d %s %s\n", 0, RIG->Valchar, mode); + + GetSemaphore(&Semaphore, 60); + Rig_CommandEx(RIG->PORT, RIG, -1, Resp); + FreeSemaphore(&Semaphore); + + if (sep) + Len = sprintf(Resp, "set_mode: %s %d%cRPRT 0\n", mode, filter, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + + +void HLSetFreq(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + int freq = atoi(&Msg[1]); + + // Send to RIGCommand + + sprintf(Resp, "%d %f\n", 0, freq/1000000.0); + GetSemaphore(&Semaphore, 60); + Rig_CommandEx(RIG->PORT, RIG, -1, Resp); + FreeSemaphore(&Semaphore); + + if (sep) + Len = sprintf(Resp, "set_freq: %d%cRPRT 0\n", freq, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + + +void HLGetPTT(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + int ptt = 0; + + if (RIG->PTTTimer) + ptt = 1; + + if (sep) + Len = sprintf(Resp, "get_ptt:%cPTT: %d%cRPRT 0\n", sep, ptt, sep); + else + Len = sprintf(Resp, "%d\n", ptt); + + send(Sock, Resp, Len, 0); +} + +void HLSetPTT(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + int ptt = atoi(&Msg[1]); + + if (ptt) + Rig_PTTEx(RIG, 1, NULL); + else + Rig_PTTEx(RIG, 0, NULL); + + if (sep) + Len = sprintf(Resp, "set_ptt: %d%cRPRT 0\n", ptt, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + +void HLGetMode(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_mode:%cMode: %s%cPassband: %d%cRPRT 0\n", sep, RIG->ModeString, sep, RIG->Passband, sep); + else + Len = sprintf(Resp, "%s\n%d\n", RIG->ModeString, RIG->Passband); + + send(Sock, Resp, Len, 0); + +} + +void HLGetFreq(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + char freqval[64]; + double freq = atof(RIG->Valchar) * 1000000.0; + + sprintf(freqval, "%f", freq); + strlop(freqval, '.'); + + if (sep) + Len = sprintf(Resp, "get_freq:%cFrequency: %s%cRPRT 0\n", sep, freqval, sep); + else + Len = sprintf(Resp, "%s\n", freqval); + + send(Sock, Resp, Len, 0); +} + +void HLGetVFO(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_vfo:%s%cRPRT 0\n", "VFOA", sep); + else + Len = sprintf(Resp, "%s\n", "VFOA"); + + send(Sock, Resp, Len, 0); +} + +void HLGetSplit(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_vfo:%s%cRPRT 0\n", "VFOA", sep); + else + Len = sprintf(Resp, "0\n%s\n", "VFOA"); + + send(Sock, Resp, Len, 0); + +} + + + +int ProcessHAMLIBSlaveMessage(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, int MsgLen) +{ + // We only process a pretty small subset of rigctl messages + + // commands are generally a single character, upper case for set + // and lower case for get. If preceeded by + ; | or , response will + // be in extended form. + adds an LF between field, other values the + // supplied char is used as seperator. + + // At the moments we support freq (F) mode (m) and PTT (T) + + char sep = 0; + + if (Msg[0] == '#') + return 0; // Comment + + strlop(Msg, 13); + strlop(Msg, 10); + + // \ on front is used for long mode. Hopefully not used much + + // WSJT seems ro need \chk_vfo and \dump_state + + if (Msg[0] == '\\') + { + if (strcmp(&Msg[1], "chk_vfo") == 0) + { + char Reply[80]; + int Len = sprintf(Reply, "CHKVFO 0\n"); + send(Sock, Reply, Len, 0); + return 0; + } + + if (strcmp(&Msg[1], "dump_state") == 0) + { + char Reply[4096]; + int Len = sprintf(Reply, + "0\n" + "1\n" + "2\n" + "150000.000000 1500000000.000000 0x1ff -1 -1 0x10000003 0x3\n" + "0 0 0 0 0 0 0\n" + "0 0 0 0 0 0 0\n" + "0x1ff 1\n" + "0x1ff 0\n" + "0 0\n" + "0x1e 2400\n" + "0x2 500\n" + "0x1 8000\n" + "0x1 2400\n" + "0x20 15000\n" + "0x20 8000\n" + "0x40 230000\n" + "0 0\n" + "9990\n" + "9990\n" + "10000\n" + "0\n" + "10 \n" + "10 20 30 \n" + "0xffffffff\n" + "0xffffffff\n" + "0xf7ffffff\n" + "0x83ffffff\n" + "0xffffffff\n" + "0xffffffbf\n"); + + send(Sock, Reply, Len, 0); + return 0; + } + } + + if (Msg[0] == 'q' || Msg[0] == 'Q') + { + // close connection + + return 1; + } + + if (Msg[0] == '+') + { + sep = 10; + Msg++; + MsgLen --; + } + else if (Msg[0] == '_' || Msg[0] == '?') + { + } + else if (ispunct(Msg[0])) + { + sep = Msg[0]; + Msg++; + MsgLen --; + } + + switch (Msg[0]) + { + case 'f': // Get Freqency + + HLGetFreq(Sock, RIG, sep); + return 0; + + case 'm': // Get Mode + + HLGetMode(Sock, RIG, sep); + return 0; + + case 't': // Get PTT + + HLGetPTT(Sock, RIG, sep); + return 0; + + case 'v': // Get VFO + + HLGetVFO(Sock, RIG, sep); + return 0; + + case 's': // Get Split + + HLGetSplit(Sock, RIG, sep); + return 0; + + case 'F': + + HLSetFreq(Sock, RIG, Msg, sep); + return 0; + + case 'M': + + HLSetMode(Sock, RIG, Msg, sep); + return 0; + + case 'T': + + HLSetPTT(Sock, RIG, Msg, sep); + return 0; + } + return 0; +} + +int DecodeHAMLIBAddr(struct RIGPORTINFO * PORT, char * ptr) +{ + // Param is IPADDR:PORT. Only Allow numeric addresses + + struct sockaddr_in * destaddr = (SOCKADDR_IN *)&PORT->remoteDest; + + char * port = strlop(ptr, ':'); + + if (port == NULL) + return 0; + + destaddr->sin_family = AF_INET; + destaddr->sin_addr.s_addr = inet_addr(ptr); + destaddr->sin_port = htons(atoi(port)); + + return 1; +} + +VOID HAMLIBThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT) +{ + if (HAMLIBMasterRunning) + _beginthread(HAMLIBThread, 0, (void *)PORT); + + return ; +} + +VOID HAMLIBThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for HAMLIB socket - error code = %d Port %d\r\n", + err, htons(destaddr->sin_port)); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (HAMLIBMasterRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + HAMLIBProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "HAMLIB Connection lost for Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + + + continue; + } + else + { + } + } + sprintf(Msg, "HAMLIB Thread Terminated Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + + + +void HAMLIBSlaveThread(struct RIGINFO * RIG) +{ + // Wait for connections and messages from HAMLIB Clients + + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + int ret; + unsigned int maxsock; + + HAMLIBSlaveRunning = 1; + + Consoleprintf("HAMLIB Slave Thread %d Running", RIG->HAMLIBPORT); + + while (HAMLIBSlaveRunning) + { + struct HAMLIBSOCK * Entry = RIG->Sockets; + struct HAMLIBSOCK * Prev; + + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(RIG->ListenSocket, &readfs); + FD_SET(RIG->ListenSocket, &errorfs); + + maxsock = RIG->ListenSocket; + + while (Entry && HAMLIBSlaveRunning) + { + FD_SET(Entry->Sock, &readfs); + FD_SET(Entry->Sock, &errorfs); + + if (Entry->Sock > maxsock) + maxsock = Entry->Sock; + + Entry = Entry->Next; + } + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select(maxsock + 1, &readfs, NULL, &errorfs, &timeout); + + if (HAMLIBSlaveRunning == 0) + return; + + if (ret == -1) + { + perror("listen select"); + continue; + } + + if (ret) + { + if (FD_ISSET(RIG->ListenSocket, &readfs)) + { + // Connection. Accept it and create a socket enty + + int addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 sin6; + struct HAMLIBSOCK * Entry = zalloc(sizeof(struct HAMLIBSOCK)); + + Entry->Sock = accept(RIG->ListenSocket, (struct sockaddr *)&sin6, &addrlen); + + if (RIG->Sockets) + Entry->Next = RIG->Sockets; + + RIG->Sockets = Entry; + } + + // See if any Data Sockets + + Entry = RIG->Sockets; + Prev = 0; + + while (Entry) + { + unsigned char MsgBuf[256]; + unsigned char * Msg = MsgBuf; + int MsgLen; + + if (FD_ISSET(Entry->Sock, &readfs)) + { + MsgLen = recv(Entry->Sock, Msg, 256, 0); + + if (MsgLen <= 0) + { + // Closed - close socket and remove from chain + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + else + { + // Could have multiple messages in packet + // Terminator can be CR LF or CRLF + + char * ptr; + int Len; + + Msg[MsgLen] = 0; +Loop: + ptr = strlop(Msg, 10); + if (ptr == NULL) + strlop(Msg, 13); + + Len = strlen(Msg); + + if (ProcessHAMLIBSlaveMessage(Entry->Sock, RIG, Msg, Len) == 1) + { + // close request + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + Msg = ptr; + + if (Msg) + { + while (Msg[0] == 10 || Msg[0] == 13) + Msg++; + + if (Msg[0]) + goto Loop; + } + } + } + + if (FD_ISSET(Entry->Sock, &errorfs)) + { + // Closed - close socket and remove from chai + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + + // Check Next Client + + Prev = Entry; + Entry = Entry->Next; + } + } + } + Consoleprintf("HAMLIB Slave Thread %d Exited", RIG->HAMLIBPORT); +} + + +VOID FLRIGPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + int Len; + char ReqBuf[256]; + char SendBuff[256]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + char cmd[80]; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + // Send the Set Freq here, send set mode when we get a response + + memcpy(&PORT->ScanEntry, PORT->FreqPtr, sizeof(struct ScanEntry)); + + sprintf(cmd, "%s", PORT->FreqPtr->Cmd1Msg); + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 10; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + strcpy(Poll, "rig.get_vfo"); + + Len = sprintf(ReqBuf, Req, Poll, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + } + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID FLRIGSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + char ValueString[256] =""; + + if (!PORT->CONNECTED) + return; + + sprintf(ValueString, "%s", Value); + + strcpy(PORT->TXBuffer, Command); + Len = sprintf(ReqBuf, Req, PORT->TXBuffer, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + + return; +} + + + +VOID FLRIGThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoFLRIG(struct RIGPORTINFO * PORT) +{ + if (FLRIGRunning) + _beginthread(FLRIGThread, 0, (void *)PORT); + return ; +} + +VOID FLRIGThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLRIG socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for FLRIG socket - error code = %d Port %d\r\n", + err, htons(destaddr->sin_port)); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED && FLRIGRunning) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (FLRIGRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("FLRIG Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + FLRIGProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "FLRIG Connection lost for Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "FLRIG Thread Terminated Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + + + + +// HID Support Code + +int HID_Read_Block(struct RIGPORTINFO * PORT) +{ + int Len; + unsigned char Msg[65] = ""; + + if (PORT->RXLen > 400) + PORT->RXLen = 0; + + // Don't try to read more than 64 + +#ifdef WIN32 + Len = rawhid_recv(0, Msg, 64, 100); +#else + Len = read(PORT->hDevice, Msg, 64); +#endif + + if (Len <= 0) + return 0; + + // First byte is actual length + + Len = Msg[0]; + + if (Len > 0) + { + if (Len < 64) // Max in HID Packet + { + memcpy(&PORT->RXBuffer[PORT->RXLen], Msg + 1, Len); + return Len; + } + } + return 0; +} + +void rawhid_close(int num); + +BOOL HID_Write_Block(struct RIGPORTINFO * PORT) +{ + int n = PORT->TXLen; + UCHAR * ptr = PORT->TXBuffer; + UCHAR Msg[64] = ""; + int ret, i; + + while (n) + { + i = n; + if (i > 63) + i = 63; + + Msg[0] = i; // Length on front + memcpy(&Msg[1], ptr, i); + ptr += i; + n -= i; + // n = hid_write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#ifdef WIN32 + ret = rawhid_send(0, Msg, 64, 100); // Always send 64 + + if (ret < 0) + { + Debugprintf("Rigcontrol HID Write Failed %d", errno); + rawhid_close(0); + PORT->hDevice = NULL; + return FALSE; + } +#else + ret = write(PORT->hDevice, Msg, 64); + + if (ret != 64) + { + printf ("Write to %s failed, n=%d, errno=%d\n", PORT->HIDDevice, ret, errno); + close (PORT->hDevice); + PORT->hDevice = 0; + return FALSE; + } + +// printf("HID Write %d\n", i); +#endif + } + return TRUE; +} + + +BOOL OpenHIDPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed) +{ +#ifdef WIN32 + + if (PORT->HIDDevice== NULL) + return FALSE; + + PORT->hDevice = rawhid_open(PORT->HIDDevice); + + if (PORT->hDevice) + Debugprintf("Rigcontrol HID Device %s opened", PORT->HIDDevice); + + /* + handle = hid_open_path(PORT->HIDDevice); + + if (handle) + hid_set_nonblocking(handle, 1); + + PORT->hDevice = handle; + */ +#else + int fd; + unsigned int param = 1; + + if (PORT->HIDDevice== NULL) + return FALSE; + + fd = open (PORT->HIDDevice, O_RDWR); + + if (fd == -1) + { + printf ("Could not open %s, errno=%d\n", PORT->HIDDevice, errno); + return FALSE; + } + + ioctl(fd, FIONBIO, ¶m); + printf("Rigcontrol HID Device %s opened", PORT->HIDDevice); + + PORT->hDevice = fd; +#endif + if (PORT->hDevice == 0) + return (FALSE); + + return TRUE; +} + + +void CM108_set_ptt(struct RIGINFO *RIG, int PTTState) +{ + char io[5]; + hid_device *handle; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (RIG->CM108Device == NULL) + return; + +#ifdef WIN32 + handle = hid_open_path(RIG->CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + Debugprintf("Unable to write()\n"); + Debugprintf("Error: %ls\n", hid_error(RIG->PORT->hDevice)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open (RIG->CM108Device, O_WRONLY); + + if (fd == -1) + { + printf ("Could not open %s for write, errno=%d\n", RIG->CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write (fd, io, 5); + if (n != 5) + { + printf ("Write to %s failed, n=%d, errno=%d\n", RIG->CM108Device, n, errno); + } + + close (fd); +#endif + return; + +} + +/* +int CRow; + +HANDLE hComPort, hSpeed, hRigType, hButton, hAddr, hLabel, hTimes, hFreqs, hBPQPort; + +VOID CreateRigConfigLine(HWND hDlg, struct RIGPORTINFO * PORT, struct RIGINFO * RIG) +{ + char Port[10]; + + hButton = CreateWindow(WC_BUTTON , "", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_TABSTOP, + 10, CRow+5, 10,10, hDlg, NULL, hInstance, NULL); + + if (PORT->PortType == ICOM) + { + char Addr[10]; + + sprintf(Addr, "%X", RIG->RigAddr); + + hAddr = CreateWindow(WC_EDIT , Addr, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, + 305, CRow, 30,20, hDlg, NULL, hInstance, NULL); + + } + hLabel = CreateWindow(WC_EDIT , RIG->RigName, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, + 340, CRow, 60,20, hDlg, NULL, hInstance, NULL); + + sprintf(Port, "%d", RIG->PortRecord->PORTCONTROL.PORTNUMBER); + hBPQPort = CreateWindow(WC_EDIT , Port, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, + 405, CRow, 20, 20, hDlg, NULL, hInstance, NULL); + + hTimes = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 430, CRow, 100,80, hDlg, NULL, hInstance, NULL); + + hFreqs = CreateWindow(WC_EDIT , RIG->FreqText, WS_CHILD | WS_VISIBLE| WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, + 535, CRow, 300, 20, hDlg, NULL, hInstance, NULL); + + SendMessage(hTimes, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "0000:1159"); + SendMessage(hTimes, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "1200:2359"); + SendMessage(hTimes, CB_SETCURSEL, 0, 0); + + CRow += 30; + +} + +VOID CreatePortConfigLine(HWND hDlg, struct RIGPORTINFO * PORT) +{ + char Port[20]; + int i; + + hComPort = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 30, CRow, 90, 160, hDlg, NULL, hInstance, NULL); + + for (i = 1; i < 256; i++) + { + sprintf(Port, "COM%d", i); + SendMessage(hComPort, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Port); + } + + sprintf(Port, "COM%d", PORT->IOBASE); + + i = SendMessage(hComPort, CB_FINDSTRINGEXACT, 0,(LPARAM) Port); + + SendMessage(hComPort, CB_SETCURSEL, i, 0); + + + hSpeed = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 120, CRow, 75, 80, hDlg, NULL, hInstance, NULL); + + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "1200"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "2400"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "4800"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "9600"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "19200"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "38400"); + + sprintf(Port, "%d", PORT->SPEED); + + i = SendMessage(hSpeed, CB_FINDSTRINGEXACT, 0, (LPARAM)Port); + + SendMessage(hSpeed, CB_SETCURSEL, i, 0); + + hRigType = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP, + 200, CRow, 100,80, hDlg, NULL, hInstance, NULL); + + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "ICOM"); + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "YAESU"); + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "KENWOOD"); + + SendMessage(hRigType, CB_SETCURSEL, PORT->PortType -1, 0); + +} + +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int i, p; + + CRow = 40; + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + CreatePortConfigLine(hDlg, PORT); + + for (i=0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + CreateRigConfigLine(hDlg, PORT, RIG); + } + } + + + +// CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, +// 90, Row, 40,20, hDlg, NULL, hInstance, NULL); + +// CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, +// 135, Row, 100,20, hDlg, NULL, hInstance, NULL); + +return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +*/ + +#ifdef WIN32 + +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +//#include +//#include +#include +//#include +//#include + +typedef USHORT USAGE; + + +typedef struct _HIDD_CONFIGURATION { + PVOID cookie; + ULONG size; + ULONG RingBufferSize; +} HIDD_CONFIGURATION, *PHIDD_CONFIGURATION; + +typedef struct _HIDD_ATTRIBUTES { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + + +typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; + + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +static HANDLE rx_event=NULL; +static HANDLE tx_event=NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +void print_win32_err(void); + + + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD r; + int n; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +HANDLE rawhid_open(char * Device) +{ + DWORD index=0; + HANDLE h; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + + if (!rx_event) + { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + h = CreateFile(Device, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (h == INVALID_HANDLE_VALUE) + return 0; + + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) + { + CloseHandle(h); + return 0; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + + return h; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + + CloseHandle(hid->handle); + hid->handle = NULL; + hid->open = FALSE; +} + + + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) + { + CloseHandle(p->handle); + p->handle = NULL; + p->open = FALSE; + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + Debugprintf("err %ld: %s\n", err, buf); +} + +#endif + +// RTL_SDR support code + +char RTLModes[5][6] = {"FM", "AM", "USB", "LSB", "????"}; + + +void CheckAndProcessRTLUDP(struct RIGPORTINFO * PORT) +{ + int Length; + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + int Freq; + unsigned char RXBuffer[16]; + + struct RIGINFO * RIG = &PORT->Rigs[0]; + + Length = recvfrom(PORT->remoteSock, RXBuffer, 16, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (Length == 6) + { + long long MHz, Hz; + char CharMHz[16] = ""; + char CharHz[16] = ""; + int i; + int Mode; + + PORT->Timeout = 0; + RIG->RIGOK = TRUE; + + Freq = (RXBuffer[4] << 24) + (RXBuffer[3] << 16) + (RXBuffer[2] << 8) + RXBuffer[1]; + Mode = RXBuffer[5]; + + Freq += RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + sprintf(RIG->WEB_MODE,"%s", RTLModes[Mode]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + } +} + +VOID RTLUDPPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on HAMLIB + char cmd[80]; + int len; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + int n; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + n = sendto(PORT->remoteSock, PORT->TXBuffer, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + if (PORT->TXLen == 10) + n = sendto(PORT->remoteSock, PORT->TXBuffer + 5, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->TXBuffer[0] = 'P'; // Send Poll + + n = sendto(PORT->remoteSock, PORT->TXBuffer, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + cmd[0] = 'P'; + + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID ConnecttoRTLUDP(struct RIGPORTINFO * PORT) +{ + char Msg[255]; + int i; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for RTLUDP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + PORT->Alerted = TRUE; +} + + + + + + diff --git a/Ring.wav b/Ring.wav new file mode 100644 index 0000000000000000000000000000000000000000..ea59863c3e882b2f1e4761f63d2407f9e4b79bf9 GIT binary patch literal 63760 zcmX6_1(*{_({7uYWCcIm-5m~hhr>SH;SN9C-DPn-+}-tXcXxMpmbD}^t$*$P@FdwJ zt<~MtRd2nOrgdx8@_7igtlFYxw?2cjCPN5e_+KRpLgOnTL{KvHSLeZ<_ApKuS2CoE4@c&wfgSG-t4?`aq zF+|}N!E<8ozswKw3%+-uoe;IG8CW1zjnb`7*fkq0wLYzIS&=D)FE zBw%E5@R@+}6xmQVv9yg`_$>ZE$t220Kuu#T=qGXZiE}bA!o=P@=*9g{dT~$&|0liw z?<;W@iDenoQ6^DZ2qn(#*MFs)|D=#OuNdg(C$y3%Ip_ptnK&6DX0?%4-B(U zlqrL65?3n$-YIwpkST(>1);77e<#i_@n2#a62_D`^2E~L%vTc)-`UVh;&(ofoFC-o z{wL`~S$gIZjKuwK?ZtnR63|oPU)=bhyaj#5LmP>^B_7%g{U;F{>Lsp%hBApeC;+2P z+*ff>MnO;dzcv{3l(-)BzjaR>tprH~VZ@1hByl7u;hBNlIgIlc)b*frEcBMRhKaM$ z#(`2clo9aWz`I15CiW5y?O>=G2d{~;OO#{~_FO!Cnkb#P|Kyf9mL$-E1rHyL^OyM! zWxklV@SHg3Wau}P0?H@KOTrj_nvc-4gJNKILHIN!N(1#%K|e3dYZ!rmRzE@?sZl!U zF&0{R3$6Ik3uyBdv|a#aY)n2>5T!+LO&H1y=U|uVWj>hZs0g(3%w$J3(C;WWIs~6T zfWB&?*JgsbY)YeYFq(K6Uv^Xoy)lJQ9*}W3?28}fp*dl?n}en;DvrjWKIj^ZZm7uw zHIkSW&~`X#iguyZ(C-R-MW;u7@k!knjYq5WYV+AF)XB_wbkj^g6YvQ{a9)%h{bACY z%_u1$U2Gzw|a4lR3lxyR(;eS zJIGCHOg>6#`I ztwp8K8q){XJR{nScAzch9h{v>C=Xr+Gpu9kna1W4ta%ypM#t+6s5l;lbKoDa*UF>w z=qKukTATcOv)*HFz>d9OHo@+_k21kHBXC(X-=qPFKQW_qw92dVn)>KD)T)X$!j4M7 zxAAgR(+twfp#D{@^h=$^Y&Q4NeO!gyz!lLaI5(ML9S-Q`x`=LMcA&D*?k980G(nfq zBRIEZ%mV#X-!`jZR$=HmoUUHz4O)j*nYDVo&JFMTq0?qGjCL0qg?Hk3I0xfRH}eL* zc?eQ!2GZGQZX$-8p*T3L>rrOh3^zia{-Dd4>ab(SnkaoouP}R29efX3pAI`SGkQv0 zG!8!`e|YOm7BtyZp>yRq1Lp|UGYi~|rV7f7r?G~VuqbQ2^+qp~Idna5VknK8A@7P( zzSO>NY6#v%&fpcaG0Tin@i5-j8)vrQaXK5ys|(ZC{?xwFXu8TTkGgwJD)Jm1)E~SA zSDQxuF8-6`qwFcanq%ZPo~szI!t<%a^gJ6%50IKF30`Gauv_Eed|I$gFiNe*#q^*0 zfr&-^{Hu~mC&m<4aeSZ`Eheh1!STWDJl33b7W$IC#Y!7ZmFGt?6D&gM)Jyh zgq$Vwqw&6(kyVqGjF`?k$nWk-`A~lGb_K$N%iMJ`#Ew|u))2OgEqAs!sqJ1UTHf@I zc^&mW)l4?y^SpPm7rkL+q3fx@!=aaMEDHY=$@xfrLNr&=>YJLc_M@+~oqgE8K>Og) zW+;|uv}z%mYc+}mhK=y8b`0; zeQKunr+BQ^qQ0iHN-gus;aHK67IxzCFjEYqkQ(3A%jFuck;tgW;q&;eKB8x!4>XsR z%O*6nscEjNPiBX?DL?WU?}S=vs^Ni{g8sgQZ(Adr%+?4rUiMLybP6+Az4gj_^Y~ev zggj1F^`wl^?w+W@@N~Ua9Ju7Gb5VmZ*{Y!(1YoEc8XH z$KnpUOke501eF&;jpaW5Cz)%Fur^wGaeg`zPcoa(Sk*>Uaa*W*s+;;qBFq|chK#T$ zNA|RL>Z)#aQG#D{r|B%RrnoLo;V`?Euc+@E*++Kc!}z(GDkks|30K_VsuoJYim~Ok zLS0!l|7$BB&df9M-mW92$(e$h9x8y0Ga@{LJ;vlg`_&Cq7VLsv-ty3M*;6f`VKfyf zNAj|+{ta}jl?nO0>+XQyN_AE?RHM~Jnc5!f?;6(B`iLulSPxo1=!- zef$e;!+p^myS3HHUW+78sSff#&{ng^WK(;*=9-%IaPr&gw`3n)Vc)VY(tlM}-bi=T zhv4*7=3DtR`40||r1mUY5kDZO$Yq8}d(%~J=eEwJTIwqz-aR1;qy6l%&3zA97*0U)rZEiVDf!b(m@@U$})s*!?I+Q^YpfORV*D zpOwqGs<;@q>QZ2?sSs7m{`xHB1daIW@g}#kXtKniZ@256kf=bf+ zaAW);4e1EG5gA}SFOx`Xvg?&7yLzrXQvk=)qc{U?YdY%JD8Cs_jLxM?sA8%tNk!(d z)+V{ir(Vd}qyP?}ifXWaL%!2|&Q}!DCFOoT+f;f8e|0O zPYTGNZWZsBHxn`P8@phpnk?)rYwkNll{qfv@=j`$bW908n^)CW@P6M$`!~B18m9N) zd}tT$F0=3`?<@ZTHqTX(4s5B~Y@&71wn=)lQTCGQ%_cn%?chhf(y}UAWxb}=*lx1| zb)z+DDz;MQ74N*Nsy3+tH+Ve}rRtzDG>LN&3)Dm&*7-?65=$zJB(l7GN(QiKvK1!; zo9J@71*`6#E-RzmqKN57-in&IDqkZ{qo4GcZwH%h^%HR-Xu3vX zxznkLYPwYe-S~LEU#D=VCS>-ab%yW<5wYPZoglkq=VE(UX!^-~(Dl6h+avt&n= z2Njf!-9KasGsr4zb+l5@9k@5k#B$jcP@Fj6r3i(Kr+T1iDxaysD38_BztT!zl+mL}_(XGU)9N@1qeKW&N^}+I?tU z`q++SYiS9+ffsUd;4wcaxSXI~nLbv~UJ>@jR;&Yy1#3mh8RAZGRl@L42_BDA(amgt z)6$9d#X8&UIe5F8#8ZdTduKeKN4;GlhnYc(_*OZeeD$5bt#Z~Zlv1zuQo1=qwSpT& zGt~^=A|vQ#`;rxHZ(~)-dF{$%`We3?qWCGkLbXJ5tkv{04W}h=JwixMl2-MVrRDE( z2A+kE>IZVF%77-&4iHee%T`2`uP=8@0n6r8=TU{^e04V+5^Y%#e^ z?#Fjb8GVMo@p`Ly<|TFUW?Toaq`Q6XtqL?;O%)~NI-zwuzrxSDqj^`@Ve5TutOe|E z>okkEp5T63hmHl0xdpjWQY=!{^b_3G*TKIcY?jpk{{q{1lbIu4d!2$Yq0({`dO-72 zOdH@g)N^h*>#e$auov&Sq5N{XYOgXI2kz&^{(8Q4D784L8tcv^7tN)8a=!`aaQPNz zF$AQaMwe$7SYuxfdPfg2esKw1!5ftJ9*26%Zh9{1VzniW$loB3vc5trzpNEn@8)-Z z!2P}(cQcHllNpitL_`@RP^sc zX?gC@`rtW{R>r6~yo!>vyFJa{!Y*#l!~uQ9Y?p)7P;a$cguj<_@OOL=9R0jF$WmC( zth%@qdL!HOaNSeh)alh7F>(Ohnoe8T9%}PVyTT~(c;$9}o>+e=~ zBSlNo0~0zM#p2I!w@k9jlkMoZ%B5$hx!yqj%*zI_N({B_t!xy%1)9Gsn?Wujzf3Cr zgxhtK&JG^QUX>YHbPyfP7U4g^XFPzu;hw0p*)A%|G`a=iC^h&CBTPHG*-FBWllCfH z#p$-DHu!hT^+ORWU40o&>WO8BA*zm2B8deBP~ic zfahe`N{a#j(vE9)tCt{8=%a9wa+}9AhtD{!6^-}ft>&c;=*hfvsA^Dq<8(*5%qmA7 zl4Dd^eSFic?6|%-#wqCjuM+bc9#yySYu3T)!7AaFI2Y~7n$Y3qqG0Z3ufE!3+M_6) z7UZ?cx@F&Iqe(Brbpt)jeA1D^28&~x>VON-2i<&P|8^aSr27C@VQ*_i;9Xh5SmT zm6JU!H|oWrvK%1N;c(|~yAk@pbLpZmnpO6Cb_JJ!R#u1%=s4LR_wiU!2-hM#$UnHU z{){ry3r?J!kG|1`Lnrw~of(bPE8Un-tXGJXb6(rj%1_QR%gN+x&+5w;fl`4m?||%Q zzIyGvBdR_f5*F{P?LT7|VYOIU`hi^aw#U``RX=7NA8YcEiKG+T9kwD;CzbHi4i04F4|S|l*`LgJ1MHU3)(5tO716W3OTjyVE|P*rxI)LsdPvet{-e%M z_8k4A%cEGmP)G8A+@pM$T7!3x@8lIaLK=YYvexd6p30nFL*7I6RejBHnMcn-3#hb< zvt{(A?qmLhb4}?$QQe*CPLNfwF_rL7^*^19WU}7dPslm^ubzsA(i!Nw?#?HBk;2jx zrPVpiDZPRubzVBs=}bDC@#>QrqqmBm-p^ne-d0Z_|Fh4)=ns&RtfqaP)x(YWb9WA` zOKR0k?2&PN4r;)(y~|InhBO)Zot37`R0rNCkSX-l^8t#I(X=PkXg>Ryzqs!u+W@}i zMcx2b^{e1~tD8)|N8_!OzOC#TDS;jOkzB+ROndK!mqGl}HlC+vDp$mC0|Yw;5o*5@_GKZPnBh)n(aUAJl)T(XzAGOFc2+bc%Ht zw?qF!SFDuIQmV}mZ@AZ6U_iOjs7Eqf7D3r*dF!qfMZ(EYREd^h@#wAW=wcBek87#N z$lm&IeU`@AnVs5JGSn9Q`EWeb%oDNh-q3BCUuU5Y*eBeAoghiARqRi4*+hG##2hau zChG*b#w4g#w3qb{+w0qaQZ}GYimbx(0l(@un}0RzCtw#ow|aZA7o3G-N*+ZOh3)faqIGcaZ!19b$)S3oZ$ZL!XdDkX zVF~Lk`Uk9oU3{|cpvT}2`k|_-?x?ox16^t@#79g~bW3-lWlee2QfwB*(J@nxyw^=k zBS8L&Sd-~}_D#2!eauhQnNZVD21JxRPN&f|^a|Op9>{9=PdH7V(LMfFD)YBVs?)jI z(NQB2^Z#KLvD>oQ<{#coRzRylzr1F$o*ZP|@Q-6z$AJo5UdT_Bwho+FzY?%zmn{qncU{9be$Z0oPjzr1WTl=`N^geHeUJXdxr-*_mh;1REDs4^-82#U*In`QJcSiLLQAQi@GptJa??gQ?A zgRfK*^ljVrXM{1%kyp@ka)bVbmV2Ar7r~+?5|_mz$x*cujU|3(5-V()skZJBeVh!k zBuOfV$?@_Q`)-}X>D1of0?|<0)^IDk`JlSUy{t6{?ZY0Z+WLuK%b{h2?8f8l8)WNIqLt-Dq7esZ5WU0s=bb|Kvl}z>`Y?1Xs z&2nRTK;F@LgAL->1Qv*k?2h$^Rm_Yf&wW3_J|($j&rxfFe+HxZYq3Sv3w({Q?gem) zh~>V8c7NKPRSBCIUfMU)%MrKi*OP>WDx=<`>u{yJ+Q}k*CAA%gB+=h>8vIRtm0sNE znEJs6`j~%;eUbD*bICaWmGIkPowVQ8!TIhD@JrviHG&^nL z?yi1|ul(zNTw1R>X^(Eg`J2M(MD9;=FEWO%lRw;ZUS6%d%xL+8mvxysueY^Fw9SA6Y# z*UU;lmVa1FthU;cz9J|1FE=7MQ%)j& z%V$^f&I+4+u|_1xX$kG}nWC-EEs~Rsyg)FG{1-KI+W3zWlU+vf67F+wsTf z;Ah!~?xGh=Un1?%N!;)czEOAzuPGXOF|r_k5)1{lhI-=dRuRABbfXz)09W<3wY#By zqI57Xzoz!0NvZ)q%X7$$fWharvy)?bIG`DetcG`Tf828@gC{{nrR-Xv0g53t6t8|`TGFsP*Wjui?qeRqwL<7&^lgS zPq9wWlsGjYVX3X{c2OspnZ|#(e~Ny%0zQfka9ga=jc5k{I@%sP`h!WJ?@51Bif8ev z@Y8ZC_Rw2=LlrXj$XeFSd5Zp3@AyHPkH(S8q@2ja|51DJPdf!kVqD%^T*R7{vSL(w zc~lbo$S%Mf{MakZ<5f|T)>isA@tZeC9c3f!F&5Ir0ndAgT??Wr@<&K{T6d+Rtih(W zDx%(_1$MYopEeQs#W`uAXi^*E0?)nYI>O3q53{hYDSPQi+QZt8bLf4cWW2N~PYj)B zZmWNR76C3+jJE++uBH9swpak9zz5>kGiP`?oD|f6=Kua zMD;^<7UyulzR7OkTD+iarf%Sk^qk2ACvg-iM0?Slw6WS~wyCLj9mEHg>qYS?+&_&& z8raWJA2Sz+`J4NSpsBj4H_^T5jn|jVZrqQp)wl3hdWgNlDX@(Odt22?m0IUUjdVRd zQ%AFEz8t=LXj`bJ+NW!nR^}`pqUz|qW+EzKpLCv>mcYC?&&HF!5Q%6LS}qTp&wz}D z(R%2aY-X>qH(1?pJ8zGe=l!l8>*itxzb{KT)qPj}tHaaN0x};@hpw2pyj}2Q+&(WO zng_^F3}IFb+ZsM1Y@@YLpAMW(Xdjco%b^$XNKr{O_j{46k|uL8+{ic4 zNyCQg4E%xat(HM#q?mV@`$ZR0jHRZv@Mb*?FCkIZ6E=u6F%QIWom?-_XT$}!9KRyg z(%kmn_6N2JPp6q^ZI%-~0YCa_Xp&l|LAU5QH4oRNN&GFH`D`}oCo7tbYOXHIySZsZ zHCc?Uw(i)`G#{YM1L?o)vo0v&Lc;=^1W~`h&UV$mq%E5nHr!vtd4o^N1*(QDY36Yn z`W(vYrNYZ;oHd@d!}(ZmYo)&fD+c&_n0rI^kg0VC{+36XLb#o?!g|D};_KRnH<*?5 ziHQ+w#9`hP{QxX3TBi}Y%gr@K@FiNyKgD%b z5Djp)vw`HQCOR3tgID2la+_DeJ18cSf_Nd^Ytbf-rL-UWnzL+Z4j<+rb4^7l*E{5` zmeWuV=eqU9Dg`!JYpQ7!`%OL!?GB#v{={8SFML_$Gc!mII>^_B9>CYZ_FD$A_1gHI zsKc|!#^@!AphM|0Ih3q6+gMZI5IsdC4UUq{^(dT^n<@MWyvz zmKTuUX`s#SnXyP&byau3cnX^&WEb8|HmcM5PdtF_gA;Isr}i4kZEBW6B%eN@zUfc2 zgP-{tu#niQ7MMH`VJzrY3snv^(>G~ftA>*feC)BbpYO0$O^5R=fg*teys7T5>d7vK zk$k?`B)P)tvaiP0e~2o2syY_;EdFWey37Gl)0TFa)!nJ-3-=vx{J5nk6v!Ir3#edw zFN?chTnOOlM zAhu+gkt&`K;(vfocStuiQFt!y#MWDJ_7p2A;FJU8Sh-A87r%?!9Gk%?Gvn4y=41Ww zN$`z7pcA?Yp9)cjcwo;?Bv&s3lIM*geY0V?zn4?;Kf64HxZr;J$Yg%~cM04r#Tik3^oC;b*bqL~;xgV!~Xl%vB? zI(?s4l08&cGY0L^33`dHh{20vooFUttrRy&ad(JE?G`P)_wp{fOI)%A#UMaP0LflS zY}`?;miJ^@RZ;Jk4dnk+53CCjKEx;if|D&oJ)H?qT; z2&?f3EaFd6sm-#iR}i#ZF>eRH0&$%Mlmk8hUY^~b-1K(KL2e;;ni;0*>4E%`*=A>q zIA{;yM@$<$4g1k!-ACtu*iBJg-rD9XO2Eey@n{lTfkS8p@6PLh_ql*{gH@?6dYksJ z%m1(@%5lM*qQ8EMa-n^yFWlOfSts8^|1%P!eix_p5j<8u;hO^cLu*Y6-&Ly!sn7DT zb=G^<-a2fOxUqqr@vVS9xlIx^KuxuphnP`Qiu(wKhRuU2`_V=+I3h`vX*C;QDV1jBzC){MS8GaOIX>RHg-!qpWdOf z?5$+2_}$$RsLUJ6uc!~+s>h?PxTbH4@1nH~XMp|rKXusjm0{jNFF&tD)-Z^5+D~v( zvJ@`|PkD@->t+aa6+_fw6l1#T&bT|t;$P)IZ)YN&9HvspaXPyMmF^z#DwvwMja8S8 zhTRj%7W+Oj3~}iiZfbFjPmnFR0z|&6X<(g$bNsuNmJ}up(M+<+2w9Pzar>)NdKX~2 zfQadBCWdKN(MpeJsbYX|KV~O%UcLYF86XegX6(BE8cTuJh(g|EU0feV31YI>Nbc6hoeTC*3pi7{8A(kh zvW>F4tAJx8&@A+XSm?7pKy$KW{s+`T39_kpijD$XAv<^7MKYP`Ky zdUH;uz@0=k_I1cHafI8Xc#5?5zIZ!kVy3Y?>Nt3hV!QN5tRmWnP`1(=FjX zzeKy3S2Vx<-b!z+0bbf3u}Lj~=-LWdk0(=DPh@kf1=zASp@}3GYx4s2QGIn<{;wRZ zSF3pAR~1M#w#Qmw6(uFeZCwK=S5(Xrt=ui@ql`AWNEo?cjia_B*hP|uv{Ml}7w-?8 ziXmQd#b6&vU~*W#lU5ohC22sjnkgKM6`@GJ)cs8!)8!DeG z^G)_vOZ6b$LLXBvbW`BjHL|+kHgq4ZhvooATuPtiUDZN8&g{jX)de%%q#>8cv_#ty zxQ=7h0kzg-RQpYo>}wjLOL{tS>nO>}ysXSVl5wf2i$2Y3^XfaT# zuc3cv1M-NSun=m4hw?(=v%D>Zj)S$>q`IQ>Y!}&Lm8Mr{VR90sBjwc&xy1V{UZ_ZY z64h0^fPWc9a$0}T1FQ{Niksr`x`)aKyqC@*9%6p^flK*aGxn9Fu*zEHP)Gc)Jfnol z@68hR_+#^cd?7o0!*CP4kWIoh!OqK3Kb}N}*>|0yWW86D&+-bG+TP+8bp$=uiOW8T?3b1wOI_-s3Yw4#?u zb^2aM^BJKqHwuKqP=p&V~m-EbK4R zhvaiISPM}Nh#CEjRsh?2jW@?zA*!mWz#J__523YqlJ(7wbJ~)FGNW6>8>*74c51H3 zrS6zvthMiu-Iz)J9pd53a3(a&>*Ox=mdes-8u*My)Dx24USb=6Ppc|Us$a-yYQ1RB z&jm8LmE{I=g}q=cEkn24Iea5*&7`p1m7(hVwyZ6)sjfWHm$<<;`u4B^C-`%bFoH zC-A(c0sD0m`h_dfhI$5YVS0f!s!hJKR47g#H95&xyP>rSmzJ4i4mDa^h^lgOglMUM z(oRlA`!wnTcit$%*k*GV;$3O@9NEkK0*`LFE^a(l!e7|AN59K8;+#5$O5%RN75&@Y z1fD`V)`iVPJ@G_xhYhl8kU@YhjS)v=IK-p;GKVN7m!apD?W<{DF~0$~I2CYV1_H0H ziaP>)i`RG#{-pl`j`(MGAK0Hs(F^ffFT*Ec=igC5FWl>iQ-gPSiBwU)o53WWMxe%M zDA@fcFebCfA+p`P5gm9++7Nxvm-G<+Mm4pzhn*nn^=df9eQ|Mi!(0=ifJ^G?WA>ly zIu^jC)$BX)l6LWiAccawJ^FwJ`vG3)yW^j07sB&h=MLM`%ORXdHX^$yyUVeYDLzW8G1aDIxtmEL%krb4_My&vtWHiI0q=e+zD!Cv z&wb6D++>>GE_#TxU|;tZUEDq16Lpk4wsru&dMlY{+0F*m9q$r7A}Z{mW-mw7|IufwS+PEmWpbDvuD5des zu3nUQ1Qzi<>H*(%t2M;e%4uldqKhF3>5;qAWx$(DCo+0l&;np9G!Gl@>*DzArOsEp z2cjM$V)w+y1y=B$5R3f?`=V{w%g7RuS$sbsa=o1o7Tx)zP=?USV0zKrykaq|Idy;y zw%DF+PqZH4N}{~j=VlVC)KvLImX$u#kF2&jSgXl38V~X1JAm8(UJ8sG1b$^Rz$;1t z5BN3PZ9Qh;Y=e0S-fd3v3s|G4#d_XczXN@}nViR~;q1L*-@r@OrnaaoQi$@pwwfUd zs6{HAR)=Wpb@nG7i>u>|9jXM{fCl2`CQK~_yJ8n)DBXu_CJT1aDtg17#-8I5`ni|{sMjx@Sq>Bz z;lxjAY{SxWF>%AgV1-djFO6ax;@C`I^bv((EnIJr!1h3QKpGWO)j7*Cbh`r zE$4Oh98#2Q#^)g4qc?fQn%Uz)4?F=|vo2(?ywdZ6y!fuUP znvZIZ`fiS))%umNb$@)7ZZx@MD^);wcnaLbwVen&QwF?tz_?F_9;$!51|}P1i4`Cj z2&PYvM?2F~G&3zP>x*;1+qka>i~78w_Z!Mc_py24Repwyi1KzCtBJ1X<_aG6`g-N% zMxK!$&@PVh?S^o8Pv|V|R*LNtT|C%K+%o;~IhxV8BfMR*-4TPGZDvTE6fa3i(KyB=ugRTbS;W*M%cOk=#$?h2Y_ z8r_XQlMRrwlTp0)V%%IZ1!Q|^TtL?{GuTaQxbu=6G6%(G3HgAgHF_y>$jZ7Yx(F8c zGn_{c(L2Z<3MW!mmO1!5kqVgNo6%9x5$=79wQ$ap#yA>SCvl`HNkIF=RsJuZF4vhm zItpFY^>H?9r;UBPNHpM29d$`lUSQB!$fh`=nWUA|)(OyCs*}F28<1_dxSGPBhw`f(C>5Jc`#@e|TXTWFW+P}CG>?Y_aBp!+ zlFh7_1w>Ku98a(svoYE==S4IXY$>YiB~RF+LMk`#yqka>n2US`mUKo^6J7Pji{tzs z)CfnCmT(SRgMI)#!wQ-D`YHba`BuM^UN~CTROdxuJEi}lF6~Y9(&(P(26#C6^aorK zIK4k0M!yKsR;$Yv3X^fbnmG%M#&PsLy9W&38TgDEpq~SVTtq$g`X($3Z4&thJ5Ts(C(>@_M6+)Ge=W#6b=St8j60Js zQ>Ig=z`L1+-}@IO`8}z24iFd6ks@jzzaPpJ-zjc7Z(=&w+gTp^-m2uB3ESe-w#%9O zZUb+a`xNpf>WXk-$zSw@ZTp_niQuu#A=&K-XrT&uBy<)Kxq4(BiUM1!y@>`*QOU}I zU#p?|1>kF)%}CjjCkHn30KAKi#+}h(+yYpHqb-M)Q3C-noF}^iC;U6aWv}R-Y>oBM zzDBR2Ib=5tv(BUba+muy*h(~3#Y}bZ;fJBREY|zCcd)KFxt!{x7G6LzTKVnY(N=ye zcrJ8NRt3h4tsg@St`>6$ZI3KpQdZb3tV0$lX=m8Jc*US3v?e@Ni`xiDM?lXS2A2(H}~v* z)+`(+yUHdg3csWibW71oAeD+vPRw9ZT8@F6>bujJ<_1Lchv*Ba?*?<7uk#SLm542RX85NJ-$B+&2?xHcJ5;k>Q z0ML6!4k5ik0w=*U*^QH+ZL|RmLI&t2PIZ1$9G^xHL>jP9>X78@3~7(k>w=^%u1$;4 zI;NtYB+8lPdI>NTTgzG|2$At)^b78de!y)o3f-mG$yW1R_u++rOWg;4yr8~PW{*BX zE3+eLF=T61g$&*K_#+C5ohrYY2v$QB?TH20b2UsVI+SGBT~rjmqo@izTs^L<=zFDrun=i5gPa!vhE@?nl(;slRwW1EaV*xcob(XdGd$mI)G0)^+IoRwo z2iY0ghIR+sCp~S1vykh+pS&)%!7ZH6JV&+(2V1ZOu3(J@Mn6bcy+Nzd5u8<5l#L)W zq6k@yI-qs>53rkm$1m6bJO?trZ|Ot01>~Fm63xX{@YvU}-)PWe(k0ao)C|v{3(#s+ zL_bvrVWu{&V$K6wcn?a#N&*H}S1Q?-cBAv?HC;svldCnR2T>!qlUu1mq%xao{f;UD z4q6mguH(=*Jz6Z_x%C&+#-2>G;eUZa`kbyNlgXcI1Xz$9IFT74=1~;bBdy4C7U7hK zJTP0=G1+N*RLk_{ySY$F(Iv>M@E}rhz;HT`MYBq#mP(MtbTnOO8mL7)US7w&XnN

8lNx5hLVsgzD6vJ z{e>6fr@(G0VeL?(`J%ubcatiL9+O<=1KtG@yJT#&xv!eIyMe_!6y;&hAcw2DSZ0;* zEwDC{mHd$&4&Fz0vsz?RljV1E&MxnCvi2w+P?_Sn!&n`Qs09EYCecUx= zR3uoLabWY7#R2C#ZAqT!kltqf&Azge5S2*kS^SWB0Zienva{|1nPk6wsTn1SIS2*m z1N1k#>fQ|X<0Iu(d{#d*GaydK?QFiSVV&qeuz~le%IKcxCXxq-huW$dIGasz41(-= z$iDU4W376szZfePs#~b9m@5vto5V5jUVGV)_W%*T%50zSBIF)Oue!U=JHwBt9lWH< zp>|-fMx9)~vA8g*YF?T~-Yw4|jX}3Y*C8+Eu-6D}c6vFF?3;i*-^5kGO0S>?@UMZ*9z*+ZbG%yKQ6qI( zcG{QGjv~E94;V)*u&z(>mu|{X7xatvw^qI?kbwgdBUozKSBl6Ej`UC;je`{iU`%y{+Asir}h58 zuiyo*8o9tA(?bd`fnD(>3)^W+T|ge=v*0#hdY$_^ZnoD7RrZbchwOrQvEe@Ao4|I< z-3h+9fq^~n3nhO6CpxPFdp*e?$dAIpNg)5LGkC@@$wk!37p-A z@LL=E>;k?={)dFCu5u^r?N4%-XzCVs(;Gj;6=HEW_%(vlxU-$#PGA{T8u?39kcAm(s3fnA&%eutnt*~*eT8Q3inu5v+SFqXem?ZhMRvS@(rTK(;=P8dY-5}9GV&G>VA?#=wo`>K0z6) zU`@4h+lNhEMZM-;NlE{YqO$;wDp{j&cV8Pxg1fuJ;<`92?(Y7u#odAv+}+(>f-X*Q zcNT&>L}%t&cfYgssIkvNOOi6%bqZ_X|!yPGUR^aHtqd!!9<*)i2c4XP7K zbT{**;3Ayjqpi|*4JWUhtCm!XAjkEpRBNLtT7Fs5jkosNpWVmq6l8@&+^1c%%SJL( zL{X#~^C&t^FPqu<%$Md1cNhtfZs0H6XT7zW+BJ=_aMG4fgATvH+rzDFWwJBLcFIhB zuTp@`f}*FWT1W|yVQvrj{F}O2NKr5+>WjssuW~_atXH951!+B;tO7(;08POpVyMsxA$3L@JJ5UYpWqz9E!duy@=9Pba3jJk)d<^8?}|eEG2eo zoMQWSdneMo?ogAswPb22pvzkh6ge-|KRl6oZDIIS${M=D(n5vJ*Um=h#xKyvyfx{l?KN_E$FfcCCTpuR7UYs_s1v`mu80^V zt5H?WN>7ONSU2s|ckI5C!CGfUp>ms!^bil6ks^_`)4O`IC{xKR@j`ZBZ=}Lcg*utS zftwrN@l3eI&ahbhkbXdGCr7)VoQfi+{2Q+EO4c{0n{0$nS_|u;5lN%%Fj}gOX`HnI zaY;%_(*DOSxGG|3n3@cg#%XJ-wbOk@=Bv{YfemuriMr}oZLjtp zRm6`Iyt(>JX69qeX3ku3lN41Ci(z6PFHWcGMKq>fbkn$o2!j{mA5<=@*;jcDMAn1V zpLn*fVi;Se)}+1V2Yah~OspZ{ESl#+wAr1e)WWnB$|Cm)|BD=9)s*JYaYb6Kc}DE6 zeC(4*kNfqgTEu9rtRbb`7Bp|hSf`TP)#*pJ!#_1jy7q3=uqu0QY3oEs(i*wo zLn}(OMGl<>@28@k&+GG)p&#sXVz&EKlz`f@i1zk4bVE->p6i9Pl=c_C)XH-SwFed9|6cJm&n$5&7*>P3CWeLKP*fCG`}jE7Ir zHoZ{H><{&}Q^}L+RQ> zr5;E2Q4#rK5nIKnJ49S{%YZHv4~T-GQk+SwgGupDM_5u!BW;N5I<(}`lvs7simsMP%R}05T#fj_6N-h z74bZ$Vdx$zF8{GUE{U474dV0a1_y~kL51fz2vKdMunO7R?K#LzE-B|&Cg?D(>> z578&G1TxrBGEV&0$s6My=AT(kZM*Uq+L2+T5uL4+f-`xD`v6*m*J21+EgQIfECyKrcZ8&Eg<^a_h1^Qtai|o-80)rQtBCd zjIPFGwU;|9;7?j@&UPO0!F<2bWC;2)QKW(UJ+La6J~T$$qTOk6 zyvu%SF=L>2iS`#|qMtL7c%aakWX%mOwIkhJN)C0jvYt(&w~d}gA;joQoThe1bAo%q z{cV4-YTLU=I{kpJjAycXgQWpUBaW^VOTlP3720jB$I10c41~A&u`$?p(tlGwOiJ=@ z_7MKs8EYL5#3na!E|UnOv#~<^ro2+Cdv_W!YC--1bfhfiEW`x`_%3^wJ65TxH}!Wl znkx6`O}bGX02_d+@203|Kg8^eVm!;#8PCs`Kdid9QAL~%?v=gG~ zjAf|jE|=G39__sTulh=?aZhFXlF}SsCD|fju@Ab=0?N4y^#o>aI2G z+CSZk)Rl?IjBIzE+S2&tsi58y%|tVKh+QLB-4A9V^MdnDd|}Yl$}l*xI~iQhtrsPa zZ3lhOVPq({6>mdT>;iPYHq5|Zhi0h=ta1BTTXEBBWu9;*ims?WophGCUNTT04n^{9 z@)e4Wvb3C1QjT_Rn%%8G#2HdRSq`^iW3r7M#5*$-pWK3|Aqxq~;cx(5vmS{^y$L(4 zZ)O(gGR>j4KP49NeWKFTylL__8{_1>o6Rfh^T7RVmZKkZYpF0PfGh!&o z=l&L6=c{O}zBb~u$pq1fQidk;;m&cZZs3r;oF~voWhtV_ChTuxo&H=~MEy>4UKgaR z^E_zhb91?=*dgWee<7+2LoLFiHYN4kJYbxJtd4F|_LVkZj#6A1>+PavRCCLKC7dF5 z1aAdb#T!(Vi>Ya~uAVW*Ozn)ii_KSw{Nkhw)=tV7>R}ECFYpNMrF}ON{3|`zJv$k! z;82ESa{bU-<~A2w`NM*&LXSNI4 z*%8~ELgImdC=z&9t<7PI<%a zXhsD?reYTsSsW%mkS@wePZRBvHX1tNl}Z8W@Lc9jD>0PdECQo%o!VU(%11NmQ`)7JOQ?|9}*CcB#iYU{JsHM3+=e(SzF zLI2ZV&PeU~#m?!G#zE~L(lV$9vm_UVlRl^WOhh^x!6%#HFYmvr6jDlpmNk~Fa2nZX zk}{c5_D8x#1t&+XO%LdO^nbO>N-ifHN}hSViL4GX+&;^ejcAnq#dt|C)1JIDyGW17 zHX@_h#`W;>N(!|y84h1jJiV!8)aq#yP@CLu_m;g$0c2z4t^ML3GE+IM)Fpzn;YE}Y zYGrjZZDt>J$~e>MX_uM-AR;++V?6otVmZqEe5#&hs zlqPyPTh@#u~_W0N+h2M z-s9`k>slu;DDvpfz4N`xJ(sEJ>pxuk+Gb*Hr#}4YKjbuIHw|>ue z1wJJ-Oe$qQfp+H$SwfB|WxdjuJ#4i$SdM~9Vv)GVrBf1R5gHPfz$u6C1Ps!%!pQa)n)g#ha$m(to>+tWtxEYG={xL3r=B=e6@ zm(T>eFJsz6Z%=jtVvw?&j!_07oA^qjl?9+(Z5As~Egy#cl9#^VL1(vH zf$Y$-tCy7gvX8t#pOM?_A5qcWhOX#0GJxb&*5F<^Lpmuwqd%KRzq;w%>!J;^(G|{C zXb)-;W<wdnj*I-( zRV%YoLVROO2nejmJBoq(*GJ!mZ)}<4xX0yD_o90gO0Y|)_-xm9G7AKm1#F#miIrn5 zpbo5s_@X$~K!DsWM)RvmCM{Z-r0%D4#4qOv=?o534_;Y36vdRk_5bl}P#4XUxoJSA zqPs;8L~qM!9;K3a2Q}MMQU{%)+ED3a0b%!$(m`z?U$a5%e{?mEce1!ctwCai*g?Ob zuJR|%L#k=v+6NXxtGHj?>t;o3B46z8Cr5=BwfbVlQGF#z184mwC@2q`VWHwl@g@Yh zYI%K}It-b6TCXssBVtJt8g8wC2e+_&IQY|v;_>Qgql-VrUq#DhtYbZth0?aN1^dKz z4n_yxxrHgC3pB$RAD-yT=9yVm)T7W`rE3Ps7EWRLpIh}MFhpf(bDMOK-c zPYUg{lFVkJqL}RdE7OWU)r-nzb+=j`+U}F!`PBwTFiKQ)X328EWcL-T+%aT`SgY;< zx#cWew|8kDQC(?CGKhs>aZN%GY$;8H`tStxH|?PnVnY#qtZ?SQ`!`rF=hvJ7@6Ic$ z8I@03oVtNlS8_qMd`9fzIo!!+30}g90!4WnIi%iZPmSHmJf#8aBl3|kxbMU5Sadp1 zIo;R~;={j?mlf6GSPOj&nJNnLxAsU;-p+!`*K9X}79-#FbIh;qQ~I#f`hA%Py4umm z%Dce1V!DJ@A|0UH*hQaeSDLCtkYK*+x+V-Oe?9G1Ra>wL>62 z+=W{6oN`v25vQH(c7%B0E@RQCpNw9OL&6e3|!~zwO*l0(Hv1_TGB_ZCA;-b>Mw1+OyKwUEnb>UaNogYkq>(7^7N;^ zgFa+!*=SZyU&$IM)7*8)keWMRWHEO+Kjxg^6BLhf7hdRkv@p%X<|xHP8+ji}(mY@Y zXJkFxALvL|q$Bn2?4bGrY=Gy&h7T_u`is?2=Ti?mgx^l>_5#a9 zx=ZCY$LG|x#werppUOmUgf>Q3cC00z1Vx89f}7my{EWd%Ts~* z_;r&wS4Ar~zr1fhv%c^LYDZsxnilG|5^|M#Tz#hZgC@^wW-;@CXtk@WVJA=y1Q+hLyDruU1 zi!Tu!!5x}mMzSLQg}w)2u3CzP$R&1Nz3)bwi{l!^+hzgsT{$8C2J>l`cW{c&{(v6u z$Ed>mF(GanV@Vs=nxJ2sE>p)4ri@w}X@=Pxejr?V$zo<@z!Y|WX%r-liH$zo;4OBC~)6%4hx=*8?#>PQ% zNL;Y`xmD#bIgl?2rMELVIicd;3WwcF*-u%c*3(`pWsoi23ufb$NprZeOWEthI=V+c z=)Iy1r|Cc?ekb0@Q{uhZ&6)|4=U4M(?p$ z3)0qYy`8d)1f64YIWnCr?6GZwO}a(iqoqKn{cYFdS5cRIijM9B5+*9K%StH_4tV}9tpJJ-7Ns2hQn_~Cc<~JDYEIbUDn(kg9X4QehLc%)A2b|MZA$Mkn`8td~5LcZ1SryMBp9 zXjMJA{b}Kh8D=*PWHpnV^75j)*vyWsaHHPL*WOs7tRQFT3Z;Pdi?o9htaT^~KDimR zzu4)Nk@=K~p4*-c>UCr%QS>{hijGH~P_9r;`!`7m-@p(OfiA~c^ElaU~I4n@2xEM zJ~6(iHfyRhfuFdZBCWrZM+Xz3`RnU0hf8RQxS`(n6!bUNHj)FPJg>zX(hvMBvav_j zcHRrF^SpE^G(t_a>P8acJl*LQb=WnWP&G({*%Ok(f#y7~?DW(E@q-quRi!C4ku zV;%;@VU|44qnsbyM$C{2Jf$XtA-?NLugMM~t5ql1(mExws2Upt-RyFj-8g(`BYc0|5-4w>ycaEvnJrWj>^36;0%IE6?K(FDYlbNq`s)YHiu zVCm&9sWHFyon&>N1`~oow;Nf{43bKG5$ly{p4{-5RDi1XpmUSAW~s>or?Oec4QKPz z9{MA&=^dz^2YaR}+|82QFZ9UyAa^Od#U_wYw`jwS*Xl%NJM?0?U4uulNOm06sy(8C zydl@9KUqosr*mIKf?~B91o5A^o91c@K{z;NMFo1;%gAUtnQVe4-V|Sz++hD@P!78h zZd=ixO%qwHKTTrp66FxrBxom61l#=tS@* z)GyIIja?8#@RYQ^5$D;fFQa`$8oEiX4f$PV#|ULKZ-Tj;js40p_Tw! zC?{EhK73nGOJf?XY2OZxuzH9(WGmEa6Re5iUoDgGp{KO^4tf4x=yNw9t*p1nJpwR{fG4|KQMkCT@F&Y4g@m`wW_yFG7>cWO@kvu9}7JN>>$GRp^j_BozO z?64*W9|f1mJVsi*jMjwIAU(9+#%Av)Ew#*G`OIE!X6|!$hRy_{ot><^C$o34c7*O@ zh9|+ZSR2AG1Xl(61Y3iX@Gn$MZTJkegs*YTIH29nHQUNyZRXBD&!Y|#_+T`R#e|I%|DrfSw@`!RtT@3PG zZBX+1kvbrEZ{^3$9#%BJMKVGW@<>J@6Kkq()P^v}$zhM;*P%e|CI_MWP@iv94{3FD zjV=@^;ef5AtzxF=YOMz|u)KIk$I2x5!(WmcMi0+oV=y`5t_J0z3fnDaTWLZAtqd|7 zR6H@X7My`apjo>BM}8*<`H6FyQxeG!+10Fjyp=jcFKe7+Ye)-_w>zs(LlXFHv0AAKu17khn)A($5qoJEZ7sqqH@OhWr%v?r)Nk7L$uUxrokjnno>*-5 zCRgZHFldgDOXvbDB?a+5n#=2m5r(m%zQ_7sv>P84nncg)YuRXC(i{-#;cPLQ8ad%u z94=C7M?iG>%;$t21YNTg9isJQtN3y2xy)e%{11$d$VrPk;H+UTOf9EbXt=YBWcQ@^ z76daWGmlWydERQ*-J8h^lQsuaL4mf1HKof$klpld3eT@!aEqXOI)^_JYoJNGlRUx< z5X;-q*T&OH4ML+k282=Un550|jglX_nc#4p0=52nwVAI~ii5s?X=b;j6D3lj5|Jvn zH|e9fRp!FS0y5G~bkA zhE!*WZPs|pibrLHC@okU){f0;Lehf(=F3Ju=|Q1kpCObviOa;jKk?X&mt!gLPeh4R)V zdmH(v_15Po-{A~D#g>E5w?Zto(wViLCGfrvhTqG8wmLm}`(?ChP*OZ}F35WD8#fmt zt!(yWQd7ezNAKa=n=5N+Gc=pjK!q<-PEcxtsanlBVk%B0xPjIySH(l;Fdxf`8>=)* zVw@|eP(-tE<+kW+p9i^WuU6JD*$3X&$t8Cvhg26cB|exnoI|9zx=GnDo47l~8zq~; zw59UBoe~~`Dy#(Z$Li=v#q!6h$B0w@glf4CIj?3jIDp-95`{ zd-Hlhp@g2w2bC6D0q&a6$7!2mw8uWnch!l1>9Bg~(bJEs5ZRT;# z$OBp_{j%CyRK+g-A3k|~^^g+*u8||VsZWGyPD$8eE@4xk61@Q?h)*f1s(Ku);LJ)4 zCVVz0%l?XO^ibBAKgfO~%2OOQHtPP3Q#`lahWfxRa~dW?Ox7d9qKvlA!@v>rC*z@H zc+4L37Tn$f`EBjYj-G}$Cir2<_1hTSj}R+&-IGR8@65<>AB(=tTwO@ zCl*fn8t8@`ce>ltokYL*Dn;B3H;rwwqT2#A;&bi;D-h@zNC&Fe8ojI0Njbw7DIQNd zPiegdACa8S++#12J>@Pt!M=dI;lAglFRgk@Mw6=0-n^zm?74w4Nhh6aBs0{ACFoFg ziDmNc!Tacgyn}+!V~GxO8I(RN=l* zn1U6ruG4N}cF0M)fO`v50P3^U(4Pm9ac)uyvlil#-3XG73C2p}Pt8kKq1Fwmt+GuR z*0oT;Tue%{fod0}hAhr9YWuv2+5-8-ZeVucPsKU;%57qNPRtF?YmN@P`){bCmEcCI5#SOQ0<{Um zIToi~r}dm2!MNZvQ3^huX)+_cko9O^Lo<%5ikM~}hT5;9@>?9VaziP%MV+Z`Ri5!X zc12LuJbD$a18wSLcM6b=bctF^mbEvafB2X30{+t|XO#1j+*V^esnr+!inYx-%3diY z*>LB1=)N^iY{Yznb#w&Z#ye4Q-RJ5h}cuKPOaJ>)~# zk;W*|qz;R=FC`YVK8wRjcjJo|Yk#xHt0|!Q`z9CLM?!;zM1QOd9piePPCTHl^>hO3 zzk%J=or$btI=f=efM<3XeaW(krS4`cWM7gm*i8&NZ0TH<(Z+e-Z2ceF0-ijd>_r;V z8BSPeXt0fQiQO~Ss)Ok=P*}42)@g0!23t@5jX6cNcr7=(8!j%AwB9t~ZG6krZ19!! z6U${K_j_n$utad4y++nD(t4lijf@HU8}AlREd3RlnQ$d}eQ=n4*$ILQK1kW(yC0D* zMRsp7`c+&8EjrRZk$gX)f70J}Zn8%0qyAQQDFgNKVb6U3Gor|1d$P5}ZpnX{Edxyg zH_Z1mq(3)KsG3p*T%^~&Zr+q+U&siS54CnW$nMS$%j?_{Nm@taf_@zij7Dk+eU)CB z2JB49&p__E&adOFF9ltirG59M^F`|w(Ql8(E~_igm=6Ql0{d_~-&NLQPSjqqOjmqq zd@oc`DXf83wBvIZfP52aj&KIDa-Lk?ItEB~$V@Y;zrcOX5?Yzu4l{C+Wl2&%%q1!H zb-ts%Q@9C}oO)0NmPMyOjrAZn%33Zcp)W-&t)}v6ebLhygWeNu?^yM@}iHv>|Fsa?b8zZ*^3;L`L!@n5L!4 z#mW%6(dPwt)*6q4CCvpfg#%&rBr1$oM0*3ChBc4H}=cDi2Rf6t?D$b4{`9c z>6|U{lsaCz)@tXTxU6mSKlhB4%Xor&+0CPThelotHVuvC-IVEiaWxNI0*CeU{=A+O zvW;Z~>S5;8VwsvI+uK9?#br-;L|vS`$KWnKFaL#(p-Ct!2-7Q_fU*$vvni~HT3+*n zH}@Y9GpvolS~x z3$UH)T@WW*kmsoTuJ=~cn)3~oA?~0qlwGtBEexh|{$*XkgG(V7x;0sPZIY)AX1c9& zYS?!XeS7FPXH;l>sFK@@`IYgkBe^67saw4se?3jX+=;hVMsbX{ak_>+1_nAiSirl( zTS+Udbc4FQ22O))&hbDObkJ7|pKJ}n|21(REQu@rT%H>2lsMo%5R<|8d>ed}+|#}y z`)fD#zmPRvU{#GYo^o1SF*P&?bduR1buGt9n~G0Vu6aiJD;t&RE6k1T#^xcriL%NB z&zi&BqpXwChQ5$hpq%;!4`C~M#0lAp+=EmDD|f#A1eD)VOsbP;G{5S$5$m=2dXhR2 z^UUgtMAA>~MVdPzR3EwZE9yngew~j4>9>_PgK?9Uc6(T#;BjZ91V3suu-@`%;61nV z+*P`R&=HAQHl;}y=X>yBFvd9yFXuz~OMH+kl#|{V&v{)ZWt_rb1hpcDdpI;GFxcA3 z9KDaXrMj1tq=TS)NUuJ3gMs)!0yy9|K{*(Lo$!%8@-6hW)gHJ}=t9MSy&2%A&DVj3 z&Uw}t^VVL8%6uU))jvIX)yCuvCIt1Bh1nA&3T(gx^RAmo>!_#DvWU`dce-1<;7OpV zoMQoQMw4FZG`7-e=I*j5kda|e!OE}4D>~7vgZ^BZX$=Y84Gpr6v9#d*-zR&N*+f@X z`)ae*!5yaMZgYuJOJ9YV6hV27miDXxr~j7~Cz|qda;aw+)^>X<8%r=&sYR(YgZ5gu zPZz7lNE3Ho2o52=spq_B4atOwMQ_Py9d2DWQ)p4hp1y6JSI!2#%o2xW`@o3enbAhCRjhT9a`%4 zfjPm4W_mWmd%-` zA@6Bx_geCtcyHVQt0U@ry1s->R&E)CBW{Ipbs4%%B4An-`F-nG;Jf(`Z(?*0YZq3^ zGlbn`_m$l^$>xW?C8UmT9U97Cpf0mW?qI2mr0_f8)O#D;*Kc-RaL()6pOTL!H4LpM z%RuLAX*^M?YoCnWzO&jj@iC+%=d{|owfPk1rq#h&C7*hvFPrzZ`rpjUr}PmNp{1=f z!Q8<;?jv%RqJzk7CzQI`y~o zL}Mo9W-|qpu~XzuhXmVMN5n|In|GJK8cfkBkZijegw?aA2KEPAVopyu84NetM0rl7 zz6YM=iUJMWds;$?BaVGG@FY0SK26iAvFJ;#6iMoJZz~#sj9n7nJa(OOR}N!j2zH1BknsY%DdC-(yXou|0Sqr=fqj`OwvJtlm#dLRT8Uh z)1UCIpz!8~hj}af#ZAnsZe`-IJIXS+KhulOY`?xpo2*@gv3M{2A018qa5kDvgEgFv zv^X24HBd5Zvo+fj@I|nkPLE_8{GUc3=(Gv?tr2boHO?3Ez4Sf+Unr;kLtjFB27e`# z`jrv2?pC4}_J0}NlELsksowahz7$U0@*o(G38jp$lpqr8h>z+Dt+J;X`v^kg%*C+bW0NmACt9|P}e-@F4RXuH#JIq&N6uKdK^z5@;x{%uq+UR>iQ$6yPX=5sHQvN?R=Om2G7F^ zwGb)J2Lx9qMg}56d&q69E{oJvE&o*i@~~{$D=`Y)_rJv!>rpUY(wO9F&Ip+W{oE<~ zBK;PKdp|w9wJL7m(5B$1KoV5l|Ffo1p-+T&je>; zZRQnq;5#nIxYy@<;cp09*j{i%y-E&uMyOZP-M~<%w5(}(m7Jg{XxdT#QST6aguB_S zWq-9pqM-A{)Pe);OzI`iUtzB?Q7aqkqSp1KVWoJhz}i4pI7t_hT5hbf*KN$g!dCjL zdHcz~-LmMvW>YV?@6AI=69WUJV28B;)ESl8D9^EoJH`lh(JWwIaM(9(Pk)TkRB>v9RB0s4wI1oqyDO-UxNHy)Ae$NjF{jj82EHXgME3 z<{Ov8S9{y5H$gedBoE1zZp!2jNw0%NaAIZlSMqIQYv?kyhr_`_(DCBUlHxd->ic?nC!;e@e7>X?3S{EZ9;8eg>fRR zxxa!^MQk@Sy7@&hxrtW|Z4TA8_d~DoMCnHw@j6i(N7+T(6Rm>oTG3Ujss4d8IWIg-bKy&wjfpMy z%xU%jx3V0oZl$Wy614frMqz!l_K*A1jYgd+GtXs@v7QCT%dFZv#q#uo(y@g43iHnx z*l=Bvd0>L`-mOoX@p_oZqv`K`?LE&RwIzI8pTmkmSbSm{fJ(s-SQ04jtwq0tr582m0$q`nBn^n|jDKOb9 zndC)GYv^sXSpKIpNLATMK0;M+Y{+tE(E;qd5-%6fk;-H6v+A?jWUTW)w*&Hm_2Q^G z%9>|Sm49g?wd3j=;#CJ}4fIOdOfK!Rp&nL0Oy?~x{%0GY1vWM6!$JH|Jt0ft4m<<; z+?!Cr(2C#@P%W=Pp?5KDoS&Bs)HhmNr3~vYO0u>}J>?7e;^cMD*e*JAo5guP z2v3@uebb_}Ep#ZHjBVK@wg~>)ULYNTJb z3B8ksAex=P4EY)8oqC|QzUb>giZ+)s8Y7V3qw!8zTHnUA;G z6WIz*uoiA-xe;XCjUY)Z0pEBk+!#y9H1vdOQM?557Sf~%$qNUJbWfM7#zOp zKkQFv(EE~mEIVa#k$cQ;%_j;QI`!uAs2oais)=xCR3b0=GO*k`&}Q-#?~6IPZ{=;W znpHs6q7D4U#ncb10|l!Ugm@3sc74QgxCN+K&N^V$QD4esh}5U~lmnzc*!TmSCeZS~ zCRLyattgL>c4|?zic$``m`T!+S!k5F;BI%e!nIeKPR2K=lhmkjoCnuq2t0c=T*=RY zj@Ur-=NI?^`GuuYKC!AKT3#Wi=>t}PloJ9p%DVEFoJlHUra&Fpg|<)!D2v%SSw!9- z_dxjm!56?|K8gQLHj~bOn2}qA5ObXc+ z1ff+*Eo#cE{DOE5=EE3}(>CLc&mmpec&v$M@+x^jO}d8O5cx$BOoYlvHp&Sy8hr5( zPU@A)G&pFh%Z_jZp|(oO@=3fa&kvw})WFM;O)A@E;gTR$d4ftm>$!wS!dKDbfR(z(YE5?!&WE$389}!Mx;GTFuK_`^Q zFl&1QnT%PTU!ml$FZ<8|U?8`}`q>FJehl42ilb&xUi2i{$P?s3V|ZiC@62*0CdYl0MM*AvliOr= z(h?`iXqq1x#|Hib^F2NjLNb8uR$AnOo2DkyXi1p>htLCh#7lAb9{5&O2x$n#;d1_0a=;&P%Xg`pHSyRfj~R zz*KJo`5o%ClOmJo z&Nrflx{JP+KG_3$rLU|cJx(-vj>pS-m_A=aMvCuZk6_Fpx8eOc1ttAE*`5_9qhxP! z5S(LEuEn(3Sz@GYj~Q06(E7K=jy;MvUuQ$)ZqZNV0#&p{_>S3fUCDnY!O>Qg8j|BM8gb()8cPx$@a9VCUrZ+K<;x{G-Zjj&PZT1OsB&Ufwasw)~dDt+pkT!##lZEY- zXF!yACw7sEc+-Qi*Jr`+_%AI$Ce!D9j>s(c$-eOBPs8-F=5iR_OE$90q94A&X&^xz z$KQ_PCxtC*k=?8=)?Hh1T*g5!=|%P*5?@6O*2q9P0MwAw*z0S^TJ~66#8kwr&=4@Ax3LtXK1|mWYHbh*J=$>H8@m zs^4_5=CGYcMneOGayX7#c9+6>!luRNs7|i{G2>56`Gxm7iVMxUY69vZ_j}z z<~H0w4QYF}U5+Cec~Ex6-j9YaVZFSM$)eBD{r;2o;9zmd-ZUe-D1Lw%UWdGg=c_B7 z$YZdd2Gg=EP9B2Wr8p^pvu!^Wd=*p@x5yN@ge3WkXCf2jVX*MB!E;lB&jkVeEy+du z;R$Qtw{K92bz6ah1W2~hX#5B_pWF!>(I zZ~Qb(z?|67xE1gP+$LubWgaCv#2xfN z(y|LAGfkABW|7~>q}PxpaxmmK{qJXRbHIFUdA(~r&JKKc!bfDZLPh+Ot9jwRIbe1?OR?8i706Qd`LBaJ_ z6rhbs85S?f;g0Mgb73lbkW|2I;u<6s8O6HDx43!B$nEg09-*T#rv-cgoOpYnVl9LE z;SbUVXH$acFCQX{;~p9>c3q434rec?mwdqO6nDNh$F^S(=W-eXv-x#J8N9IP|StN&JYj z+rY6Ir12tJj=>vQ0iItCT0zvsn(jjUIO#J%AwCWFQzYiIFGB1W2PI7`RT0UkqKC|Z zsfPEcg%ht8c=ny?U{Vfhl(BM%TtHsIgI*A%-OV@^*WsP76aP)sZBO>l{Ny6k%5(qo zH2;Jq{ZCKCuS*!vHdn*2DkLCbrAl z@;O;StIB_|Rx^^%h)Khtk6MP$cZtjcW2Y`Y-+NhyyaVImj@T+Ei3xZ!{qb}8z>nW3 zZ8DM$lwY9g_=D^x7f2HB$CV%{Jiu%#KdDX9iFlclgwe`~c{j^}Sm~0?By&l5aSVGo zGi`}e=PjI#e_@RWz$jcKR)DpbW_3AXOKW`(;1q@^9g}N03J1uxtp| zR4#fSIebazM>^u9h{4WZff(fn=5X~S8L)PKT*P0y$Tb;>dwvQ2ZdrV$=lI5Q%5ro! zX+{2v9&!>#_Qd_u9_o(Q*rN+bEV(E5VlB+Y-<}QrRv*NI(@10Vs zan>yt;p7Wk3^l24V=ry{QDMmykd7L_kW6-1#O=|?#hH*j^ZMMjgC zh4Y=<7Iw@)d6D!c2CayB z&LgBPo6?-59*KqbsWx_gH9B1`g?>01PuKymK_arp%Mv;+`V#r#eK8eJ*oQ#ZVc6XZjPreOi781?N3!k13-+!0KI!Q>hkC`!vX;H)2}EaAcUJ zxqAj$4s^;3aPEDRV-dA30YBxkh?W1X&HLam%tIE_7yHmeytGT4MK-gLcxYKf`yo*j zZ=epDMNVMeU$k6`xNMtDLJUO64E*l~`6Uj>N^&^9NiTkGnRtaK+=bYvHufr}wjh4( zitJ>cxGHDKefU>TqS~_pZV_Z**c}e~-(EQju?waRk|pBXe@{kC?0~n(kvd|1&BSSQ z68CExywC68VW%h4@k|9#o4Yn_FeR7;2cP4X%gKy&(zVFHSoXgSU{~y6LOmYrq zTw(c^#9-~FCgYHI2pJ#;Nggm~ufUTD1vz;33o%V%vJ~Vr^z(6u{pXN!$f`#muKtK< zESYR1d2z0fL4+71Q-XHd2$A1zu$lT}msTNbgpPP4A6 zbR20&ykr7${0*4MHxX0qpCL|u3GFWG>6nrc4u)lU3eFkw)~R9SDJ6m{YH|0+2mzG|ccIfB1E9iM*$%|d#~W0-PQj%Fr5 z$R!yKe_>s@gytr;Tp*W-x8$X4jr%>Ld?No}(tW_+SiXM%e~wXzkR73f5HdotWn}NI zA(D!uv`_lm0uerYlhRrCuLpL(m{5AoX=Sl#$L z@2h&pZ&_^htoirJN1{IQclrfC$9>}E$zq8kal^!rIIUb*I$n@C75Ad!CCSCn{P?rP zM16)Kp;C1GO4KUeFYi8sldg=@vCUS$<-6TJdZ^0Q#&^fd%{nP68UK`=6nz=5H0wlf zl)RFB(H`zn>!lGT&&T@`GyD=_AvJCWPmqjH)ZulXxbtRROZCTKQvjW}$dJMBSRmqFYndllGq{zNf=^$vW{J(Q=h=^SFGp zPp&RwN5c}EldnPENuKf=8837%f0Ous(Qon3i3nC^z|Y=MIahN}vtMJU)#HfQAhujIIA-~-{ z(agjI(fW`7-i0Swv5#fmzE?cjm^hzoE=G^Xxe}M;iWb;=ySQ{TTeom_R5iILeo;L$ z)jN0!c>81#zjm}T&gU)dM@ZNN&$^~t)I6Cz+N*EU&AXm1$9L#oj!g809yyaq$kqXW z_&ixkXQ7@9cht3=Jh1G;_%&~{&KdWM!aF(Vh|5li#=Q1i@~x-`yj&TjNfy#^csqJM zF_)+HOnMdx;@uM7bQX2qi6+RMJ(K04kCWT{YTp4>_^InHc+Rg; zt;C^Z6B5qX;d>}L8x?>E=|eAFx9{cXyeck7v^L6~cs*Gu@~ny9yu48rcmDe0(Ti~g zzwMeKZiu67g;^uvez|1*=uomq;;(3`I{L!(&v52TFm@vQ`SsewY;Q4q-#b3~h_14U z6H(c?h8pH5377i4frg%i-01yq>v(I6=%RPDFM)sSqBY49e4!X#_qhCivkpvI&wZ|p zy2y<=l4N!-*J%2y=1K1ka5NcL!767*<7DAU(P?&VmaLg*Zin@;u1BK+@pJgY2zf0} z@*3~Vi_Q7{R36qJ4=E4-ZcWaFi+yDJLSnyZQYC74rJ`2JPEnfpBOR$#SZ4_x;GeLo z8vG`w8sY6kV{f^+gpGX;>pn=dj2g$M@S{d};AQCjGVNSRet~(OlVxhU>t`|kaI1WG z)jI}hT7dY(o5lU)Y$A*`4zir$6F5p~!VH z))Q{5f|nIS7t_hr02iMQf^MRAq5VB)X1Mj{`?S}D6sfvV3( z7rZ0zGnL}tM19p-3wwN2mGftGLI*3aUqP9os?7_zJ7fMA^;FVfi=%X72RnO&-=XXj z_m8f}uX`WR7Kr?@PT61aU!G9!E$960S4`j1zd4c|5N{Xzb)o8b&-LzvYHQ%^T31?| zMcJZ$iR$rbtnyn}azzAfhnMSkVqg7@2GOSEV!wT= zmKA{C>5V$@w%|(cF3_Jc_^Oi_i!)!jqf~uRbJyX5MLDB%Wv= zO)&fu$*lVN+ub2M8yh>Hd>+b7V1vTy#;);3{5wx{!aaNU`*$nt29GElPs8q?i_%0M z7{T!l;I}*Ct#K0m<*`OV&!7IPqqyA)m7`tW4s~zzGxmN@^r?3_lyP;XoL!v2Q!mrb z@A_ZY;%{_ty6FpV(xZ9`i+RM_mGS)*;`0vsKO5!t?^Msde+*S;TCb_B{}J|4#Q!G7 z_sD#gr`=@5$k=J{$r={_SSm}>Gy@LA>)!92M4p-@8Jc6kWa`s{! zt66>p?6-KYcy9`=CyLE1GX2M_)L*ZCHtmmyTEKzBu5%s5mTr$86qP-B)JpF-+Y)pG zYfi?GMpbDymmM9DS<*n!y*x5)^cgE{h?m6~%(_e`qHO$=YAwGSriY56KZf!(Z9b1j z?!>2x$l+t4OLZRloVmWhxQ|1K+pP8@-4`^bpa@^ihi`zf*+to945}auSjYY=q53_3 z4s{S|vQd87rY@Z8 zsiv!KjnR-}x^C1(??=3DKYgJ{C42ltEMB$t8F$9ajz^lSpKe?_kzYW@?-XT>>$3S* zqJ6dfok+f7jX`S2n&!FyEKfEirc`*OWI$=fHBL)do{y}bfC z)44kAN){_-Oy>zO`BYHRAMWYjR23s?n>-EK>z4<@$w6tyIYmBPT#u|2_MHQ2hhT3$N$EX^7?(6 zadg#6MfQ%|{urJxE_%Z~?vJ>dImfTd+>wwC%>Ei4+XqwXEx-1rlifV&o%m8OUl4u;~jNWxTr+!b`L@B6~hj*)MFzy=o#xmNe=S{DF(&8(2^ei@? z7n*hSv~>>^Q&rJ1kX=W_1rj%_QcgjkF0%Gmkvy7&pZTS#JbEJUI_^mu=>4x6sWjbn z*Zp}Ya$lIP^v4+ZIlOL}owQ7JB;iFJ$(LBAQz9)CX|0mZZQuXtFztk2OLePCx_Wej zU9A$!_azP{GxFwjab0U~#4`5C(HnFVVpmv8>$?1>2FjRNqhk6Mo;FKVpu<08^l|b{ zC3h&@35QP8;j{5J4EnsdIfk((cxxs-&wZ}VXHie)mI)F%Yt>}ab;*Q`xS!3psugPE zDL44F=sh}2ImE~f`cScZnvY`q-&iA)Zf{xMTqG)(Xe7f=({X47FYiihaUC|l`2BxT@F5)+BqH8=yAF7_5TS$GjE7?QdZ-bxAQ$Y<& zHjZYBp_>y0;^EQEWJfhXTAkeKxO7#~eKdMm9(gO-RfXFyy5>3KUVa(=MOP>H@Z@2} zyF(!Q%Ue+fN1u>h-yOqQkbIamN9t2fwC@3O{x)ya+z$z#!V*W*!nJs@UzmIXA0LNB zb&OxtIUXT@x0VG~VK{?qgXyF(GbXA%7&riQ?*k&t3MH1FgI*fmjc*<~Ou_q1z0zeWR~QCItzqjves zRnxO}bif?|O`^;&cC@b7OY-Y0qNuT}T6^i{1^N6(IxLLYcF|d?#{vtZMY?p|N%$bH zd_hHC0gL+z;yosc+o?7yMKhqzDerBn%_G~xoiYdzuew{_By0E;R@A3J&B`mw6mU3#l&Sy z^+0?exl1SPk~da82vzTJt^5o&ya|u|z?~dL^_HioU(%9dmnSDzL&CbGxi4`j&Ze(* z&HdT4T>D)GLH@&NizI%-7^cVBSnC~YtkWG{h1p#aeLwjPsJbHQVRg;MxS}X8$ouBP zn(2Dl2fekkaALEp5seOJ^=IA!&U@iBe+BaZiU9jveTT!s<6o??RY>1wnJXE~J2 zYPD&)Vjb{^kByol2H#dCZNpE_;u`Nktsx{_E{c2VcfT%M7SdbZ5r6J#PLj13^VOGK z@tGwKy;lT_dNN)VZIpAm;d|@iPxWcOhlGtGw_h32i+P<6-!}6xxns3l{0VzttSZR+ zaI<0RreE#7y{ms;VU`ie-}a8tkFm|)$omppcp{#O35Gs!RU;0=rkm74O^uqZT024V zW~!Otq&!N8L!j3}8TL|qP5#L1yN&YNDQ}>wpvGCLZ~r%JYJ+{O^KVRREpY69IM^y{ z3|FP@@Hw-t#9Q`PT@~^Ue)64rUH-slvqZbp{cS@}5Zf%E-~CV=$;>xkdnaAhFNpE& zcit9RpfpKhn#_giRyOC*SZ9~6+QEj)vPfyYiMoq?I?lh)0 zwlUUkK_%()KB%)_rp^n`FF~V87;p|4P(kG7!kbFO>Fun&Uj28t&Yf{FcvsK88f|5h zC)It=t9a}4-`sk&k6>>f^3Q7Gw~l!2Oq-cKxz|g#Y8%W|ldA_hxofT`KKc-b*jl{Z zi)-ZPY0u(>U(!W2Rw%B{X%IDa_E2_j=h{&YXjmRzK7~mRz@{H^^`VMt^bS0ykVtwL zj<1pxYSTm(C=-}}M%o^v&PyYvtKm)ie7YACy3T7~4Am0G-`d*KU2(WaB%Y0%;7wN` zTy9x8R#SXn{=&NAE$L&sQTb`%WZX^8%V3QQ{5P|__o-^;nwaiNA17gUgma$_Jpc^t zejfLfOtLPXpzhei&-&u=|61cfJObDM3PPsE1=p*78bk*%*FV%L`+43-lC-0jw@J7X zSN%r@4A;$`w#GaBemdqqQzkf1tKYirQO)d=WvzW;e=LtZW#rfKWM1^6vk$QQTs2x} zNHm(aZBUIYkSC7lXnrC4mw<#{TJ7I>vFp#TVu)|>m!+|1s+|3k%3z|Jr?KOo`X#iZ zDxu}xUB6pTZy5IIx2TO;C^9}3hds>tjy$@@K7KZS6YLsdo^tj!(TKEW(8+P9Q*Rdf z1%e$T`CPs|#Q2svgdeEl{x#Dd{`!$ko=e>?_&4p0Q&Elc>1oz{#qsla;YF62uabHm zDpXVve!xRAv+4zFY*42>W`(Q%8-vH)!?IV!=pl&EL$&k+{S1m0IPxe@_x>Ky`YJCU z0rMW`wf%WWZjxq+hN(*%8kI+-{&%PrVCrN$?#Cw{mQ`+J)hw!?H}o3&^U21n*+UTWpo+US6_bB zh9?x@!H=paT9d7bJH$Sd&F>e*CrR?K_cFGF#1BK1T^M2|)zt!#-Wt>H3_G5(3w5)2 z>k%q+*IX7ERo0HzlCmWUAA^A}$@u3WTNO;jFVne(HO8n4@j(7q%!-Xj{}?+KOx2Nh z%MCMGqNd#5&8~)GA2;cJ+$UOI=971@avN(5$6@N5>tWobDcdLEY!lc!%>G|8$~~!S z*^gCDcfq>45abcIzmYW?8#S80cUNonw?-X)cht4S8paQWeDCQ}Os9`h@fWPx#C6)X z)@~Zl(91c^!%B+cp=_VczYpO$-}27#Y*oYBUEN!7UhLJ;YoCdI^wuYTlAh}5QvS>{ zN#Ld>Q}Xb3;9%LDpFAI`=y)dV%%Q ziKM31egO-ZVjoRp^6P4srhMyhw&+cg;o?4<9i7D`N7J_JY#ph{BHb(4EL1kvX4kwVAK-Ga+vq;r_(+vvL|`R zyZFPKWQr2q#QFxB+wbhvYQ#1A)33Sq(Tz`d%bX|vUe;G?A79~@Z(}i!>r6ea7xZ}a zrV6B)YV9kGWh2RVU=oKQ$_Q0aJ6_NmCXW35A< z3}=q8V?um=iAA-?>RY-R-k$`CL>UqV^xkKut9Zc~k?5n6p+j-CO=52q-Nq_UQz@lz|QeUf(Gr^_zKfWxeEmwYvY5K8rYI>E%8;v{47b*O{VgHe z9s0Wu1(eWRzLV9rkm)h;Rw?=jU!AD~(~1^CKkZ)mB8RAH!Gpe6`*xt|`}Dw`k?GDR z+d`0=by@S!;r%rAoZU~v@4G^ZDmtSLXeXm+sw_Lq(6{P^V|I4W>|l)GOtJ?KQv#nW z1PSlb&uS0xMmhUQ5_ZI{x{&;;K2$Du&Wv=wT`%$Wl6N{!fqQ>AzDXb99Vpd{-iJfm ze|XCoHRAx^_vMvO+U+o$vZA^w8&9ie_U7Wampd;8(fBi1S2I;vPBD5b%<;Yf*U_I9 zJ8ji+UC29D1h-Tr6|;w`7=2k;yrtvss+ESS@4CaPEUbD>rfHs5?(a9ekriQC-rLlvEYfxLF2%59)*l?yhVhJ5Am;qGj*1ee@mw)Et(dEq|E@QCf>>cd+V#J;GXpjoOKu98?QUfzDHC=zUTB zeEbW4t45lKVAhx9{ZZC94YO9@0}^>oYK?fjU2{VZcnI z9`f&Yc=fyL;#DJ7!hnf-!-dK8i+PID%Rm-b&K{quOx&+R*5$l^jKAvh+bxjjI>t9z zR$42+yboD2S!pYexi45w{#x(s(a?4Wwz=5Z&yZn0=CV=!Gzl-C=$v;k#4(O<(WPD^ zJIvz~Z_s<|RP{O-i~Pp%sn+Pv=Qcsr{p>M8?|eA9`r}t)py6R?azI2bferoCRaGI! z3^AC2^ar5FF8b;Xldr+m>8>Z2#5@j(o>L;?1&FiSf1mLAz&`%uLI3Ft471iqOt2ru z_l)E2h=-YPeO`CIwhE-PK4vDemDEF7&QH5QoUFLY zV-PF{3CoGFzcGwQ`09S>{itkJSWc>-FOwgG>kAX-`o1lnDq+-J1jt+aN`_`+l-Zjq^(qH3`xt!e>bG^zcZ}Xpn_}Nvy8S$__cyAe3WInR@ zb&&G1is3K)&xgckyWk}%5S>v`e2W+zZN{JZir;cH*JO4-CEoU9D@Ek7*VPx-^dCRN zm46fEJNZql2k@FROY!P(cg|1d+R2BG=!8D)%+`3*GFk`-vO^`c!8M|0(5R1C+{dSu zirZaKXQ@83`vP=zSM##%K0RTs$M9BH<5*^(T^ARbYe=|T9r%rN4~U0n>7*%(?^aLj zmW95v#!6TA+FR`jeDxF$-a+!E)|jSps{nCc_E!ey?J;7VO!Em0$-o0&hsY)Q$^m|` z)f)e^@^4V6ztvh`SLb-%e)5i|jc+i6c4X?##_6%`Kj}6+XY?`5D`$VtLWKOjKWh)+ zI{IKM+(>^fLWC;3CsIRi5*Y)G+E3R{i^}HwF$?rQAm#>T@6cGS*_cgX~Y?fnIH z^H*lBB_BVAv(?~#G0zw*Uk$+p?%>V6L|-Ggl2=vsxiy}Lu$Rf(6@s*)xw}<4EBV`# zMg`w*W#12ztBxAysC@X8y-%T4c^;ST;hbN|+eSC68%gS5`a6tzoHu_Bb*qWB$DGre z-(BKmedXg39;Q=-D{0D>X8pcd49D+KRkXAFUx>G zbcgf($aYm%EmV*nK-T+V&0zlC)u^2`+R*;qr~Aj?>Rh^QWz>9n4~Ra<`UCmJe!Fe1 z`WQ;TS?uFEpFZJ*IiX|^T%))7PV0hnw8l8!KcMy>WZxa|flctghWhs-Ydi&Kr?XRI z9R5ofKH7Bol-x?F;%VRLVrE3iXN%EI^ppUcXi~K?myO$mH zA@6qkXzcjcG}?jo`_!PvQ?Fc}A#nkEZg`x2ZJ_ zs7#)v=_P!)08Z79gfAGm24;oawpvuS6%!-aHB=^3bP@BDWDy@4#G4jaqZ?*0!kp)H zz(1vh*-+tko)Yd3c#Z%5sOR~twbzQWd^FdeG#$#C467r5SYOejK6`z4G_M(MRveQ#mT+PxYSZ4ut z`~qp7fqmswSnEYzTl(G1--ggo7i(O!;!;uchLNY}Xo9QIt>Di>{j_o9ogk)fl4lpu zbQh5^NVVTbzwB#XUP9L1VvWAoS~t6`M*da&x|LWz#Z!mlfDgI`eNm>F52cbj+-gTHXu&hRuEKxHPinHqA^hqK5%w}k^6R}W<9*e6t@%10Wljr>uhi4$9y~ssqfJ?m4*7T%3z~B1>%U**w%dxCaa;F%@x zj5pLf?P;N+8tDU`S(#?9*wp|#?FKXb76G4q*43`ga(4xHAG`>A8t9f~huyu^#hui$ zuz&~mQ2jh2>Wf9)*tH`GyJAQC%yS>FooU79#&&d$J0N75u6Ftm@8}K9%YMRI|8w>}-1{%wvk&bI6l*K>{6;|N9kj8}Rj@|tfw7p{ zKPro%uyQRcY=M`Bu-8xEYa!Yl#jZ>2BQU;8?m!$S0va1R4bm+Ncj{SrkK_Gl=Q)0| zUL7#c@gK>s46AJB%n|f|2HwskMY#KI5r$ZVK1Ru?*|D5?G_#TS&$7l&S5Y1ESda#1l1YB&Em;Oz0T-*!>lmxT3s!XI+x8CIKPt@$vZmF(IDdR_9( zMY~;&jf}No85({X)?{$xs`>YrZ-h}UJM)ZrkbVjq);3o=c`CMEde@jj|MWdLlTU5& zh_!Q&GHdi8e5lKZGs%p(VbHhs_YrPi+^ELRzKOQ;yPo_DR2!~J_N1t;%@@x8Qr5cP z8a3fSE}Zmsk@SPgHKaM zY9T83(BuSha?ToQcya+ervt5DgOi_%!F{6qDvprRcO7(&?jX%{=&`}4%Pg8yBQ>Rs zy7ac3gx`zBzv0F|u1?)2Mmnmp4(JoiWA{ULdqh=sXDW)1IC@5n)I>}*<2~ERy9AaU zVy}a8Rb|?HjFtZJzr{TE7i(-+@8qMMUXa*RnIfQp$m<}Q4)c|jymLFB*lbiOQT${o zu8!z=w=t?Qtlv*p8+g=O``BhwEu*@V`6@}mYU}7dTtO>r#-|+5>%5;yxXC`|tD~-o zfS!D!f>B3UcpJo{g;LJ>4H9$qd??FJdeBwg-C^WQpMY3#~DUHL^mlM_H~SdnI`78X6i! zb0>}J#FHC{-J4ZX;ZD5|NxFgT4XxeAr$Q`x!uU@}@)hYz`L>HlDr=3)yy6S~Gn$4n zvPDOpRul4N!NQl=qdSqr)kPydHu4k9Ikz=BtL5v!sLZn64C}wcdgn>fTK4sXAD=8D zT027Bx<;p0`AvdgBT1je8m+}bbGt1_l0~dJ$XuRwhO8f2aja~fojp2WUae^~w+uCv zM&FhXPx6f>#y>2!D)RnS)_9ecHjuXp>AH%=x^{ce`~!TN4XuiZqDOGOj;xdplX;(p zK4SL-d$h$dyWzftMBh~U9ca~qELs;LJY~l<)O4%K6msrz*jAc!q1J50``wd-3HE_h zezC@b{u;>A^{|X3_V=8e@ijlWn>}8&$A?7g ze*5Sz+l^6KXQI(QMm;Mk($V{1EO-DblYJdHVTqE8B#=c=>L%Dq! z346;pU%}Zs<=j`y+(W(o2aUdD)Q3>)CTsMS>0f|knN_gENb))x{wb$*b@m{V+-tXA zSR>T4D0-u0Xb)IPOrh6p1f$p_Nt75@JsUNM+9PU$Iiw)O|sxRc(8u~V?Z253|rLmow=!563Tiatht z4Jq!h#xPb39jgtZ`)S-|EG`-2K`%OII5fNh(O$OpP`mw&)!Li&6F5_xFM8v<@7|{O zqkQuP71{e{`Q07SJsls#dYNUe!R85Fg$+ET0eL@9Y4ogK@ymSl1L%2`Hx4rE5E%Fq z7SqYTKV`Kdd~qDU@s7PLQW2K(*CeYpPgOrp@TReR_-4NN5pFh^M!#3rcrH|BGLqjP zleJ!h86S}M3O^fdjrVxxD$J*Y9erl5qO#VsRK@f{9{z*oeoz$LB-+Ti&RVtWeEvk3gc2&G0Ju(#cFipiWyJdzJrx&4w@7^@Ua;GYu;N9L`k;Mg z!p#=)*xvSWNCxaj3lID5cD2P$JL*dd3;9VqYdo16bwy?jxYUAG^Lz8sD$+lNm281{ zT_Hthv0D)~cxs!EHlnMduE#GSQ!g=h#C=9xtkIkv%k%I(R_hJRm%{f4MOYi>)Kslq zSB)^wq;+5P*Hkm#>Dve3Nh|rbh*9p(rNfqxk&STh^H9oa{WlyyI+t+B~AX^pSSD|C? zc{8&WHdjkNT^rw=ZPZX0cLL+hA!h5cKzQb17aa{2AE94-vvn)u8a>!NJfS?46f6A~ zt1WKli#=Jhm_3e%F7xEP3w$xFNa-TO*2NO%!Q9C#dYo;O@Z>>u?94~E%b?@U`YTa^e^?MS~16Ij4&cE~_`#Z*P!{FuM~Qe7+{MYuM;Q%vP0ljqlI`T(}s$k`9c-GA`- zu-Yzhx)oBC^zRcimr%$2NQb+PT&E|Qi7mp_fY9040-1jm^=n1CBSG6~0zIQt74JtfmMpt(YDJUwsv*UW)AZlc|))+ouZJx^|AUHLgbB+1D$ z&+{G6&XP2TQCs=SG=F7=pW(TNTkIpBEc++RO)>s&9(XT3-RisBbX47q?|2?sEre(7 zb}YOb?3$6K=p#2>-9?h~j!m)RQL_{!PcFN$m4+Zv7OtoJa*IDY* zB5WZO56)wMck+VrI$yi(D0tyza^z>9ekdJ&cc&nmH1pRrwp%PRcAMe2Y?IB}Ek$L1 z->oG1CTQ_1j`^QBX=b;T*(7*tz`9>m#G!VpWgpFW=1G!-cesW7_V$X=lGfFg(|En)J{d2|-b|+guOu@}@8?H(x$N^2sDwYE)U0hdbhmS>qz>&x8Lncx+A>&{lq_ z3>9vaE0;NY9REE>`Um+|P2QG^$8KS@(R8`X9;=ePG1M(?A4l2zf4uB_dMsr(Eud>< zEC0u8lT%rxs0{V9Q6sGwIs)ayNK?$DynUSEVPlP&3mrngpgAT}*BUpoE5LL zLIbll7FW0Gl`OE^AMm+CsxanqObpIiM(L*51hS#H|4}AdpRe> z+Znz%#;8dox+2p)Y*$Z0o=kjkGFCL)dAselp|krLRf;cuFPjdr$B;e3UAO(s)sW`) z(bWee`I1JnTj@!2bt1_HQTh?C=Xod59r_eQtUo8E(HYi$mvy#_mInMPTn#P@R7z{mj*nFNQ1KLuqaj{PQ%VdETPcP>0<|A3@$)a{dmH`W8v1K=pw1fn#=n z5tn>B+S#GjyDA?)gva)@Mov!;ey&n`%k2B)x8QxfX|9~RRF}z`Z_sL}e=Arc$lF9d z+0Sb4*y9)CFqg>c?(9zD_p(?XVXl$9>=I;aqaNtN7xT$gQ&RGFu*P4$9ZZsN@9Yic z?uwy4;s3LfWE`6hrS}uEd3c`TS-xFXmkWZM?@(Z@cDU}RsC zcQB9rMJ^6^@IDJEYKy$(;^s|qEwS6%?c-^C?1Et&63YYGa4LVlfvz4lSGWR}o+R%( zdz2mgiD3l(`8*klW5UyT*n2b=?p`QsxBYYh>cg3pP<5zJOHy7o(T+x$FEETs#=k^Y z_mlTq(K6i3D^poMSnU(Cd0z+3eMpk|)`zunRR?B+l1UTRyF(uj6JVP?xs$8+NjHXcCs}- z6S2RlYnmAUA|K63lKDnVun+GYk*B_}kEx>ND9+Xqmh=g&`?9&}z~FuGXBIE}N*r7_>M0)BR&1S?b>~@o0?q!)lS9^cg2r#a zHkMgq0!xJZ&a0EFubB(;lb_i83&=84xAV5tsBtvCo=zH?>j~%N#y5Tvv!C19LRq5> z+zTFlx9C2`>t~9ENn$NC?+RD>>yqS>DEZDgAE)?U=&66n)251p0!9Vf-Y3&sVEKi< z8;i+Y)&qNlP8!|`SdvaG*oL{%vzP+)9d)HV5?DV?3ot0H`V^8 z$WyoS;DG35#Z=5H3(YVlRiU4xr;kPQ9HZ`nVa=TF`7_vCn@(!6p?8G7Z>%s{obSLiD#P(wX1-gO?yT`+cv84q;&y&s)7jZnHby*ne59vzsLk+2aoU;IasBU{q21JZ28>XM#T&%ypDM9B_8% zwTCx1JZPQp{*6DZahQJBlJBJd)o^xr;^uE&cY;o~vgU4|?&h_j$8lX$o#l%=Xes2+ z{N%63hi|mTB^n5|&~oyIJECf09odb%XvT1N{xYoYwC^h0Q6aGv`}Qm!S!S(W(6A)S z6i@NU%f71siSl4K8Ti)U^tjk4Z&Fta?Q{0`@lmZ=1FHSr1ul_QX;j# zA9&MEYLL6ca}N8+fjb;>_BT-Hk{ODbKc8I%R&YTr5}v|2;=97+xz&mV)pQxmyo@AU zMP*KN=ZB|-`Dp>@z1@r}_;p(U%V~{#WGj-2s~pZQX3m^Gg$jN>y=1i8JgFRV&?s+k z^65J}`=BhQf6Pi(6~$XFY;dKq zn|S#Z*11U}-%D4e#6qz05A1l7PGVSI+CJ`=UqUZp39mV9)qnAgLa9hPWsS&g?}c!c z%zRKsG2Cw#B0oIMRD!1K@!w#d)x01`a$NoIEh9Ybpp`GW*QK&GYSHsaUa^)x{!Hd0 z{PcE6(vXKF`M?@T@l(nt57E(jNV$V%!(E3JeQIpvf2z8zB4#^H?8eRuIM&Si+3DwJ zl5aM{CLWlctsbPgJK*eZ_Pp7sHC8^SlB!RxDvta`!tF4AHLD&rDjcilQ$$0d-daiT z8;!c1OwDOE7jF+y@uM|Dp1#peTkyFOWH?QpP5AvHx(RRgX+&T5^Z9Gm_{FGi%(6$s zRH4z(Q@ok~9fk;ttg#yE6{DTz5UzmTo)ptd>1rX(-30NQIUXv)tGr?zEli`6zm0kj z`>Dn6!hN87%r%W9yWvhXGBuJjZnMUpMulsR-q)Av1cbUOTw%Q=4(Ho#xXApTBsg?l=FmfOoaR^K_Lpy4S}OfhPO7%4=z4d8!e zb`ST@&hgzG$did3LQnVsv**$4Tgv9+$rbuMq28)XC&k6vHY5J$zis@u7zrE5^1(Cz zGAb~MA9!XimVCe(by)2>{Y|#_>EudSqq?)3@UQ%G{-V?x^+cujMLD~L`NH#(TbwzD zwzpg3c6qg#-PVK4r+qt~KZG92%`{fu8cld+LKd8ib9~A67hq`>nX8@XD@fi&G(C~s z572uFe|0in4Wo9^$2gzX!-Bk|YiovvBsm7b#$nG3`Abr?)W<&BlO$XVpMqt4WsQG$ zay4soOGSTr|28*sTOR#yDjz<=CRKUaR$3d6zpSuEcD~Trs8&4SG+h21i<@JOf90g+ z=JKW=R?Fv1Z<4d>K|H#0YSaQT60t{n8tO=MNq#fi_!%^Q3R2W}{4v;(iw(mS=b-J- z4+(nj#pmuJZ-}mNFU>M>myaZYK{nv`$JlNvMmB?gUzPVdVIQ5ydyR&tW00S+*TGbF znq;?gU|P7>;bFUK&X3MpW12NWt#^t>o0Fs~Kgn*lOE9%?|6jOPRmG^sXs<-7erZd; z9mtzWPwQK=j^o3>%4qe>^C%k@hNa=|-v9B3Rd!q2y4^@pkuUznl0ge|`DhOD--Vwv zf(qd}(hO^VMzf(3Z|!({F`Z2o_%6i_Ylzd+{OU8KLWG5@2<>sOmUf#F>zi-I5EZ9V zQ6GBrGay1XR_UNRXk;I;-A=dmL~C87lh9=kSGK(=%^HDmh8!L^?j)Z=XC|#EX|4ik zZH;WAd$w6W5*fdiC4Y02+zkQt}4z(oFa9!k}rg?49%U~p>7;;tgeQO+SmU(aj7{mP6qH)d14deLGt3Gd%?|V!{QXaJ zjCXc;b|Nz`Ys_jbQfn0FeeJDrKdX652<^0Empr0-E-#*FXW<(2-R9{8<0|M2Zm_Fx z&;4duD;G>`>Fm0=!vSkdu=gK$S-R936Rf|JZ41#;Cq5rC-Cp=MF;x{6qPg!_dKyW7 z#gFdf6lpW%+wI5m3sgz%OTt$iVc&Q@|a0WnjX4 z%<+}8SFl>rb-HFO&|D5X2iHTTx=>7oXG`vPyfYNL2^xK8NAqZO7hM&z(=PT=7=n1u zzFAv|gKMn&y?rcX`+cldhF^EG&piAjbUNqTOSqmIu5NTOs+?W@23r@??-JP|X|8tW zs!MaH;b*8{z7_Sy=%kKO%~>y|(;vw+&l;N{dboSL18MTf_q)x%kTt_yNFk>OuMC;S zudSOa%W;6a4?&$vqa;!7+0vbFEDIT!z zDn=$!nWhB|6^9__SuNz-5cSvOdvBEH3*lLS!l`^T8wwwz-|Eh+YUZrg_``~!m$pqN z51GFfJKl!TU!s$xqHJl(e|}5ZFg)9sR#a?cnfZ2mjnCb0wbHDU6@u)wqj}c&lZMLC z$vrfe*BYl&B^7FjaNQ;iKPyI(poI{5+l*M?Q@H;t{FX{_+PhtzI%e!V^BpuQ zFYOoOQKhW$A9)t~Ym4=>kf0#R?}F7C;mHbKum<-G&pPHMZz&#A*r**wEg@y#PdP|Y zoc2m%h$r#qrL6Wlc{7o^kh8sy$!@b5e>VvOt32Y}K`~+%u{ADnr83q(5Zt^(^#{lUR=>;Rgy8(_Eb&IiijVdZ)GVscI(INQ^x}q{L6+nJG-2H6c9h#{kxvepN2JQ>?3rsy#I`!hP$(>+E38LMzd~7 zjry1F!Zo^saO+rzxS zsI|khVBucK&}9l;pgrnJXVL#h3fAL9qn~|HEo1lRdK8)?WJL>U!A?u8b67V z;ByUOU>mcvh&k!dJ>x)pJhLP*;Q2|?t`_tQ)^Upyeyq% zkbA=Y%(HmTWmX8stJuda*4X0gz|N1b)!lYl)joD@S~#`8Fv{Dw%{a?$ z7g;gf0bA9m`$&2-KDX3dGifMXHLHN(gzJ!@`q^UbDdsvzbAh|mB4rt#eArGye=giX zkO%hHGFNpTdj+1%pt(>pr#E+a;;lZb-J)vvmOW-@YLUX^;NqEOasP)42 zm*6L%D}N4_eT}6qgD#h3w7~kBih$4+T4;Q@ljX3;yT{rc$y-?5hR(>$)TnE87k zTl+X=A62Z_jxNJ<;A?Txu*NR@=yvjkTBHC-#*UEtqqKCMBW?hHr#7GlTUA|Gv^w85jNDfc76KI0^c{2cNSd*y;n4UQ=@LP z#$LLb21P>mC-9agR`kwY(NZntC!x0y?mcOk^3lz#6Z(Qp)dZ!W)E^}I${L|l7&6U+ zMuj_B!hIOu*yCLJ_&OY~WsUGm%gvbDkB)yyl0R9eq7n6sEG(veHqU29hUhLzqv0vb zGSK!Uy-z3MG8mA9r`8oy)#xg%zC*Y#`fJe=VyY^C54f0zkN%jFt1<)$Tym~h`(AY4 z2ovtNb`98nljvS-{7f1@ld7aa+h5B-QMJfIv&YLhUJ=w4&g zTo@4gCq?Nhbfrqk#d{&aEawDl2zQ*+HmVxWI0w<@JH9r>UbnILEPLNe@44VlO=p+q z8OPXgHf?Vdr|JA#$sWTKrdRpmx6WQkCl_383RjgHrm!JH3gZ?-kl*d2fKm1MT`}_h zMBZ;`bq_Dg%zpwdRTM49Nb;?-L)K2JkwPt0SDp+xWU2AtKD1N(=Pq(RC?c~PwaKVe zDV=y*Ia@Rk&v){Gkg0!g&JJs&PvxAoBK9axzRf)K?I6Eh{cPRU*8c_mWaKBcWVz6} zKE^6*X>JQoz9RcpWw}ahdrrn&Z;f!zeYiRwo|CT0E;sPlUH%HSLb$iRpivFYT!4O$ z&|lcgHqnxi$JU~SqT=?Xf4BG)-mM%MYRHTg%ypf8!WD#d&JK5Xlyv5OK4pY6hm8t+ zV-FsX-&{3Pesw}r++(hCzDvuS4yNS2gAFf-cD3tl|EUP-z7w7;2%FbaPScgr^n4 z^8gw6;!$#~An8$a6~dm1(Mc|O^@34r= +#include +#include "time.h" + +//#include +//#include + +#define MaxStreams 10 // First is used for Pactor, even though Pactor uses channel 31 + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + +#include +#include +#endif +#endif +#endif + +static char ClassName[]="PACTORSTATUS"; +static char WindowTitle[] = "SCS Pactor"; +static int RigControlRow = 185; + + +#define NARROWMODE 12 // PI/II +#define WIDEMODE 16 // PIII only + +extern UCHAR LogDirectory[]; + +extern char * PortConfig[33]; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +VOID __cdecl Debugprintf(const char * format, ...); + +char NodeCall[11]; // Nodecall, Null Terminated + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + +VOID PTCSuspendPort(struct TNCINFO * TNC); +VOID PTCReleasePort(struct TNCINFO * TNC); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int CheckMode(struct TNCINFO * TNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); +void SCSTryToSendDATA(struct TNCINFO * TNC, int Stream); +VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction); +int standardParams(struct TNCINFO * TNC, char * buf); + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +static FILE * LogHandle[32] = {0}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +static char BaseDir[MAX_PATH]="c:\\"; + +static BOOL WRITELOG = FALSE; + +BOOL SCSStopPort(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 SCSStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->ReopenTimer = 999; // Reopen immediately + + 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/SCSLog_%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 Flags, char * Msg, int MsgLen) +{ + if (WRITELOG) + { + if (LogHandle[Flags]) + { + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + fwrite("\r\n", 1, 2, LogHandle[Flags]); + } + } +} + + +static int DontAddPDUPLEX = 0; + + +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]; + + 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, "PACKETCHANNELS", 14) == 0) // Packet Channels + TNC->PacketChannels = atoi(&buf[14]); + + else + if (_memicmp(buf, "SCANFORROBUSTPACKET", 19) == 0) + { + // Spend a percentage of scan time in Robust Packet Mode + + double Robust = atof(&buf[20]); + #pragma warning(push) + #pragma warning(disable : 4244) + TNC->RobustTime = Robust * 10; + #pragma warning(pop) + } + else + if (_memicmp(buf, "USEAPPLCALLS", 12) == 0 && buf[12] != 'F' && buf[12] != 'f') + TNC->UseAPPLCalls = TRUE; + else + if (_memicmp(buf, "USEAPPLCALLSFORPACTOR", 21) == 0) + TNC->UseAPPLCallsforPactor = TRUE; + else + if (_memicmp(buf, "DRAGON", 6) == 0) + { + TNC->Dragon = TRUE; + if (_memicmp(&buf[7], "SINGLE", 6) == 0) + TNC->DragonSingle = TRUE; + + if (_memicmp(&buf[7], "KISS", 4) == 0) + TNC->DragonKISS = TRUE; + } + else + if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) + TNC->RobustDefault = TRUE; + else + if (_memicmp(buf, "DontAddPDUPLEX", 14) == 0) + DontAddPDUPLEX = TRUE; + else + if (_memicmp(buf, "FORCE ROBUST", 12) == 0) + TNC->ForceRobust = TNC->RobustDefault = TRUE; + else + if (_memicmp(buf, "MAXLEVEL", 8) == 0) // Maximum Pactor Level to use. + TNC->MaxLevel = atoi(&buf[8]); + else + if (_memicmp(buf, "DATE", 4) == 0) + { + char Cmd[32]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd,"DATE %02d%02d%02d\r", tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100); + + strcat (TNC->InitScript, Cmd); + } + else if (_memicmp(buf, "TIME", 4) == 0) + { + char Cmd[32]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd,"TIME %02d%02d%02d\r", tm->tm_hour, tm->tm_min, tm->tm_sec); + + strcat (TNC->InitScript, Cmd); + } + else if (standardParams(TNC, buf) == FALSE) + strcat (TNC->InitScript, buf); + } + + return (TRUE); + +} + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +void SCSCheckRX(struct TNCINFO * TNC); +VOID SCSPoll(int Port); +VOID CRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); +unsigned short int compute_crc(unsigned char *buf,int len); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID ExitHost(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); +static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); +int Switchmode(struct TNCINFO * TNC, int Mode); +VOID SwitchToPactor(struct TNCINFO * TNC); +VOID SwitchToPacket(struct TNCINFO * TNC); + + +char status[8][8] = {"ERROR", "REQUEST", "TRAFFIC", "IDLE", "OVER", "PHASE", "SYNCH", ""}; + +char ModeText[8][14] = {"STANDBY", "AMTOR-ARQ", "PACTOR-ARQ", "AMTOR-FEC", "PACTOR-FEC", "RTTY / CW", "LISTEN", "Channel-Busy"}; + +char PactorLevelText[5][14] = {"Not Connected", "PACTOR-I", "PACTOR-II", "PACTOR-III", "PACTOR-IV"}; + +char PleveltoMode[5] = {30, 11, 14, 16, 20}; // WL2K Reporting Modes - RP, P1, P2, P3, P4 + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + size_t Param; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char PLevel; + 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; + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + Debugprintf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif +#endif + + } +ok: + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SCSCheckRX(TNC); + SCSPoll(port); + + return 0; + + 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)) + { + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + sprintf(STREAM->CmdSet, "D\r"); + STREAM->Disconnecting = TRUE; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + if (TNC->EnterExit) + return 0; // Switching to Term mode to change bandwidth + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr = Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return 0; // No buffers, so ignore + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + TNC->Streams[Stream].FramesOutstanding++; + + // See if possible to send immediately + + SCSTryToSendDATA(TNC, Stream); + + 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->Dragon) + { + if (STREAM->FramesOutstanding > 25) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); + } + else + { + if (STREAM->FramesOutstanding > 10) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); + } + } + else + { + if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) + return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + + case 4: // reinit + + // Ensure in Pactor + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x1; + TNC->TXBuffer[4] = 0x1; + memcpy(&TNC->TXBuffer[5], "PT", 2); + + CRCStuffAndSend(TNC, TNC->TXBuffer, 7); + + Sleep(25); + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE)0; + TNC->ReopenTimer = 250; + TNC->HostMode = FALSE; + + return (0); + + case 5: // Close + + // Ensure in Pactor + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x1; + TNC->TXBuffer[4] = 0x1; + memcpy(&TNC->TXBuffer[5], "PT", 2); + + CRCStuffAndSend(TNC, TNC->TXBuffer, 7); + + Sleep(25); + + ExitHost(TNC); + + Sleep(25); + + CloseCOMPort(TNCInfo[port]->hDevice); + + return (0); + + case 6: // Scan Interface + + Param = (size_t)buff; + + switch (Param) + { + case 1: // Request Permission + + if (TNC->TNCOK) + { + // If been in Sync a long time, or if using applcalls and + // Scan had been locked too long just let it change + + if (TNC->UseAPPLCallsforPactor) + { + if (TNC->PTCStatus == 6) // Sync + { + int insync = (int)(time(NULL) - TNC->TimeEnteredSYNCMode); + if (insync > 4) + { + Debugprintf("SCS Scan - in SYNC for %d Secs - allow change regardless", insync); + return 0; + } + } + else if (TNC->TimeScanLocked) + { + time_t timeLocked = time(NULL) - TNC->TimeScanLocked; + if (timeLocked > 4) + { + Debugprintf("SCS Scan - Scan Locked for %d Secs - allow change regardless", timeLocked); + TNC->TimeScanLocked = 0; + return 0; + } + } + } + + TNC->WantToChangeFreq = TRUE; + TNC->OKToChangeFreq = FALSE; + return TRUE; + } + return 0; // Don't lock scan if TNC isn't responding + + + case 2: // Check Permission + return TNC->OKToChangeFreq; + + case 3: // Release Permission + + TNC->WantToChangeFreq = FALSE; + + if (TNC->DontReleasePermission) // Disable connects during this interval? + { + TNC->DontReleasePermission = FALSE; + if (TNC->SyncSupported == FALSE) + TNC->TimeScanLocked = time(NULL) + 100; // Make sure doesnt time out + return 0; + } + + TNC->DontWantToChangeFreq = TRUE; + return 0; + + default: // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + PLevel = Scan->PMaxLevel; + + if (PLevel == 0 && (Scan->HFPacketMode || Scan->RPacketMode)) + { + // Switch to Packet for this Interval + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("SCS Switching to Packet, %d", TNC->HFPacket); + + if (TNC->HFPacket == FALSE) + SwitchToPacket(TNC); + + return 0; + } + + if (PLevel > '0' && PLevel < '5') // 1 - 4 + { + if (TNC->Bandwidth != PLevel || TNC->MinLevel != (Scan->PMinLevel - '0')) + { + TNC->Bandwidth = PLevel; + TNC->MinLevel = Scan->PMinLevel - '0'; + Switchmode(TNC, PLevel - '0'); + } + + if (TNC->UseAPPLCallsforPactor && Scan->APPLCALL[0]) + { + // Switch callsign + + STREAM = &TNC->Streams[0]; + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + + strcpy(STREAM->MyCall, Scan->APPLCALL); + + sprintf(STREAM->CmdSet, "I%s\rI\r", STREAM->MyCall); + if (TNC->RIG->RIG_DEBUG) + Debugprintf("SCS Pactor APPLCALL Set to %s", STREAM->MyCall); + } + + else + { + if (TNC->HFPacket) + SwitchToPactor(TNC); + } + } + + if (Scan->RPacketMode) + if (TNC->RobustTime) + SwitchToPacket(TNC); // Always start in packet, switch to pactor after RobustTime ticks + + if (PLevel == '0') + TNC->DontReleasePermission = TRUE; // Dont allow connects in this interval + else + TNC->DontReleasePermission = FALSE; + + return 0; + } + } + return 0; +} + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len) +{ + struct RIGINFO * RIG = TNC->RIG; + + if (RIG && RIG->WEB_Label) + { + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", RIG->WEB_Label); + Len += sprintf(&Buff[Len], "", RIG->WEB_FREQ); + Len += sprintf(&Buff[Len], "", RIG->WEB_MODE); + Len += sprintf(&Buff[Len], "", RIG->WEB_SCAN); + Len += sprintf(&Buff[Len], "", RIG->WEB_PTT); + + + if (TNC->TXRIG && TNC->TXRIG != TNC->RIG) + { + struct RIGINFO * RIG = TNC->TXRIG; + + Len += sprintf(&Buff[Len], "", RIG->WEB_Label); + Len += sprintf(&Buff[Len], "", RIG->WEB_FREQ); + Len += sprintf(&Buff[Len], "", RIG->WEB_MODE); + Len += sprintf(&Buff[Len], "", RIG->WEB_SCAN); + Len += sprintf(&Buff[Len], "
%s%s%s%c%c
%s%s%s%c%c
", RIG->WEB_PTT); + } + Len += sprintf(&Buff[Len], ""); + } + return Len; +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "SCS Pactor Status

SCS 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], "", TNC->WEB_PACTORLEVEL); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Buffers%s
Traffic%s
Mode%s
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +void * SCSExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int Stream = 0; + char * TempScript; + + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + DontAddPDUPLEX = 0; + + sprintf(msg,"SCS Pactor %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_SCS; + + OpenLogFile(TNC->Port); + CloseLogFile(TNC->Port); + + + if (TNC->BusyHold == 0) + TNC->BusyHold = 3; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->MaxLevel == 0) + TNC->MaxLevel = 3; + + // Set up DED addresses for streams (first stream (Pactor) = DED 31 + + TNC->Streams[0].DEDStream = 31; + + for (Stream = 1; Stream <= MaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; + } + + if (TNC->PacketChannels > MaxStreams) + TNC->PacketChannels = MaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = CONLOCK; // Scan Control 3 stage/conlock + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + TNC->SuspendPortProc = PTCSuspendPort; + TNC->ReleasePortProc = PTCReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = SCSStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = SCSStopPort; + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // get NODECALL for RP tests + + memcpy(NodeCall, MYNODECALL, 10); + + ptr=strchr(NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + + // Set TONES to 4 + + TempScript = malloc(1000); + + strcpy(TempScript, "QUIT\r"); // In case in pac: mode + strcat(TempScript, "TONES 4\r"); // Tones may be changed but I want this as standard + strcat(TempScript, "MAXERR 30\r"); // Max retries + strcat(TempScript, "MODE 0\r"); // ASCII mode, no PTC II compression (Forwarding will use FBB Compression) + strcat(TempScript, "MAXSUM 20\r"); // Max count for memory ARQ + strcat(TempScript, "CWID 0 2\r"); // CW ID disabled + strcat(TempScript, "PTCC 0\r"); // Dragon out of PTC Compatibility Mode + strcat(TempScript, "VER\r"); // Try to determine Controller Type + + sprintf(msg, "MYLEVEL %d\r", TNC->MaxLevel); + strcat(TempScript, msg); // Default Level to MAXLEVEL + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "ADDLF 0\r"); // Auto Line Feed disabled + strcat(TNC->InitScript, "ARX 0\r"); // Amtor Phasing disabled + strcat(TNC->InitScript, "BELL 0\r"); // Disable Bell + strcat(TNC->InitScript, "BC 0\r"); // FEC reception is disabled + strcat(TNC->InitScript, "BKCHR 2\r"); // Breakin Char = 2 + strcat(TNC->InitScript, "CHOBELL 0\r"); // Changeover Bell off + strcat(TNC->InitScript, "CMSG 0\r"); // Connect Message Off + strcat(TNC->InitScript, "LFIGNORE 0\r"); // No insertion of Line feed + strcat(TNC->InitScript, "LISTEN 0\r"); // Pactor Listen disabled + strcat(TNC->InitScript, "MAIL 0\r"); // Disable internal mailbox reporting + strcat(TNC->InitScript, "REMOTE 0\r"); // Disable remote control + strcat(TNC->InitScript, "PAC CBELL 0\r"); // + strcat(TNC->InitScript, "PAC CMSG 0\r"); // + strcat(TNC->InitScript, "PAC PRBOX 0\r"); // Turn off Packet Radio Mailbox + + // Automatic Status must be enabled for BPQ32 + // Pactor must use Host Mode Chanel 31 + // PDuplex must be set. The Node code relies on automatic IRS/ISS changeover + // 5 second duplex timer + + strcat(TNC->InitScript, "STATUS 2\rPTCHN 31\rPDTIMER 5\r"); + + if (DontAddPDUPLEX == 0) + strcat(TNC->InitScript, "PDUPLEX 1\r"); + + sprintf(msg, "MYCALL %s\rPAC MYCALL %s\r", TNC->NodeCall, TNC->NodeCall); + strcat(TNC->InitScript, msg); + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_PACTORLEVEL = 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", "Buffers", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->xIDC_PACTORLEVEL = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE,10,160,430,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; + + sprintf(TNC->WEB_BUFFERS, "%05d Queued %05d", TNC->Buffers, TNC->Streams[0].FramesOutstanding); + SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS); + + + MoveWindows(TNC); +#endif + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + printf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif +#endif + if (TNC->RobustDefault) + SwitchToPacket(TNC); + + WritetoConsole("\n"); + + return ExtProc; +} + +void SCSCheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + unsigned short crc; + char UnstuffBuffer[500]; + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + + if (TNC->RXBuffer[0] != 170) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) + + return; // Wait for rest of frame + + // Complete Char Mode Frame + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); + CloseLogFile(TNC->Port); + + TNC->RXLen = 0; // Ready for next frame + + if (TNC->HostMode == 0) + { + // We think TNC is in Terminal Mode + ProcessTermModeResponse(TNC); + return; + } + // We thought it was in Host Mode, but are wrong. + + TNC->HostMode = FALSE; + return; + } + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[2] == 170) + { + // Retransmit Request + + TNC->RXLen = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + TNC->RXLen = 0; + return; // Ignore for now + } + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + TNC->RXLen = 0; // Ready for next frame + ProcessDEDFrame(TNC, UnstuffBuffer, Length); + + // If there are more channels to poll (more than 1 entry in general poll response, + // and link is not active, poll the next one + + if (TNC->Timeout == 0) + { + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->NexttoPoll[0]) + { + UCHAR Chan = TNC->NexttoPoll[0] - 1; + + memmove(&TNC->NexttoPoll[0], &TNC->NexttoPoll[1], 19); + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + CRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return; + } + else + { + // if last message wasn't a general poll, send one now + + if (TNC->PollSent) + return; + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + CRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + } + } + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + +BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + BOOL ret = WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + TNC->Timeout = 20; // 2 secs + return ret; +} + +VOID SCSPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + int Stream = 0; + int nn; + struct STREAMINFO * STREAM; + + if (TNC->MinLevelTimer) + { + TNC->MinLevelTimer--; + + if (TNC->MinLevelTimer == 0) + { + // Failed to reach min level in 15 secs + + STREAM = &TNC->Streams[0]; + + if (STREAM->Connected) + { + PMSGWITHLEN buffptr; + + Debugprintf("Required Min Level not reached - disconnecting"); + + // Discard Queued Data, Send a Message, then a disconnect + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + STREAM->NeedDisc = 15; // 1 secs + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, + "This port only allows Pactor Level %d or above - Disconnecting\r\n", TNC->MinLevel); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } + } + } + + if (TNC->SwitchToPactor) + { + TNC->SwitchToPactor--; + + if (TNC->SwitchToPactor == 0) + SwitchToPactor(TNC); + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach + + // If Pactor, stop scanning and take out of listen mode. + + // Set call to connecting user's call + + // If Stream 0 Put in Pactor Mode so Busy Detect will work + + int calllen=0; + + STREAM = &TNC->Streams[Stream]; + Debugprintf("SCS New Attach Stream %d DEDStream %d", Stream, STREAM->DEDStream); + + if (Stream == 0) + STREAM->DEDStream = 31; // Pactor + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + + STREAM->MyCall[calllen] = 0; + + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + + if (Stream == 0) + { + // Release Scan Lock if it is held + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + if (TNC->DontReleasePermission) + { + TNC->DontReleasePermission = FALSE; + TNC->DontWantToChangeFreq = TRUE; + } + + sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); + + SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanner + + sprintf(Status, "%d SCANSTOP", TNC->Port); + TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch + + Rig_Command(-1, Status); + } + else + { + sprintf(STREAM->CmdSet, "I%s\r", STREAM->MyCall); + Debugprintf("SCS Pactor Attach CMDSet = %s", STREAM->CmdSet); + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + TNC->Retries--; + + if(TNC->Retries) + { + WriteCommBlock(TNC); // Retransmit Block + return; + } + + // Retried out. + + if (TNC->HostMode == 0) + { + DoTermModeTimeout(TNC); + return; + } + + // Retried out in host mode - Clear any connection and reinit the TNC + + Debugprintf("PACTOR - Link to TNC Lost"); + TNC->TNCOK = FALSE; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + UINT * buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + TNC->HostMode = 0; + TNC->ReinitState = 0; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + // We delay clearing busy for BusyHold secs + + if (TNC->Busy) + if (TNC->Mode != 7) + TNC->Busy--; + + if (TNC->BusyDelay) // Waiting to send connect + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == 0) + { + // No, so send + + TNC->Streams[0].CmdSet = TNC->ConnectCmd; + 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); + + Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[0].CmdSet); + + 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 = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + free(TNC->ConnectCmd); + + } + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + STREAM->ReportDISC = TRUE; + + } + + if (TNC->Timeout) + return; // We've sent something + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + TNC->PollSent = FALSE; + + //If sending internal command list, send next element + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].CmdSet) + { + unsigned char * start, * end; + int len; + + start = TNC->Streams[Stream].CmdSet; + + if (*(start) == 250) // 2nd part of long KISS packet + { + len = start[1]; + + Poll[2] = 250; // KISS Channel + Poll[3] = 0; // Data + Poll[4] = len - 1; + memcpy(&Poll[5], &start[2], len); + + CRCStuffAndSend(TNC, Poll, len + 5); + + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + return; + } + + if (*(start) == 0) // End of Script + { + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + } + else + { + end = strchr(start, 13); + len = (int)(++end - start - 1); // exclude cr + TNC->Streams[Stream].CmdSet = end; + + if (*(start) == 1) + { + // This is UI data, not a command. Send it to channel 0 + + len --; + + Poll[2] = 0; // UI Channel + Poll[3] = 0; // Data + Poll[4] = len - 1; + memcpy(&Poll[5], &start[1], len); + + CRCStuffAndSend(TNC, Poll, len + 5); + + return; + } + + if (*(start) == 2) + { + // This is a UI command Send it to channel 0 + + len--; + + Poll[2] = 0; // UI Channel + Poll[3] = 1; // Command + Poll[4] = len - 1; + memcpy(&Poll[5], &start[1], len); + + CRCStuffAndSend(TNC, Poll, len + 5); + + return; + } + + Poll[2] = TNC->Streams[Stream].DEDStream; // Channel + Poll[3] = 1; // Command + Poll[4] = len - 1; + memcpy(&Poll[5], start, len); + + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], len); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, len + 5); + + return; + } + } + } + // if Freq Change needed, check if ok to do it. + + if (TNC->TNCOK) + { + if (TNC->WantToChangeFreq) + { + Poll[2] = 31; // Command + Poll[3] = 1; // Command + Poll[4] = 2; // Len -1 + Poll[5] = '%'; + Poll[6] = 'W'; + Poll[7] = '0'; + + CRCStuffAndSend(TNC, Poll, 8); + + TNC->InternalCmd = TRUE; + TNC->WantToChangeFreq = FALSE; + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("Scan Debug SCS Pactor Requesting permission from TNC"); + + return; + } + + if (TNC->DontWantToChangeFreq) + { + Poll[2] = 31; // Command + Poll[3] = 1; // Command + Poll[4] = 2; // Len -1 + Poll[5] = '%'; + Poll[6] = 'W'; + Poll[7] = '1'; + + CRCStuffAndSend(TNC, Poll, 8); + + TNC->InternalCmd = TRUE; + TNC->DontWantToChangeFreq = FALSE; + TNC->OKToChangeFreq = FALSE; + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("Scan Debug SCS Pactor Release Scan Lock sent to TNC"); + + return; + } + } + + // Send Radio Command if avail + + if (TNC->TNCOK && TNC->BPQtoRadio_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = Q_REM(&TNC->BPQtoRadio_Q); + + datalen = (int)buffptr->Len; + + Poll[2] = 253; // Radio Channel + Poll[3] = 0; // Data? + Poll[4] = datalen - 1; + + memcpy(&Poll[5], buffptr->Data, datalen); + + ReleaseBuffer(buffptr); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + if (TNC->RIG->RIG_DEBUG) + { + Debugprintf("SCS Rig Command Queued, Len = %d", datalen ); + Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + Poll[5], Poll[6], Poll[7], Poll[8], Poll[9], Poll[10], Poll[11], Poll[12], + Poll[13], Poll[14], Poll[15], Poll[16], Poll[17], Poll[18], Poll[19], Poll[20]); + } + + // Debugprintf("SCS Sending Rig Command"); + + return; + } + + if (TNC->TNCOK && TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char ICall[16]; + char CCMD[80] = "C"; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + + Buffer[datalen] = 0; + + // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address + // before sending the beacon + + // If a Dragon with KISS over Hostmade we can just send it + + if (TNC->DragonKISS) + { + int EncLen; + + Poll[2] = 250; // KISS Channel + Poll[3] = 0; // CMD + Poll[4] = datalen + 2; // 3 extrac chars, but need Len - 1 + + Buffer--; + *(Buffer) = 0; // KISS Control on front + EncLen = KissEncode(Buffer, &Poll[5], datalen + 1); + + // We can only send 256 bytes in HostMode, so if longer will + // have to fragemt + + if (EncLen > 256) + { + //We have to wait for response before sending rest, so use CmdSet + + // We need to save the extra first, as CRC will overwrite the first two bytes + + UCHAR * ptr = TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400); + + (*ptr++) = 250; // KISS Channel + (*ptr++) = EncLen - 256; + memcpy(ptr, &Poll[5 + 256], EncLen - 256); + + // Send first 256 + + Poll[4] = 255; //need Len - 1 + CRCStuffAndSend(TNC, Poll, 261); + } + else + { + Poll[4] = EncLen - 1; //need Len - 1 + CRCStuffAndSend(TNC, Poll, EncLen + 5); + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + // We also need to set Chan 0 Mycall so digi'ing can work, and put + // it back after so incoming calls will work + + // But we cant set digipeated bit in call, so if we find one, skip message + + ConvFromAX25(Buffer + 7, ICall); // Origin + strlop(ICall, ' '); + + ConvFromAX25(Buffer, &Call[1]); // Dest + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 14; // Skip Origin + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + if (Buffer[6] & 0x80) // Digied bit set? + { + ReleaseBuffer((UINT *)buffptr); + return; + } + + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + Poll[2] = 0; // UI Channel + Poll[3] = 1; // CMD + Poll[4] = (int)strlen(CCMD) - 1; + strcpy(&Poll[5], CCMD); + CRCStuffAndSend(TNC, Poll, Poll[4] + 6); // Set Dest and Path + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400); + sprintf(TNC->Streams[0].CmdSet, "%cI%s\r%c%s\r%cI%s\r", + 2, ICall, // Flag as Chan 0 Command + 1, Buffer, // Flag CmdSet as Data + 2, TNC->NodeCall); // Flag as Chan 0 Command + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + + // Check status Periodically + + if (TNC->TNCOK) + { + if (TNC->IntCmdDelay == 6) + { + Poll[2] = 254; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + + CRCStuffAndSend(TNC, Poll, 7); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay--; + + return; + } + + if (TNC->IntCmdDelay == 4) + { + Poll[2] = 31; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = '%'; // Bytes acked Status + Poll[6] = 'T'; + + CRCStuffAndSend(TNC, Poll, 7); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay--; + + return; + } + + if (TNC->IntCmdDelay <=0) + { + Poll[2] = 31; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = '@'; // Buffer Status + Poll[6] = 'B'; + + CRCStuffAndSend(TNC, Poll, 7); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay = 20; // Every 2 secs + + return; + } + else + TNC->IntCmdDelay--; + } + + // If busy, send status poll, send Data if avail + + // We need to start where we last left off, or a busy stream will lock out the others + + for (nn = 0; nn <= MaxStreams; nn++) + { + Stream = TNC->LastStream++; + + if (TNC->LastStream > MaxStreams) + TNC->LastStream = 0; + + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + + // Dont send to Pactor if waiting for Min Level to be reached + + if (TNC->MinLevelTimer && Stream == 0) + continue; + + buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + datalen = (int)buffptr->Len; + Buffer = buffptr->Data; // Data portion of frame + + Poll[2] = TNC->Streams[Stream].DEDStream; // Channel + + if (TNC->Streams[Stream].Connected) + { + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[3] = 0; // Data? + TNC->Streams[Stream].BytesTXed += datalen; + + Poll[4] = datalen - 1; + memcpy(&Poll[5], Buffer, datalen); + + WritetoTrace(TNC, Buffer, datalen); + + ReleaseBuffer(buffptr); + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], datalen); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + + // Command. Do some sanity checking and look for things to process locally + + Poll[3] = 1; // Command + datalen--; // Exclude CR + Buffer[datalen] = 0; // Null Terminate + _strupr(Buffer); + + if (_memicmp(Buffer, "DD", 2) == 0) + { + // Send DD (Dirty Disconnect) + + // Uses "Hidden" feature where you can send any normal mode command + // in host mode by preceeding with a # + + Poll[2] = 31; + Poll[3] = 0x1; + Poll[4] = 2; + sprintf(&Poll[5], "#DD\r"); + CRCStuffAndSend(TNC, Poll, 8); + + // It looks like there isn't a response + + TNC->Timeout = 0; + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], 4); + CloseLogFile(TNC->Port); + + ReleaseBuffer(buffptr); + return; + } + + if (_memicmp(Buffer, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if (memcmp(Buffer, "RADIO ", 6) == 0) + { + sprintf(&Buffer[40], "%d %s", TNC->Port, &Buffer[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &Buffer[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr->Len = sprintf(buffptr->Data, "%s", &Buffer[40]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (_memicmp(Buffer, "SessionTimeLimit", 16) == 0) + { + if (Buffer[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&Buffer[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return; + } + } + + if (memcmp(Buffer, "MYLEVEL ", 8) == 0) + { + Switchmode(TNC, Buffer[8] - '0'); + TNC->Bandwidth = Buffer[8]; // so scanner knows where we are + + buffptr->Len = sprintf(buffptr->Data, "Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (memcmp(Buffer, "CHECKLEVEL", 10) == 0) + { + CheckMode(TNC); + + buffptr->Len = sprintf(buffptr->Data, "%s\r", &TNC->RXBuffer[2]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (_memicmp(Buffer, "OVERRIDEBUSY", 12) == 0) + { + TNC->OverrideBusy = TRUE; + + buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if ((Stream == 0) && memcmp(Buffer, "RPACKET", 7) == 0) + { + TNC->HFPacket = TRUE; + buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + return; + } + + if ((Stream == 0) && memcmp(Buffer, "PACTOR", 6) == 0) + { + TNC->HFPacket = FALSE; + buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + return; + } + + if (Stream == 0 && Buffer[0] == 'C' && datalen > 2) // Pactor Connect + Poll[2] = TNC->Streams[0].DEDStream = 31; // Pactor Channel + + if (Stream == 0 && Buffer[0] == 'R' && Buffer[1] == 'C') // Robust Packet Connect + { + Poll[2] = TNC->Streams[0].DEDStream = 30; // Last Packet Channel + memmove(Buffer, &Buffer[1], datalen--); + } + + if (Buffer[0] == 'C' && datalen > 2) // Connect + { + if (*(++Buffer) == ' ') Buffer++; // Space isn't needed + + if ((memcmp(Buffer, "P1 ", 3) == 0) ||(memcmp(Buffer, "P2 ", 3) == 0)) + { + // Port Selector for Packet Connect convert to 2:CALL + + Buffer[0] = Buffer[1]; + Buffer[1] = ':'; + memmove(&Buffer[2], &Buffer[3], datalen--); + //Buffer += 2; + } + + memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); + + TNC->Streams[Stream].Connecting = TRUE; + + if (Stream == 0) + { + // Send Call, Mode Command followed by connect + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); + + if (TNC->Dragon) + sprintf(TNC->Streams[0].CmdSet, "I%s\r%s\r", TNC->Streams[0].MyCall, buffptr->Data); + else + { + if (TNC->Streams[0].DEDStream == 31) + sprintf(TNC->Streams[0].CmdSet, "I%s\rPT\r%s\r", TNC->Streams[0].MyCall, buffptr->Data); + else + sprintf(TNC->Streams[0].CmdSet, "I%s\rPR\r%s\r", TNC->Streams[0].MyCall, buffptr->Data); + } + + ReleaseBuffer(buffptr); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Send Mode Command now, save command, and wait up to 10 secs + // No, leave in Pactor, or Busy Detect won't work. Queue the whole conect sequence + + TNC->ConnectCmd = TNC->Streams[0].CmdSet; + TNC->Streams[0].CmdSet = NULL; + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; + TNC->Streams[Stream].Connecting = FALSE; // Not connecting Yet + + return; + } + } + + TNC->OverrideBusy = FALSE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[Stream].MyCall, TNC->Streams[Stream].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[Stream].CmdSet); + + TNC->Streams[0].InternalCmd = FALSE; + return; + } + } + + // Anything else send to tnc. + + Poll[4] = datalen - 1; + memcpy(&Poll[5], buffptr->Data, datalen); + + // if it starts with # the tnc won't respond, so send OK now. + + if (Buffer[0] == '#') + { + TNC->HFPacket = TRUE; + buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + else + ReleaseBuffer(buffptr); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], datalen); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + return; + } + + // if frames outstanding, issue a poll + + if (TNC->Streams[Stream].FramesOutstanding) + { + Poll[2] = TNC->Streams[Stream].DEDStream; + Poll[3] = 0x1; // Command + Poll[4] = 0; // Len-1 + Poll[5] = 'L'; // Status + + CRCStuffAndSend(TNC, Poll, 6); + + TNC->InternalCmd = TRUE; + TNC->IntCmdDelay--; + return; + } + + } + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + if (TNC->ReinitState == 3) + { + TNC->ReinitState = 0; + Poll[3] = 0x41; + } + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + CRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + + return; + +} + +void SCSTryToSendDATA(struct TNCINFO * TNC, int Stream) +{ + // Used after queuing data to see if it can be sent immediately + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->TNCOK == 0 || STREAM->BPQtoPACTOR_Q == 0 || STREAM->Connected == 0) + return; + + + // Dont send to Pactor if waiting for Min Level to be reached + + if (TNC->MinLevelTimer && Stream == 0) + return;; + + Sleep(10); // Give TNC time to respond + + SCSCheckRX(TNC); // See if anything received + + if (TNC->Timeout) + return; // Link busy + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + + datalen = (int)buffptr->Len; + Buffer = buffptr->Data; // Data portion of frame + + Poll[2] = STREAM->DEDStream; // Channel + + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[3] = 0; // Data + STREAM->BytesTXed += datalen; + + Poll[4] = datalen - 1; + memcpy(&Poll[5], Buffer, datalen); + + WritetoTrace(TNC, Buffer, datalen); + + ReleaseBuffer(buffptr); + OpenLogFile(TNC->Port); + + WriteLogLine(TNC->Port, &Poll[5], datalen); + CloseLogFile(TNC->Port); + + CRCStuffAndSend(TNC, Poll, datalen + 5); + + if (STREAM->Disconnecting && STREAM->BPQtoPACTOR_Q == 0) + TidyClose(TNC, Stream); + + return; +} + + +VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + Poll[0] = 13; + Poll[1] = 0x1B; + TNC->TXLen = 2; + + if (WriteCommBlock(TNC) == FALSE) + { + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + } + + TNC->Retries = 1; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + 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 + + Debugprintf("DOTNCReinit Complete - Entering Hostmode"); + + TNC->TXBuffer[2] = 0; + TNC->Toggle = 0; + + memcpy(Poll, "JHOST4\r", 7); + + TNC->TXLen = 7; + WriteCommBlock(TNC); + + // Timeout will enter host mode + + TNC->Timeout = 1; + TNC->Retries = 1; + TNC->Toggle = 0; + TNC->ReinitState = 3; // Set toggle force bit + TNC->OKToChangeFreq = 1; // In case failed whilst waiting for permission + + return; + } + + end = strchr(start, 13); + len = (int)(++end - start); + TNC->InitPtr = end; + memcpy(Poll, start, len); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, Poll, len); + CloseLogFile(TNC->Port); + + TNC->TXLen = len; + WriteCommBlock(TNC); + + TNC->Retries = 2; + } +} + +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; + ExitHost(TNC); + TNC->Retries = 1; + + 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; + TNC->IntCmdDelay = 10; + + return; + } +} + +BOOL CheckRXText(struct TNCINFO * TNC) +{ + int Length; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Length = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Length == 0) + return FALSE; // Nothing doing + + TNC->RXLen += Length; + + Length = TNC->RXLen; + + TNC->RXBuffer[TNC->RXLen] = 0; + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) + return 0; // Wait for rest of frame + + // Complete Char Mode Frame + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, TNC->RXBuffer, TNC->RXLen); + CloseLogFile(TNC->Port); + + TNC->RXBuffer[TNC->RXLen] = 0; + + if (TNC->RIG->RIG_DEBUG) + Debugprintf(TNC->RXBuffer); + + TNC->RXLen = 0; // Ready for next frame + + return TRUE; +} + +BOOL CheckRXHost(struct TNCINFO * TNC, char * UnstuffBuffer) +{ + int Length; + unsigned short crc; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Length = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Length == 0) + return FALSE; // Nothing doing + + TNC->RXLen += Length; + + Length = TNC->RXLen; + + if (Length < 6) // Minimum Frame Sise + return FALSE; + + if (TNC->RXBuffer[2] == 170) + { + // Retransmit Request + + TNC->RXLen = 0; + return FALSE; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + TNC->RXLen = 0; + return FALSE; // Ignore for now + } + + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + TNC->RXLen = 0; // Ready for next frame + return TRUE; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return FALSE; +} + + +//#include "Mmsystem.h" + +int Sleeptime = 250; + +int CheckMode(struct TNCINFO * TNC) +{ + int n; + char UnstuffBuffer[500]; + + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->HostMode == 0) + return 0; // Don't try if initialising + + TNC->EnterExit = TRUE; + + Poll[2] = 31; + Poll[3] = 0x41; + Poll[4] = 0x5; + memcpy(&Poll[5], "JHOST0", 6); + + CRCStuffAndSend(TNC, Poll, 11); + + n = 0; + + while (CheckRXHost(TNC, UnstuffBuffer) == FALSE) + { + Sleep(5); + n++; + + if (n > 100) break; + } + + + sprintf(Poll, "MYL\r"); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, Poll, 3); + CloseLogFile(TNC->Port); + + TNC->TXLen = 4; + WriteCommBlock(TNC); + + n = 0; + + while (CheckRXText(TNC) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + memcpy(Poll, "JHOST4\r", 7); + + TNC->TXLen = 7; + WriteCommBlock(TNC); + + // No response expected + + Sleep(10); + + Poll[2] = 255; // Channel + TNC->Toggle = 0; + Poll[3] = 0x41; + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + CRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + TNC->Timeout = 5; // 1/2 sec - In case missed + + TNC->EnterExit = FALSE; + return 0; +} + + +int Switchmode(struct TNCINFO * TNC, int Mode) +{ + int n; + char UnstuffBuffer[500]; + + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->HostMode == 0) + return 0; // Don't try if initialising + + if (TNC->HFPacket) + { + Poll[2] = 31; + Poll[3] = 0x1; + Poll[4] = 0x1; + memcpy(&Poll[5], "PT", 2); + CRCStuffAndSend(TNC, Poll, 7); + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, "SwitchModes - Setting Pactor", 28); + CloseLogFile(TNC->Port); + + TNC->HFPacket = FALSE; + TNC->Streams[0].DEDStream = 31; // Pactor Channel + + n = 0; + while (CheckRXHost(TNC, UnstuffBuffer) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + +// Debugprintf("Set Pactor ACK received in %d mS, Sleeping for %d", 5 * n, Sleeptime); + Sleep(Sleeptime); + } + + // Uses "Hidden" feature where you can send any normal mode command + // in host mode by preceeding with a # + + Poll[2] = 31; + Poll[3] = 0x1; + Poll[4] = 5; + sprintf(&Poll[5], "#MYL %d\r", Mode); + CRCStuffAndSend(TNC, Poll, 11); + + // It looks like there isn't a response + + TNC->Timeout = 0; + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], 6); + CloseLogFile(TNC->Port); + + return 0; +} + +VOID SwitchToPactor(struct TNCINFO * TNC) +{ + if (TNC->ForceRobust) + return; + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); + sprintf(TNC->Streams[0].CmdSet, "PT\r"); + + TNC->HFPacket = FALSE; + TNC->Streams[0].DEDStream = 31; // Pactor Channel + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("BPQ32 Scan - switch to Pactor"); +} + +VOID SwitchToPacket(struct TNCINFO * TNC) +{ + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); + sprintf(TNC->Streams[0].CmdSet, "PR\r"); + + TNC->HFPacket = TRUE; + TNC->Streams[0].DEDStream = 30; // Packet Channel + + TNC->SwitchToPactor = TNC->RobustTime; + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("BPQ32 Scan - switch to Packet"); +} + +VOID ExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x41; + TNC->TXBuffer[4] = 0x5; + memcpy(&TNC->TXBuffer[5], "JHOST0", 6); + + CRCStuffAndSend(TNC, Poll, 11); + return; +} + +VOID CRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + Msg[3] |= TNC->Toggle; + TNC->Toggle ^= 0x80; + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + TNC->TXLen = Len; + + Msg[0] = 170; + Msg[1] = 170; + + WriteCommBlock(TNC); + + TNC->Retries = 5; +} + +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len) +{ + int i, j=0; + + for (i=0; iTXBuffer; + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + + // Send Restart to make sure PTC is in a known state + + strcpy(Poll, "RESTART\r"); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, Poll, 7); + CloseLogFile(TNC->Port); + + TNC->TXLen = 8; + WriteCommBlock(TNC); + + TNC->Timeout = 60; // 6 secs + + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + if (strstr(TNC->RXBuffer, "SCS P4dragon")) + { + TNC->Dragon = TRUE; + Debugprintf("SCSPactor in P4dragon mode"); + } + + DoTNCReinit(TNC); // Send Next Command + return; + } +} + +VOID ProcessIncomingCall(struct TNCINFO * TNC, struct STREAMINFO * STREAM, int Stream) +{ + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + char FreqAppl[10] = ""; // Frequecy-specific application + char DestCall[10]; + TRANSPORTENTRY * SESS; + struct WL2KInfo * WL2K = TNC->WL2K; + UCHAR * ptr; + UCHAR Buffer[80]; + PMSGWITHLEN buffptr; + BOOL PactorCall = FALSE; + + char * Call = STREAM->RemoteCall; + + if (Stream > 0 && Stream < 30) + ProcessIncommingConnectEx(TNC, Call, Stream, FALSE, TRUE); // No CTEXT + else + ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + if (SESS == NULL) + return; // Cant do much without one + + if (Stream > 0 && Stream < 30) + { + // Packet Connect. Much safer to process here, even though it means + // duplicating some code, or the Pactor/RP mode tests get very complicated + + int Len = 0; + struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; + + strcpy(DestCall, STREAM->MyCall); + Debugprintf("PTC Packet Incoming Call - MYCALL = *%s*", DestCall); + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(DestCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + Debugprintf("Connect is to APPL %s", AppName); + + if (CheckAppl(TNC, AppName)) + { + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = MsgLen; + + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = (int)strlen(Msg); + memcpy(buffptr->Data, Msg, strlen(Msg)); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->NeedDisc = 100; // 10 secs + } + return; + } + + // Not to a known appl - drop through to Node + + if (PORT->CTEXT) + { + Len = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (CTEXTLEN) + { + Len = CTEXTLEN; + ptr = CTEXTMSG; + } + else + return; + + while (Len > 0) + { + int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + + if (sendLen == 0) + sendLen = 80; + + if (Len < sendLen) + sendLen = Len; + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sendLen; + memcpy(buffptr->Data, ptr, sendLen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + ptr += sendLen; + Len -= sendLen; + } + return; + } + + //Connect on HF port. May be Pactor or RP on some models + + if (STREAM->DEDStream == 31) + PactorCall = TRUE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", STREAM->RemoteCall, TNC->NodeCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + + // If Scan Entry has a Appl, save it + + if (PactorCall && 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", STREAM->RemoteCall, TNC->NodeCall); + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SESS->Mode = PleveltoMode[TNC->Streams[Stream].PTCStatus1]; + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (PactorCall && TNC->MinLevel > 1) + TNC->MinLevelTimer = 150; // Check we have reached right level + + // See which application the connect is for + + strcpy(DestCall, STREAM->MyCall); + + if (PactorCall) + Debugprintf("Pactor Incoming Call - MYCALL = *%s*", DestCall); + else + Debugprintf("HF Packet/RP Incoming Call - MYCALL = *%s*", DestCall); + + if ((PactorCall && TNC->UseAPPLCallsforPactor) || (PactorCall == 0 && TNC->UseAPPLCalls)) + // Test for Richard - Should drop through to Node if not to an APPLCALL + //&& strcmp(DestCall, TNC->NodeCall) != 0) // Not Connect to Node Call + { + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(DestCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + Debugprintf("Connect is to APPL %s", AppName); + + if (CheckAppl(TNC, AppName)) + { + int MsgLen = sprintf(Buffer, "%s\r", AppName); + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = strlen(Msg); + memcpy(buffptr->Data, Msg, strlen(Msg)); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->NeedDisc = 100; // 10 secs + } + return; + } + + // Not to a known appl - drop through to Node + } + + if (!PactorCall && TNC->UseAPPLCalls) + goto DontUseAPPLCmd; // Don't use APPL= for Packet Calls + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && strcmp(FreqAppl, "RMS") == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(FreqAppl, "RELAY"); + + Debugprintf("Pactor Call is %s Freq Specific Appl is %s Freq is %s", + DestCall, FreqAppl, TNC->RIG->Valchar); + + if (FreqAppl[0]) // Frequency specific APPL overrides TNC APPL + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + Debugprintf("Using Freq Specific Appl %s", FreqAppl); + + buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + char App[16]; + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + strcpy(App, TNC->ApplCmd); + + Debugprintf("Using Default Appl *%s*, connecting call is %s", App, Call); + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(App, "RMS", 3) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + { + strcpy(App, "RELAY"); + Debugprintf("Radio Only Call - Connecting to RELAY"); + } + + buffptr->Len = sprintf(buffptr->Data, "%s\r", App); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + +DontUseAPPLCmd: + + if (FULL_CTEXT && CTEXTLEN && HFCTEXTLEN == 0) + { + int Len = CTEXTLEN, CTPaclen = 100; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = CTPaclen; + memcpy(buffptr->Data, &CTEXTMSG[Next], CTPaclen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + Next += CTPaclen; + Len -= CTPaclen; + } + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = Len; + memcpy(buffptr->Data, &CTEXTMSG[Next], Len); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } +} + +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen) +{ + PMSGWITHLEN buffptr; + UCHAR * Buffer; // Data portion of frame + char Status[80]; + unsigned int Stream = 0, RealStream; + + if (TNC->HostMode == 0) + return; + + // Any valid frame is an ACK + + TNC->Timeout = 0; + + if (TNC->TNCOK == FALSE) + { + // Just come up + + TNC->TNCOK = TRUE; + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + Stream = RealStream = Msg[2]; + + if (Stream > 29) + Stream = 0; // 31 = Pactor or 30 = Robust Packet Outgoing + + // if in Dragon Single Mode (Pactor and Packet on Same Port) + // we only use stream 0, so if a packet frame, set DEDStream + + // Im not convinced this is the bast place to do this, but let's try + + if (TNC->DragonSingle && RealStream && RealStream < 31) // Not a Pactor or control frame + { + // must be packet + + TNC->Streams[0].DEDStream = RealStream; // Packet Channel + Stream = 0; + } + + if (TNC->TXBuffer[5] == '#') // Shouldnt happen! + return; + + + // See if Poll Reply or Data + + if (Msg[3] == 0) + { + // Success - Nothing Follows + + if (Stream < 32) + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set + + if ((TNC->TXBuffer[3] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[5] == 'G') // Poll + return; + + if (TNC->TXBuffer[5] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[5] == 'L') // Shouldnt happen! + return; + + if (TNC->TXBuffer[5] == '#') // Shouldnt happen! + return; + + if (TNC->TXBuffer[5] == '%' && TNC->TXBuffer[6] == 'W') // Scan Control - Response to W1 + if (TNC->InternalCmd) + return; // Just Ignore + + if (TNC->TXBuffer[5] == 'J') // JHOST + { + if (TNC->TXBuffer[10] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->Streams[Stream].Connected) + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"Pactor} Ok\r"); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, buffptr->Data, (int)buffptr->Len); + CloseLogFile(TNC->Port); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return; + } + + if (Msg[3] > 0 && Msg[3] < 6) + { + // Success with message - null terminated + + UCHAR * ptr; + int len; + + if (Msg[2] == 0xff) // General Poll Response + { + UCHAR * Poll = TNC->TXBuffer; + UCHAR Chan = Msg[4] - 1; + + if (Chan == 255) // Nothing doing + return; + + if (Msg[5] != 0) + { + // More than one to poll - save the list of channels to poll + + strcpy(TNC->NexttoPoll, &Msg[5]); + } + + // Poll the channel that had data + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + CRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return; + } + + Buffer = &Msg[4]; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return; + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (Msg[3] < 3) // 1 or 2 - Success or Fail + { + char LastCmd = TNC->TXBuffer[5]; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + // See if a response to internal command + + if (TNC->RIG->RIG_DEBUG) + if (LastCmd == 'I') + Debugprintf("SCS I Cmd Response %s", Buffer); + + if (LastCmd == 'I' && STREAM->CheckingCall == TRUE) + { + // We've received a connect and are checking MYCALL + + Debugprintf("SCS Incoming Call I Cmd Response %s Stream %d DED Stream %d", Buffer, Stream, RealStream); + + strlop(Buffer, 13); + strcpy(STREAM->MyCall, Buffer); + + ProcessIncomingCall(TNC, STREAM, Stream); + STREAM->CheckingCall = FALSE; + return; + } + + if (TNC->InternalCmd) + { + // Process it + + if (LastCmd == 'L') // Status + { + int s1, s2, s3, s4, s5, s6, num; + + num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6); + + TNC->Streams[Stream].FramesOutstanding = s3; + + // flow control debug + + sprintf(TNC->WEB_BUFFERS, "%d Q %d", TNC->Buffers, TNC->Streams[0].FramesOutstanding); + SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS); + + return; + } + + if (LastCmd == '@') // @ Commands + { + if (TNC->TXBuffer[6]== 'B') // Buffer Status + { + TNC->Buffers = atoi(Buffer); + sprintf(TNC->WEB_BUFFERS, "%d Q %d", TNC->Buffers, TNC->Streams[0].FramesOutstanding); + SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS); + return; + } + } + + if (LastCmd == '%') // % Commands + { + if (TNC->TXBuffer[6]== 'T') // TX count Status + { + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %s", TNC->Streams[Stream].BytesRXed, TNC->Streams[Stream].BytesTXed, Buffer); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + return; + } + + if (TNC->TXBuffer[6] == 'W') // Scan Control + { + if (Msg[4] == '1') // Ok to Change + { + TNC->OKToChangeFreq = 1; + TNC->TimeScanLocked = 0; + if (TNC->RIG->RIG_DEBUG) + Debugprintf("Scan Debug SCS Pactor TNC gave permission"); + } + else + { + TNC->OKToChangeFreq = -1; + if (TNC->SyncSupported == FALSE && TNC->UseAPPLCallsforPactor && TNC->TimeScanLocked == 0) + TNC->TimeScanLocked = time(NULL); + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("Scan Debug SCS Pactor TNC refused permission"); + + } + } + } + return; + } + } + + if (Msg[3] == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE")) + { + if ((STREAM->Connecting | STREAM->Connected) == 0) + 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", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesOutstanding = 0; + + if (Stream == 0) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + + STREAM->ReportDISC = TRUE; // Tell Node + 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; + return; + } + + if (strstr(Buffer, "CONNECTED")) + { + char * Call = strstr(Buffer, " to "); + char * ptr; + char MHCall[30]; + + // Do we need to protect against 2nd call in Dragon Single Mode??? + + Call += 4; + + if (Call[1] == ':') + Call +=2; + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strchr(Call, 13); + if (ptr) *ptr = 0; + + STREAM->Connected = TRUE; // Subsequent data to data channel + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + // Stop Scanner + + if (Stream == 0 || TNC->HFPacket) + { + TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch + + sprintf(Status, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Status); + + SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop + + memcpy(MHCall, Call, 9); + MHCall[9] = 0; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Check for ExcludeList + + if (ExcludeList[0]) + { + UCHAR AXCALL[7]; + + ConvToAX25(MHCall, AXCALL); //Permitted calls are stored in ax.25 format + + if (CheckExcludeList(AXCALL) == FALSE) + { + TidyClose(TNC, Stream); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("SCS Call from %s rejected", MHCall); + 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; + UCHAR AXCALL[7]; + + ConvToAX25(MHCall, AXCALL); //Permitted calls are stored in ax.25 format + + while (TRUE) + { + if (memcmp(AXCALL, 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(-1, Status); + Debugprintf("Pactor Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + // Check that we think we are in the right mode + + if (Stream == 0 && TNC->Dragon == 0) // Dragon runs both at the same time + { + if (TNC->HFPacket && RealStream == 31) + { + Debugprintf("Incoming Pactor Call while in Packet Mode"); + TNC->HFPacket = FALSE; + STREAM->DEDStream = 31; + } + else + if (TNC->HFPacket == 0 && RealStream == 30) + { + Debugprintf("Incoming Packet Call while in Pactor Mode"); + TNC->HFPacket = TRUE; + STREAM->DEDStream = 30; + } + } + + if (TNC->HFPacket) + { + char Save = TNC->RIG->CurrentBandWidth; + TNC->RIG->CurrentBandWidth = 'R'; + UpdateMH(TNC, MHCall, '+', 'I'); + TNC->RIG->CurrentBandWidth = Save; + } + + memcpy(STREAM->RemoteCall, Call, 9); // Save Text Callsign + + // We need to check what MYCALL is set to, either in case + // Appl Scan has failed to change the callsign or if a + // Packet Call to MYALIAS + + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + sprintf(STREAM->CmdSet, "I\r"); + + STREAM->CheckingCall = TRUE; + 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); + + if (Stream == 0) + { + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (STREAM->DEDStream == 30) // Robust Mode + { + char Save = TNC->RIG->CurrentBandWidth; + TNC->RIG->CurrentBandWidth = 'R'; + UpdateMH(TNC, Call, '+', 'O'); + TNC->RIG->CurrentBandWidth = Save; + } + else + { + UpdateMH(TNC, Call, '+', 'O'); + } + } + return; + } + } + return; + } + + if (Msg[3] == 4 || Msg[3] == 5) + { + struct STREAMINFO * STREAM = &TNC->Streams[1]; // RP Stream + + // Monitor + + if ((TNC->HFPacket || TNC->DragonSingle) && TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Connected == FALSE) + { + // See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL + + char DestCall[10] = "NOCALL "; + char * ptr1 = strstr(&Msg[7], "to "); + int i; + APPLCALLS * APPL; + char Appl[11]; + char Status[80]; + + if (ptr1) memcpy(DestCall, &ptr1[3], 10); + + ptr1 = strchr(DestCall, ' '); + if (ptr1) *(ptr1) = 0; // Null Terminate + + Debugprintf("RP SABM Received for %s" , DestCall); + + if (strcmp(TNC->NodeCall, DestCall) != 0) + { + // Not Calling NodeCall/Portcall + + if (strcmp(NodeCall, DestCall) == 0) + goto SetThisCall; + + // See if to one of our ApplCalls + + 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 = 0; + + if (strcmp(Appl, DestCall) == 0) + { + SetThisCall: + Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall); + + sprintf(Status, "%d SCANSTART 30", TNC->Port); + Rig_Command(-1, Status); + TNC->SwitchToPactor = 0; // Stay in RP + + strcpy(STREAM->MyCall, DestCall); + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + sprintf(STREAM->CmdSet, "I%s\r", DestCall); + TNC->InternalCmd = TRUE; + + break; + } + } + } + } + } + + DoMonitor(TNC, &Msg[3], framelen - 3); + return; + + } + + // 1, 2, 4, 5 - pass to Appl + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"Pactor} %s", &Msg[4]); + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Msg[4], (int)strlen(&Msg[4])); + CloseLogFile(TNC->Port); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (Msg[3] == 6) + { + // Monitor Data With length) + + DoMonitor(TNC, &Msg[3], framelen - 3); + return; + } + + if (Msg[3] == 7) + { + char StatusMsg[60]; + int Status, ISS, Offset; + + if (Msg[2] == 0xfe) // Status Poll Response + { + int PactorLevel = Msg[6] & 7; // Pactor Level 1-4 + + if (TNC->MinLevelTimer) + { + if (PactorLevel >= TNC->MinLevel) + { + Debugprintf("Reached MIN Pactor Level"); + TNC->MinLevelTimer = 0; + } + else + Debugprintf("Still waiting for Min Level Now %d Need %d", PactorLevel, TNC->MinLevel); + } + + Status = Msg[5]; + + TNC->Streams[0].PTCStatus0 = Status; + TNC->Streams[0].PTCStatus1 = PactorLevel; // Pactor Level 1-4 + TNC->Streams[0].PTCStatus2 = Msg[7]; // Speed Level + Offset = Msg[8]; + + if (Offset > 128) + Offset -= 128; + + TNC->Streams[0].PTCStatus3 = Offset; + + TNC->Mode = (Status >> 4) & 7; + ISS = Status & 8; + Status &= 7; + + if (TNC->PTCStatus != Status) // Changed + { + Debugprintf("SCS status changed, now %s", status[Status]); + + if (Status == 6) // SYNCH + { + // New Sync + + if (TNC->RIG->RIG_DEBUG) + Debugprintf("SCS New SYNC Detected"); + + TNC->TimeEnteredSYNCMode = time(NULL); + TNC->SyncSupported = TRUE; + } + else + { + if (TNC->PTCStatus == 6) + { + if (TNC->RIG->RIG_DEBUG) + Debugprintf("SCS left SYNC, now %s", status[Status]); + + TNC->TimeEnteredSYNCMode = 0; + } + } + TNC->PTCStatus = Status; + } + sprintf(StatusMsg, "%x %x %x %x", TNC->Streams[0].PTCStatus0, + TNC->Streams[0].PTCStatus1, TNC->Streams[0].PTCStatus2, TNC->Streams[0].PTCStatus3); + + if (ISS) + { + SetWindowText(TNC->xIDC_TXRX, "Sender"); + strcpy(TNC->WEB_TXRX, "Sender"); + } + else + { + SetWindowText(TNC->xIDC_TXRX, "Receiver"); + strcpy(TNC->WEB_TXRX, "Receiver"); + } + + SetWindowText(TNC->xIDC_STATE, status[Status]); + strcpy(TNC->WEB_STATE, status[Status]); + SetWindowText(TNC->xIDC_MODE, ModeText[TNC->Mode]); + strcpy(TNC->WEB_MODE, ModeText[TNC->Mode]); + + if (TNC->Mode == 7) + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (Offset == 128) // Undefined + sprintf(StatusMsg, "Mode %s Speed Level %d Freq Offset Unknown", + PactorLevelText[TNC->Streams[0].PTCStatus1], Msg[7]); + else + sprintf(StatusMsg, "Mode %s Speed Level %d Freq Offset %d", + PactorLevelText[TNC->Streams[0].PTCStatus1], Msg[7], Offset); + + strcpy(TNC->WEB_PACTORLEVEL, StatusMsg); + SetWindowText(TNC->xIDC_PACTORLEVEL, StatusMsg); + + return; + } + + if (Msg[2] == 248) // Log Message + { + // Monitor Data - Length format + // first 4 bytes contain a 32 bits long timestamp. + // That timestamp holds the number of seconds that elapsed since date 01.01.2000 at 00:00:00. + // The MS byte is sent first. The timestamp can be corrected to the usual C timestamp (seconds + //since 01.01.1970, 00:00:00) simply by adding 946684800 (seconds) to it. + // Teminated with LF + + int datalen = Msg[4] + 1; + unsigned int timestamp = (Msg[5] << 24) + (Msg[6] << 16) + + (Msg[6] << 8) + Msg[7] + 946684800; + + Msg[5 + datalen] = 0; + Debugprintf("SCS Debug %s", &Msg[9]); + return; + } + + if (Msg[2] == 253) // Rig Port Response + { + // Queue for Rig Control Driver + + int datalen = Msg[4] + 1; + PMSGWITHLEN buffptr; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return; + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = datalen; + memcpy(buffptr->Data, &Msg[5], datalen); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + if (TNC->RIG->RIG_DEBUG) + { + Debugprintf("SCS RIG frame received, len %d", datalen); + Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + Msg[5], Msg[6], Msg[7], Msg[8], Msg[9], Msg[10], Msg[11], Msg[12], + Msg[13], Msg[14], Msg[15], Msg[16], Msg[17], Msg[18], Msg[19], Msg[20]); + + } + } + return; + } + + // Connected Data + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = Msg[4] + 1; // Length + TNC->Streams[Stream].BytesRXed += (int)buffptr->Len; + memcpy(buffptr->Data, &Msg[5], buffptr->Len); + + WritetoTrace(TNC, &Msg[5], (int)buffptr->Len); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } +} + +int GetPTCRadioCommand(struct TNCINFO * TNC, char * Block) +{ + PMSGWITHLEN buffptr; + int Length; + + if (TNC->RadiotoBPQ_Q == 0) + return 0; + + buffptr = Q_REM(&TNC->RadiotoBPQ_Q); + + Length = (int)buffptr->Len; + + memcpy(Block, buffptr->Data, Length); + + ReleaseBuffer(buffptr); + +// Debugprintf("SCS Rig Command Queued"); + + return Length;; +} + +int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length) +{ + PMSGWITHLEN buffptr; + + if (TNC->TNCOK || (TNC->Hardware == H_ARDOP && TNC->ARDOPCommsMode == 'T')) + { + } + else + return 0; + + // Queue for TNC + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = Length; + + memcpy(buffptr->Data, Block, Length); + + C_Q_ADD(&TNC->BPQtoRadio_Q, buffptr); + + return 0; + +} + +static MESSAGE Monframe; // I frames come in two parts. + +#define TIMESTAMP 352 + +MESSAGE * AdjMsg = &Monframe; // Adjusted for digis + +static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + // Convert to ax.25 form and pass to monitor + + UCHAR * ptr, * starptr; + char * context; + char * MHCall = Monframe.ORIGIN; + + + if (Msg[0] == 6) // Second part of I or UI + { + int len = Msg[1] +1; + + memcpy(AdjMsg->L2DATA, &Msg[2], len); + Monframe.LENGTH += len; + + time(&Monframe.Timestamp); + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + return; + } + + Monframe.LENGTH = MSGHDDRLEN + 16; // Control Frame + + Monframe.PORT = TNC->Port; + + AdjMsg = &Monframe; // Adjusted for digis + ptr = strstr(Msg, "fm "); + + ConvToAX25(&ptr[3], Monframe.ORIGIN); + + ptr = strstr(ptr, "to "); + + ConvToAX25(&ptr[3], Monframe.DEST); + + ptr = strstr(ptr, "via "); + + if (ptr) + { + // We have digis + + char Save[100]; + char * fiddle; + + memcpy(Save, &ptr[4], 60); + + ptr = strtok_s(Save, " ", &context); +DigiLoop: + fiddle = (char *)AdjMsg; + fiddle += 7; + AdjMsg = (MESSAGE *)fiddle; + + Monframe.LENGTH += 7; + + starptr = strchr(ptr, '*'); + if (starptr) + *(starptr) = 0; + + ConvToAX25(ptr, AdjMsg->ORIGIN); + + if (starptr) + AdjMsg->ORIGIN[6] |= 0x80; // Set end of address + + ptr = strtok_s(NULL, " ", &context); + + if (memcmp(ptr, "ctl", 3)) + goto DigiLoop; + } + + AdjMsg->ORIGIN[6] |= 1; // Set end of address + + ptr = strstr(Msg, "ctl "); + + if (memcmp(&ptr[4], "SABM", 4) == 0) + { + AdjMsg->CTL = 0x2f; + UpdateMHwithDigis(TNC, MHCall, '.', 0); + } + else + if (memcmp(&ptr[4], "DISC", 4) == 0) + AdjMsg->CTL = 0x43; + else + if (memcmp(&ptr[4], "UA", 2) == 0) + { + AdjMsg->CTL = 0x63; + UpdateMHwithDigis(TNC, MHCall, '.', 0); + } + else + if (memcmp(&ptr[4], "DM", 2) == 0) + AdjMsg->CTL = 0x0f; + else + if (memcmp(&ptr[4], "UI", 2) == 0) + { + AdjMsg->CTL = 0x03; + UpdateMHwithDigis(TNC, MHCall, '.', 0); + } + else + if (memcmp(&ptr[4], "RR", 2) == 0) + AdjMsg->CTL = 0x1 | (ptr[6] << 5); + else + if (memcmp(&ptr[4], "RNR", 3) == 0) + AdjMsg->CTL = 0x5 | (ptr[7] << 5); + else + if (memcmp(&ptr[4], "REJ", 3) == 0) + AdjMsg->CTL = 0x9 | (ptr[7] << 5); + else + if (memcmp(&ptr[4], "FRMR", 4) == 0) + AdjMsg->CTL = 0x87; + else + if (ptr[4] == 'I') + { + AdjMsg->CTL = (ptr[5] << 5) | (ptr[6] & 7) << 1 ; + } + + if (strchr(&ptr[4], '+')) + { + AdjMsg->CTL |= 0x10; + Monframe.DEST[6] |= 0x80; // SET COMMAND + } + + if (strchr(&ptr[4], '-')) + { + AdjMsg->CTL |= 0x10; + Monframe.ORIGIN[6] |= 0x80; // SET COMMAND + } + + if (Msg[0] == 5) // More to come + { + ptr = strstr(ptr, "pid "); + sscanf(&ptr[3], "%x", (int *)&AdjMsg->PID); + return; + } + + time(&Monframe.Timestamp); + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + +} +//1:fm G8BPQ to KD6PGI-1 ctl I11^ pid F0 +//fm KD6PGI-1 to G8BPQ ctl DISC+ + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // Queue it as we may have just sent data + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = malloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "D\r"); +} + + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + // Sending D twice should do a "Dirty Disconnect" + + // Try thst first. If it still doesn't disconnect maybe try restart + + unsigned char Resp[500] = ""; + char * Poll = &TNC->TXBuffer[0]; + + Debugprintf("Failed to disconnect TNC - trying a forced disconnect"); + + // Try Normal Mode DD (Dirty Disconnect) + + // Uses "Hidden" feature where you can send any normal mode command + // in host mode by preceeding with a # + + Poll[2] = 31; + Poll[3] = 0x1; + Poll[4] = 2; + sprintf(&Poll[5], "#DD\r"); // Node \r isn't sent but is there for log + CRCStuffAndSend(TNC, Poll, 8); + + // It looks like there isn't a response + + TNC->Timeout = 0; + + OpenLogFile(TNC->Port); + WriteLogLine(TNC->Port, &Poll[5], 4); + CloseLogFile(TNC->Port); + +/* + Poll[2] = 31; + Poll[3] = 1; + Poll[4] = 0; + Poll[5] = 'D'; + + CRCStuffAndSend(TNC, Poll, 6); + + // Wait for response before sending another + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + Poll[2] = 31; + Poll[3] = 1; + Poll[4] = 0; + Poll[5] = 'D'; + + CRCStuffAndSend(TNC, Poll, 6); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + // See if it worked + + Poll[2] = 254; // Channel + Poll[3] = 0x1; // Command + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + + CRCStuffAndSend(TNC, Poll, 7); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + Debugprintf("PTC Status Now %x %x %x %x %x %x %x %x", + Resp[0], Resp[1], Resp[2], Resp[3], Resp[4], Resp[5], Resp[6], Resp[7]); + + TNC->Timeout = 0; + + return; + + // Maybe best just to restart the TNC + + if (TNC->PacketChannels == 0) // Not using packet + { + Debugprintf("Forced Disconnect Failed - restarting TNC"); + + // Ensure in Pactor + + if(TNC->Dragon == 0) + { + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x1; + TNC->TXBuffer[4] = 0x1; + memcpy(&TNC->TXBuffer[5], "PT", 2); + + CRCStuffAndSend(TNC, TNC->TXBuffer, 7); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + } + + Sleep(50); + ExitHost(TNC); + Sleep(50); + + n = 0; + while (CheckRXHost(TNC, Resp) == FALSE) + { + Sleep(5); + n++; + if (n > 100) break; + } + + TNC->Timeout = 0; + TNC->HostMode = FALSE; + TNC->ReinitState = 0; + + return; + } +*/ +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Status[80]; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + Debugprintf("SCS Pactor Close Complete - Stream = %d", Stream); + + STREAM->CmdSet = STREAM->CmdSave = malloc(100); + + strcpy(STREAM->MyCall, TNC->NodeCall); + + if (Stream == 0 || TNC->HFPacket) + { + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + strcpy(TNC->WEB_TNCSTATE, "Free"); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + + if (TNC->Dragon) + { + sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); + TNC->Streams[0].DEDStream = 31; // Pactor Channel + } + else + { + if (TNC->HFPacket) + { + sprintf(STREAM->CmdSet, "I%s\rPR\r", TNC->NodeCall); + TNC->Streams[0].DEDStream = 30; // Packet Channel + Debugprintf("BPQ32 Session Closed - switch to Packet"); + } + else + { + sprintf(STREAM->CmdSet, "I%s\rPT\r", TNC->NodeCall); + TNC->Streams[0].DEDStream = 31; // Pactor Channel + Debugprintf("BPQ32 Session Closed - switch to Pactor"); + } + } + ReleaseOtherPorts(TNC); + } + else + sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); + +} + +VOID PTCSuspendPort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); +} + +VOID PTCReleasePort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + + if (TNC->UseAPPLCallsforPactor && TNC->RIG && TNC->RIG != &TNC->DummyRig + && TNC->RIG->FreqPtr[0]->APPLCALL[0]) + sprintf(STREAM->CmdSet, "I%s\r", TNC->RIG->FreqPtr[0]->APPLCALL); + else + sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); + + Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); +} + + + diff --git a/SCSTrackeMulti.c b/SCSTrackeMulti.c new file mode 100644 index 0000000..97bff75 --- /dev/null +++ b/SCSTrackeMulti.c @@ -0,0 +1,1713 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface DED Host Mode TNCs to BPQ32 switch +// +// Uses BPQ EXTERNAL interface + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#define MaxStreams 10 + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +static char ClassName[]="TRACKERSTATUS"; +static char WindowTitle[] = "SCS Tracker"; +static int RigControlRow = 140; + +#define NARROWMODE 30 +#define WIDEMODE 30 // PIII only + +extern UCHAR BPQDirectory[]; + +extern char * PortConfig[33]; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +VOID __cdecl Debugprintf(const char * format, ...); +char * strlop(char * buf, char delim); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); + +char NodeCall[11]; // Nodecall, Null Terminated +void WriteDebugLogLine(int Port, char Dirn, char * Msg, int MsgLen); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + char * p_port = 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; + + TNC->PacketChannels = 10; // Default + + goto ConfigLine; + + 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, "APPL", 4) == 0) + { + } + else + if (_memicmp(buf, "RIGCONTROL", 10) == 0) + { + } + else + + if (_memicmp(buf, "SWITCHMODES", 11) == 0) + { + } + else + if (_memicmp(buf, "USEAPPLCALLS", 12) == 0) + { +// TNC->UseAPPLCalls = TRUE; + } + else if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log + TNC->WRITELOG = atoi(&buf[9]); + else + if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) + { + } + else + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + } + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "PACKETCHANNELS", 14) == 0) + + // Packet Channels + + TNC->PacketChannels = atoi(&buf[14]); + else + strcat (TNC->InitScript, buf); + } + return (TRUE); + +} + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +static void DEDCheckRX(struct TNCINFO * TNC); +static VOID DEDPoll(int Port); +VOID StuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); +unsigned short int compute_crc(unsigned char *buf,int len); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +static VOID ProcessDEDFrame(struct TNCINFO * TNC); +static VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID ExitHost(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID DoMonitorHddr(struct TNCINFO * TNC, UCHAR * Msg, int Len, int Type); +VOID DoMonitorData(struct TNCINFO * TNC, UCHAR * Msg, int Len); +int Switchmode(struct TNCINFO * TNC, int Mode); +VOID SwitchToRPacket(struct TNCINFO * TNC); +VOID SwitchToNormPacket(struct TNCINFO * TNC); + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + int txlen = 0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + 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); + } + + if (fn > 3 && fn < 6) + goto ok; + + // Try to reopen every 30 secs + + 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 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + DEDCheckRX(TNC); + DEDPoll(port); + DEDCheckRX(TNC); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen=buffptr->Len; + + buff->PORT = Stream; + buff->PID = 0xf0; + memcpy(buff->L2DATA, buffptr->Data, datalen); // Data goes t, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer((PDATAMESSAGE)buff, datalen); // Neded for arm5 portability + + // buff[5]=(datalen & 0xff); + // buff[6]=(datalen >> 8); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = sprintf(buffptr->Data, "No Connection to TNC\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - (sizeof(void *) + 4);; + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + TNC->Streams[Stream].FramesOutstanding++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + TNCOK = (TNC->HostMode == 1 && TNC->ReinitState != 10); + + STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + if (STREAM->FramesOutstanding > 4) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + } + else + { + if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); } + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + + case 4: // reinit + + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) 0; + TNC->ReopenTimer = 250; + TNC->HostMode = FALSE; + + return (0); + + case 5: // Close + + // Ensure in Pactor + + ExitHost(TNC); + + Sleep(25); + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: + + return 0; // No scan interface +} + return 0; +} + +void * TrackerMExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int Stream = 0; + char * TempScript; + char YCmd[10]; + + // + // Will be called once for each DED Host TNC Port + // The COM port number is in IOBASE + // + + sprintf(msg,"SCSTRK M %s", PortEntry->PORTCONTROL.SerialPortName); + + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_TRKM; + + // Set up DED addresses for streams + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; // DED Stream = BPQ Stream (We don't use Stream 0) + } + + if (TNC->PacketChannels > MaxStreams) + TNC->PacketChannels = MaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; //TNC->PacketChannels + 1; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = NONE; // Scan Control 3 stage/conlock + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->PORTCONTROL.UICAPABLE = 1; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // get NODECALL for RP tests + + memcpy(NodeCall, MYNODECALL, 10); + + ptr=strchr(NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TempScript = malloc(1000); + + strcpy(TempScript, "M UISC\r"); + strcat(TempScript, "F 200\r"); // Sets SABM retry time to about 5 secs + strcat(TempScript, "%F 1500\r"); // Tones may be changed but I want this as standard + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "Z 0\r"); // No Flow Control + sprintf(YCmd, "Y %d\r", TNC->PacketChannels); + strcat(TNC->InitScript, YCmd); + strcat(TNC->InitScript, "E 1\r"); // Echo - Restart process needs echo + + sprintf(msg, "I %s\r", TNC->NodeCall); + strcat(TNC->InitScript, msg); + + OpenCOMMPort(TNC,PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + TNC->InitPtr = TNC->InitScript; + + WritetoConsole("\n"); + + return ExtProc; +} + +static void DEDCheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * ptr; + UCHAR character; + UCHAR * CURSOR; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; // Nothing doing + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + ptr = TNC->RXBuffer; + + CURSOR = &TNC->DEDBuffer[TNC->InputLen]; + + if ((TNC->HostMode == 0 || TNC->ReinitState == 10) && Length > 80) + { + // Probably Signon Message + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + ptr[Length] = 0; + Debugprintf("TRKM %s", ptr); + TNC->RXLen = 0; + return; + } + + if (TNC->HostMode == 0) + { + // If we are just restarting, and TNC is in host mode, we may get "Invalid Channel" Back + + if (memcmp(ptr, "\x18\x02INVALID", 9) == 0) + { + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + TNC->HostMode = TRUE; + TNC->HOSTSTATE = 0; + TNC->Timeout = 0; + TNC->RXLen = 0; + return; + } + + // Command is echoed as * command * + + if (strstr(ptr, "*") || TNC->ReinitState == 5) // 5 is waiting for reponse to JHOST1 + { + ProcessTermModeResponse(TNC); + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + + return; + } + } + + if (TNC->ReinitState == 10) + { + if (Length == 1 && *(ptr) == '.') // 01 echoed as . + { + // TNC is in Term Mode + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + TNC->ReinitState = 0; + TNC->HostMode = 0; + + return; + } + } + + while (Length--) + { + character = *(ptr++); + + if (TNC->HostMode) + { + // n 0 Success (nothing follows) + // n 1 Success (message follows, null terminated) + // n 2 Failure (message follows, null terminated) + // n 3 Link Status (null terminated) + // n 4 Monitor Header (null terminated) + // n 5 Monitor Header (null terminated) + // n 6 Monitor Information (preceeded by length-1) + // n 7 Connect Information (preceeded by length-1) + + + switch(TNC->HOSTSTATE) + { + case 0: // SETCHANNEL + + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + break; + + case 1: // SETMSGTYPE + + TNC->MSGTYPE = character; + + if (character == 0) + { + // Success, no more info + + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + break; + } + + if (character > 0 && character < 6) + { + // Null Terminated Response) + + TNC->HOSTSTATE = 5; + CURSOR = &TNC->DEDBuffer[0]; + break; + } + + if (character > 5 && character < 8) + { + TNC->HOSTSTATE = 2; // Get Length + break; + } + + // Invalid + + Debugprintf("TRKM - Invalid MsgType %d %x %x %x", character, *(ptr), *(ptr+1), *(ptr+2)); + break; + + case 2: // Get Length + + TNC->MSGCOUNT = character; + TNC->MSGCOUNT++; // Param is len - 1 + TNC->MSGLENGTH = TNC->MSGCOUNT; + CURSOR = &TNC->DEDBuffer[0]; + TNC->HOSTSTATE = 3; // Get Data + + break; + + case 5: // Collecting Null Terminated Response + + *(CURSOR++) = character; + + if (character) + continue; // MORE TO COME + + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + + break; + + default: + + // RECEIVING Counted Response + + *(CURSOR++) = character; + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + continue; // MORE TO COME + + TNC->InputLen = CURSOR - TNC->DEDBuffer; + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + } + } + } + + // End of Input - Save buffer position + + TNC->InputLen = CURSOR - TNC->DEDBuffer; + TNC->RXLen = 0; +} + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'T', TNC->TXBuffer, TNC->TXLen); + + TNC->Timeout = 20; // 2 secs + return TRUE; +} + +static VOID DEDPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + int Stream = 0; + int nn; + struct STREAMINFO * STREAM; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach. Set call my session callsign + + int calllen=0; + + TNC->Streams[Stream].Attached = TRUE; + + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER[6] |= 0x60; // Ensure P or T aren't used on ax.25 + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + if (Stream) //Leave Stream 0 call alone + { + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cI%s", Stream, 1, 1, TNC->Streams[Stream].MyCall); + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Can't use retries, as we have no way of detecting lost chars. Have to re-init on timeout + + if (TNC->HostMode == 0 || TNC->ReinitState == 10) // 10 is Recovery Mode + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + Debugprintf("DEDHOST - Link to TNC Lost Port %d", TNC->Port); + TNC->TNCOK = FALSE; + + TNC->HostMode = 0; + TNC->ReinitState = 0; + + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + TNC->InitPtr = TNC->InitScript; + TNC->HOSTSTATE = 0; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->Timeout) + return; // We've sent something + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->InitPtr) + { + char * start, * end; + int len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + TNC->InitPtr = NULL; + Debugprintf("TRKM - Init Complete Port %d", TNC->Port); + } + else + { + end = strchr(start, 13); + len = ++end - start -1; // exclude cr + + TNC->InitPtr = end; + + Poll[0] = 0; // Channel + Poll[1] = 1; // Command + Poll[2] = len - 1; + memcpy(&Poll[3], start, len); + + StuffAndSend(TNC, Poll, len + 3); + + return; + + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].CmdSet) + { + char * start, * end; + int len; + + start = TNC->Streams[Stream].CmdSet; + + if (*(start + 2) == 0) // End of Script + { + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + } + else + { + end = strchr(start + 3, 0); + len = ++end - start -1; // exclude cr + TNC->Streams[Stream].CmdSet = end; + + memcpy(&Poll[0], start, len); + Poll[2] = len - 4; + + StuffAndSend(TNC, Poll, len); + + return; + } + } + } + + for (nn = 0; nn <= MaxStreams; nn++) + { + Stream = TNC->LastStream++; + + if (TNC->LastStream > MaxStreams) TNC->LastStream = 0; + + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + datalen = buffptr->Len; + Buffer = buffptr->Data; // Data portion of frame + + Poll[0] = TNC->Streams[Stream].DEDStream; // Channel + + if (TNC->Streams[Stream].Connected) + { + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[1] = 0; // Data + TNC->Streams[Stream].BytesTXed += datalen; + + Poll[2] = datalen - 1; + memcpy(&Poll[3], Buffer, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + + // Command. Do some sanity checking and look for things to process locally + + Poll[1] = 1; // Command + datalen--; // Exclude CR + + if (datalen == 0) // Null Command + { + ReleaseBuffer(buffptr); + return; + } + + Buffer[datalen] = 0; // Null Terminate + _strupr(Buffer); + + if (_memicmp(Buffer, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if (Buffer[0] == 'C' && datalen > 2) // Connect + { + if (Stream == 0) + { + // No connects on Stream zero - for mgmt only + + buffptr->Len = sprintf((UCHAR *)buffptr->Data, "TRK} Can't Connect after ATTACH\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + return; + + } + + if (*(++Buffer) == ' ') Buffer++; // Space isn't needed + + memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); + + TNC->Streams[Stream].Connecting = TRUE; + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cI%s%c%c%c%cC%s", Stream, 1, 1, + TNC->Streams[Stream].MyCall, 0, Stream, 1, 1, Buffer); + + ReleaseBuffer(buffptr); + + TNC->Streams[Stream].InternalCmd = FALSE; + return; + } + + Poll[2] = datalen - 1; + memcpy(&Poll[3], Buffer, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + return; + } + } + + if (TNC->TNCOK && TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char CCMD[80] = "C"; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(500); + +// sprintf(TNC->Streams[Stream].CmdSet, "I%s\r%s\r", TNC->Streams[Stream].MyCall, buffptr->Data); + + // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address + // before sending the beacon + + ConvFromAX25(Buffer, &Call[1]); // Dest + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 14; // Skip Origin + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + Poll[0] = 0; // UI Channel + Poll[1] = 1; // Data + Poll[2] = strlen(CCMD) - 1; + strcpy(&Poll[3], CCMD); + StuffAndSend(TNC, Poll, Poll[2] + 4); + + sprintf(TNC->Streams[0].CmdSet, "%c%c%c%s", 0, 0, 1, Buffer); + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + // if frames outstanding, issue a poll (but not too often) + + TNC->IntCmdDelay++; + + if (TNC->IntCmdDelay > 10) + { + TNC->IntCmdDelay = 0; + + Poll[0] = TNC->Streams[0].DEDStream; + Poll[1] = 0x1; // Command + TNC->Streams[0].InternalCmd = TRUE; + + Poll[2] = 1; // Len-1 + Poll[3] = '@'; + Poll[4] = 'B'; // Buffers + StuffAndSend(TNC, Poll, 5); + return; + } + + // Need to poll all channels . Just Poll zero here, the ProcessMessage will poll next + + Poll[0] = 0; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + + return; + + + Stream = TNC->StreamtoPoll; + + STREAM = &TNC->Streams[Stream]; + + STREAM->IntCmdDelay++; + + if (STREAM->IntCmdDelay > 10) + { + STREAM->IntCmdDelay = 0; + + if (STREAM->FramesOutstanding) + { + Poll[0] = STREAM->DEDStream; + Poll[1] = 0x1; // Command + STREAM->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + + return; + } + } + + + Poll[0] = Stream; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + STREAM->InternalCmd = FALSE; + + return; + +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + TNC->TNCOK = FALSE; + + memcpy(&TNC->TXBuffer[0], "\x18\x1b\r", 2); + TNC->TXLen = 2; + + if (WriteCommBlock(TNC) == FALSE) + { + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + } + + return; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + // Put into Host Mode + + memcpy(Poll, "\x18\x1bJHOST1\r", 9); + + TNC->TXLen = 9; + WriteCommBlock(TNC); + + TNC->ReinitState = 5; + return; + } + + if (TNC->ReinitState == 5) + TNC->ReinitState = 0; + +} + +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; + ExitHost(TNC); + + return; + } + + if (TNC->ReinitState == 1) + { + // No Response to trying to enter term mode - do error recovery + + Debugprintf("TRKM - Starting Resync Port %d", TNC->Port); + + TNC->ReinitState = 10; + TNC->ReinitCount = 256; + TNC->HostMode = TRUE; // Must be in Host Mode if we need recovery + + Poll[0] = 1; + TNC->TXLen = 1; + WriteCommBlock(TNC); + TNC->Timeout = 10; // 2 secs + + return; + } + + if (TNC->ReinitState == 10) + { + // Continue error recovery + + TNC->ReinitCount--; + + if (TNC->ReinitCount) + { + Poll[0] = 1; + TNC->TXLen = 1; + WriteCommBlock(TNC); + TNC->Timeout = 3; // 0.3 secs + + return; + } + + // Try Again + + Debugprintf("TRKM Continuing recovery Port %d", TNC->Port); + + TNC->ReinitState = 0; + + // Close and re-open TNC + + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) 0; + TNC->ReopenTimer = 290; + TNC->HostMode = FALSE; + + return; + } + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +static VOID ExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[0] = 1; + TNC->TXBuffer[1] = 1; + TNC->TXBuffer[2] = 1; + memcpy(&TNC->TXBuffer[3], "%R", 2); + + StuffAndSend(TNC, Poll, 5); + + return; +} + +static VOID ProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', TNC->RXBuffer, TNC->RXLen); + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + } + + if (TNC->ReinitState == 1) + { + // trying to set term mode + + // If already in Term Mode, TNC echos command, with control chars replaced with '.' + + if (memcmp(TNC->RXBuffer, "....%R", 6) == 0) + { + // In term mode, Need to put into Host Mode + + TNC->ReinitState = 2; + DoTNCReinit(TNC); + return; + } + } + + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } + + if (TNC->ReinitState == 5) // Waiting for response to JHOST1 + { + if (TNC->RXBuffer[TNC->RXLen-1] == 10 || TNC->RXBuffer[TNC->RXLen-1] == 13) // NewLine + { + TNC->HostMode = TRUE; + TNC->Timeout = 0; + } + return; + } +} + +static VOID ProcessDEDFrame(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + char * Buffer; // Data portion of frame + UINT Stream = 0; + UCHAR * Msg = TNC->DEDBuffer; + int framelen = TNC->InputLen; + struct STREAMINFO * STREAM; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', Msg, framelen); + + if (TNC->ReinitState == 10) + { + // Recovering from Sync Failure + + // Any Response indicates we are in host mode, and back in sync + + TNC->HostMode = TRUE; + TNC->Timeout = 0; + TNC->ReinitState = 0; + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + + Debugprintf("TRKM - Resync Complete"); + return; + } + + // Any valid frame is an ACK + + TNC->Timeout = 0; + TNC->TNCOK = TRUE; + + if (TNC->InitPtr) // Response to Init Script + return; + + if (TNC->MSGCHANNEL > 26) + return; + + Stream = TNC->MSGCHANNEL; + + // See if Poll Reply or Data + + if (TNC->MSGTYPE == 0) + { + // Success - Nothing Follows + + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set or Init Script + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + { + UCHAR * Poll = TNC->TXBuffer; + + // Poll Next Channel (we need to scan all channels every DEDPOLL cycle + + Stream++; + + if (Stream > MaxStreams) + return; + + STREAM = &TNC->Streams[Stream]; + + STREAM->IntCmdDelay++; + + if (STREAM->IntCmdDelay > 10) + { + STREAM->IntCmdDelay = 0; + + if (STREAM->FramesOutstanding) + { + Poll[0] = STREAM->DEDStream; + Poll[1] = 0x1; // Command + STREAM->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + return; + } + } + + Poll[0] = Stream; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + STREAM->InternalCmd = FALSE; + + return; + } + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"TRK} Ok\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE > 0 &&TNC->MSGTYPE < 6) + { + // Success with message - null terminated + + char * ptr; + int len; + + Buffer = Msg; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return; + + *(ptr++) = 13; + *(ptr) = 0; + + len = ptr - Buffer; + + if (len > 256) + return; + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (TNC->MSGTYPE < 3) // 1 or 2 - Success or Fail + { + // See if a response to internal command + + if (TNC->Streams[Stream].InternalCmd) + { + // Process it + + char LastCmd = TNC->TXBuffer[3]; + + if (LastCmd == 'L') // Status + { + int s1, s2, s3, s4, s5, s6, num; + + num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6); + + TNC->Streams[Stream].FramesOutstanding = s3; + return; + } + + if (LastCmd == '@') // @ Commands + { + if (TNC->TXBuffer[4]== 'B') // Buffer Status + { + TNC->Buffers = atoi(Buffer); +// SetDlgItemText(TNC->hDlg, IDC_BUFFERS, Buffer); + return; + } + } + + return; + } + + // Not Internal Command, so send to user + + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + return; + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"TRK} %s", Buffer); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE") || strstr(Buffer, "BUSY")) + { + if ((STREAM->Connecting | STREAM->Connected) == 0) + return; + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + if (strstr(Buffer, "BUSY")) + buffptr->Len = sprintf(buffptr->Data, "*** Busy from %s\r", TNC->Streams[Stream].RemoteCall); + else + buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesOutstanding = 0; + + STREAM->DiscWhenAllSent = 15; // Dont want to leave session attached. Causes too much confusion + + 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; + return; + } + + if (strstr(Buffer, "CONNECTED")) + { + char * Call = strstr(Buffer, " to "); + char * ptr; + char MHCall[30]; + + Call += 4; + + if (Call[1] == ':') + Call +=2; + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strchr(Call, 13); + if (ptr) *ptr = 0; + + STREAM->Connected = TRUE; // Subsequent data to data channel + STREAM->Connecting = FALSE; + + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + memcpy(MHCall, Call, 9); + MHCall[9] = 0; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + +// APPLCALLS * APPL; +// char * ApplPtr = &APPLS; +// int App; +// char Appl[10]; +// char DestCall[10]; + + UpdateMH(TNC, MHCall, '+', 'I'); + + ProcessIncommingConnect(TNC, Call, Stream, TRUE); + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + int Len = CTEXTLEN, CTPaclen = 100; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = CTPaclen; + memcpy(buffptr->Data, &CTEXTMSG[Next], CTPaclen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + Next += CTPaclen; + Len -= CTPaclen; + } + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = Len; + memcpy(buffptr->Data, &CTEXTMSG[Next], Len); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } + + 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); + + } + } + return; + } + + if (TNC->MSGTYPE == 4 || TNC->MSGTYPE == 5) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; // RP Stream + + // Monitor + +/* + if (TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Attached == FALSE) + { + // See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL + + char DestCall[10] = "NOCALL "; + char * ptr1 = strstr(&Msg[7], "to "); + int i; + APPLCALLS * APPL; + char Appl[11]; + char Status[80]; + + if (ptr1) memcpy(DestCall, &ptr1[3], 10); + + ptr1 = strchr(DestCall, ' '); + if (ptr1) *(ptr1) = 0; // Null Terminate + + Debugprintf("RP SABM Received for %s" , DestCall); + + if (strcmp(TNC->NodeCall, DestCall) != 0) + { + // Not Calling NodeCall/Portcall + + if (strcmp(NodeCall, DestCall) == 0) + goto SetThisCall; + + // See if to one of our ApplCalls + + 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 = 0; + + if (strcmp(Appl, DestCall) == 0) + { + SetThisCall: + Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall); + + sprintf(Status, "%d SCANSTART 60", TNC->Port); // Pause scan for 60 secs + Rig_Command(-1, Status); + TNC->SwitchToPactor = 600; // Don't change modes for 60 secs + + strcpy(STREAM->MyCall, DestCall); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "I%s\r", DestCall); + break; + } + } + } + } + } +*/ + DoMonitorHddr(TNC, Msg, framelen, TNC->MSGTYPE); + return; + + } + + // 1, 2, 4, 5 - pass to Appl + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"Trk} %s", &Msg[4]); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 6) + { + // Monitor Data With length) + + DoMonitorData(TNC, Msg, framelen); + return; + } + + if (TNC->MSGTYPE == 7) + { + //char StatusMsg[60]; + //int Status, ISS, Offset; + + // Connected Data + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = framelen; // Length + TNC->Streams[Stream].BytesRXed += buffptr->Len; + memcpy(buffptr->Data, Msg, buffptr->Len); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // Queue it as we may have just sent data + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cD", Stream, 1, 1); +} + + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ +} + + diff --git a/SCSTrackeMulti64.c b/SCSTrackeMulti64.c new file mode 100644 index 0000000..a7dc3bf --- /dev/null +++ b/SCSTrackeMulti64.c @@ -0,0 +1,1714 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface DED Host Mode TNCs to BPQ32 switch +// +// Uses BPQ EXTERNAL interface + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#define MaxStreams 10 + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +static char ClassName[]="TRACKERSTATUS"; +static char WindowTitle[] = "SCS Tracker"; +static int RigControlRow = 140; + +#define NARROWMODE 30 +#define WIDEMODE 30 // PIII only + +extern UCHAR BPQDirectory[]; + +extern char * PortConfig[33]; + +static RECT Rect; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +VOID __cdecl Debugprintf(const char * format, ...); +char * strlop(char * buf, char delim); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); + +char NodeCall[11]; // Nodecall, Null Terminated +void WriteDebugLogLine(int Port, char Dirn, char * Msg, int MsgLen); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + char * p_port = 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; + + TNC->PacketChannels = 10; // Default + + goto ConfigLine; + + 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, "APPL", 4) == 0) + { + } + else + if (_memicmp(buf, "RIGCONTROL", 10) == 0) + { + } + else + + if (_memicmp(buf, "SWITCHMODES", 11) == 0) + { + } + else + if (_memicmp(buf, "USEAPPLCALLS", 12) == 0) + { +// TNC->UseAPPLCalls = TRUE; + } + else if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log + TNC->WRITELOG = atoi(&buf[8]); + else + if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) + { + } + else + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + } + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "PACKETCHANNELS", 14) == 0) + + // Packet Channels + + TNC->PacketChannels = atoi(&buf[14]); + else + strcat (TNC->InitScript, buf); + } + return (TRUE); + +} + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +static void DEDCheckRX(struct TNCINFO * TNC); +static VOID DEDPoll(int Port); +VOID StuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); +unsigned short int compute_crc(unsigned char *buf,int len); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +static VOID ProcessDEDFrame(struct TNCINFO * TNC); +static VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID ExitHost(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID DoMonitorHddr(struct TNCINFO * TNC, UCHAR * Msg, int Len, int Type); +VOID DoMonitorData(struct TNCINFO * TNC, UCHAR * Msg, int Len); +int Switchmode(struct TNCINFO * TNC, int Mode); +VOID SwitchToRPacket(struct TNCINFO * TNC); +VOID SwitchToNormPacket(struct TNCINFO * TNC); + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + unsigned int txlen = 0; + + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; + + if (TNC->hDevice == 0) + { + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (fn > 3 && fn < 6) + goto ok; + + // Try to reopen every 30 secs + + 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 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + DEDCheckRX(TNC); + DEDPoll(port); + DEDCheckRX(TNC); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer((PDATAMESSAGE)buff, datalen); // Neded for arm5 portability + + // buff[5]=(datalen & 0xff); + // buff[6]=(datalen >> 8); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = 21; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 21); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + TNC->Streams[Stream].FramesOutstanding++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + TNCOK = (TNC->HostMode == 1 && TNC->ReinitState != 10); + + STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + if (STREAM->FramesOutstanding > 4) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + } + else + { + if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); } + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + + case 4: // reinit + + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) 0; + TNC->ReopenTimer = 250; + TNC->HostMode = FALSE; + + return (0); + + case 5: // Close + + // Ensure in Pactor + + ExitHost(TNC); + + Sleep(25); + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: + + return 0; // No scan interface +} + return 0; +} + +void * TrackerMExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int Stream = 0; + char * TempScript; + char YCmd[10]; + + // + // Will be called once for each DED Host TNC Port + // The COM port number is in IOBASE + // + + sprintf(msg,"SCSTRK M %s", PortEntry->PORTCONTROL.SerialPortName); + + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_TRKM; + + // Set up DED addresses for streams + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; // DED Stream = BPQ Stream (We don't use Stream 0) + } + + if (TNC->PacketChannels > MaxStreams) + TNC->PacketChannels = MaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; //TNC->PacketChannels + 1; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = NONE; // Scan Control 3 stage/conlock + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->PORTCONTROL.UICAPABLE = 1; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // get NODECALL for RP tests + + memcpy(NodeCall, MYNODECALL, 10); + + ptr=strchr(NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TempScript = malloc(1000); + + strcpy(TempScript, "M UISC\r"); + strcat(TempScript, "F 200\r"); // Sets SABM retry time to about 5 secs + strcat(TempScript, "%F 1500\r"); // Tones may be changed but I want this as standard + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "Z 0\r"); // No Flow Control + sprintf(YCmd, "Y %d\r", TNC->PacketChannels); + strcat(TNC->InitScript, YCmd); + strcat(TNC->InitScript, "E 1\r"); // Echo - Restart process needs echo + + sprintf(msg, "I %s\r", TNC->NodeCall); + strcat(TNC->InitScript, msg); + + OpenCOMMPort(TNC,PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + TNC->InitPtr = TNC->InitScript; + + WritetoConsole("\n"); + + return ExtProc; +} + +static void DEDCheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * ptr; + UCHAR character; + UCHAR * CURSOR; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; // Nothing doing + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + ptr = TNC->RXBuffer; + + CURSOR = &TNC->DEDBuffer[TNC->InputLen]; + + if ((TNC->HostMode == 0 || TNC->ReinitState == 10) && Length > 80) + { + // Probably Signon Message + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + ptr[Length] = 0; + Debugprintf("TRK %s", ptr); + TNC->RXLen = 0; + return; + } + + if (TNC->HostMode == 0) + { + // If we are just restarting, and TNC is in host mode, we may get "Invalid Channel" Back + + if (memcmp(ptr, "\x18\x02INVALID", 9) == 0) + { + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + TNC->HostMode = TRUE; + TNC->HOSTSTATE = 0; + TNC->Timeout = 0; + TNC->RXLen = 0; + return; + } + + // Command is echoed as * command * + + if (strstr(ptr, "*") || TNC->ReinitState == 5) // 5 is waiting for reponse to JHOST1 + { + ProcessTermModeResponse(TNC); + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + + return; + } + } + + if (TNC->ReinitState == 10) + { + if (Length == 1 && *(ptr) == '.') // 01 echoed as . + { + // TNC is in Term Mode + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + TNC->ReinitState = 0; + TNC->HostMode = 0; + + return; + } + } + + while (Length--) + { + character = *(ptr++); + + if (TNC->HostMode) + { + // n 0 Success (nothing follows) + // n 1 Success (message follows, null terminated) + // n 2 Failure (message follows, null terminated) + // n 3 Link Status (null terminated) + // n 4 Monitor Header (null terminated) + // n 5 Monitor Header (null terminated) + // n 6 Monitor Information (preceeded by length-1) + // n 7 Connect Information (preceeded by length-1) + + + switch(TNC->HOSTSTATE) + { + case 0: // SETCHANNEL + + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + break; + + case 1: // SETMSGTYPE + + TNC->MSGTYPE = character; + + if (character == 0) + { + // Success, no more info + + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + break; + } + + if (character > 0 && character < 6) + { + // Null Terminated Response) + + TNC->HOSTSTATE = 5; + CURSOR = &TNC->DEDBuffer[0]; + break; + } + + if (character > 5 && character < 8) + { + TNC->HOSTSTATE = 2; // Get Length + break; + } + + // Invalid + + Debugprintf("TRK - Invalid MsgType %d %x %x %x", character, *(ptr), *(ptr+1), *(ptr+2)); + break; + + case 2: // Get Length + + TNC->MSGCOUNT = character; + TNC->MSGCOUNT++; // Param is len - 1 + TNC->MSGLENGTH = TNC->MSGCOUNT; + CURSOR = &TNC->DEDBuffer[0]; + TNC->HOSTSTATE = 3; // Get Data + + break; + + case 5: // Collecting Null Terminated Response + + *(CURSOR++) = character; + + if (character) + continue; // MORE TO COME + + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + + break; + + default: + + // RECEIVING Counted Response + + *(CURSOR++) = character; + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + continue; // MORE TO COME + + TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); + ProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + } + } + } + + // End of Input - Save buffer position + + TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); + TNC->RXLen = 0; +} + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'T', TNC->TXBuffer, TNC->TXLen); + + TNC->Timeout = 20; // 2 secs + return TRUE; +} + +static VOID DEDPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + int Stream = 0; + int nn; + struct STREAMINFO * STREAM; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach. Set call my session callsign + + int calllen=0; + + TNC->Streams[Stream].Attached = TRUE; + + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER[6] |= 0x60; // Ensure P or T aren't used on ax.25 + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + if (Stream) //Leave Stream 0 call alone + { + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cI%s", Stream, 1, 1, TNC->Streams[Stream].MyCall); + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Can't use retries, as we have no way of detecting lost chars. Have to re-init on timeout + + if (TNC->HostMode == 0 || TNC->ReinitState == 10) // 10 is Recovery Mode + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + Debugprintf("DEDHOST - Link to TNC Lost Port %d", TNC->Port); + TNC->TNCOK = FALSE; + + TNC->HostMode = 0; + TNC->ReinitState = 0; + + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + TNC->InitPtr = TNC->InitScript; + TNC->HOSTSTATE = 0; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->Timeout) + return; // We've sent something + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->InitPtr) + { + char * start, * end; + int len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + TNC->InitPtr = NULL; + Debugprintf("TRK - Init Complete Port %d", TNC->Port); + } + else + { + end = strchr(start, 13); + len = (int)(++end - start - 1); // exclude cr + + TNC->InitPtr = end; + + Poll[0] = 0; // Channel + Poll[1] = 1; // Command + Poll[2] = len - 1; + memcpy(&Poll[3], start, len); + + StuffAndSend(TNC, Poll, len + 3); + + return; + + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].CmdSet) + { + char * start, * end; + int len; + + start = TNC->Streams[Stream].CmdSet; + + if (*(start + 2) == 0) // End of Script + { + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + } + else + { + end = strchr(start + 3, 0); + len = (int)(++end - start - 1); // exclude cr + TNC->Streams[Stream].CmdSet = end; + + memcpy(&Poll[0], start, len); + Poll[2] = len - 4; + + StuffAndSend(TNC, Poll, len); + + return; + } + } + } + + for (nn = 0; nn <= MaxStreams; nn++) + { + Stream = TNC->LastStream++; + + if (TNC->LastStream > MaxStreams) TNC->LastStream = 0; + + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + UINT * buffptr; + char * Buffer; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + datalen=buffptr[1]; + Buffer = (char *)&buffptr[2]; // Data portion of frame + + Poll[0] = TNC->Streams[Stream].DEDStream; // Channel + + if (TNC->Streams[Stream].Connected) + { + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[1] = 0; // Data + TNC->Streams[Stream].BytesTXed += datalen; + + Poll[2] = datalen - 1; + memcpy(&Poll[3], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + + // Command. Do some sanity checking and look for things to process locally + + Poll[1] = 1; // Command + datalen--; // Exclude CR + + if (datalen == 0) // Null Command + { + ReleaseBuffer(buffptr); + return; + } + + Buffer[datalen] = 0; // Null Terminate + _strupr(Buffer); + + if (_memicmp(Buffer, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if (Buffer[0] == 'C' && datalen > 2) // Connect + { + if (Stream == 0) + { + // No connects on Stream zero - for mgmt only + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "TRK} Can't Connect after ATTACH\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + return; + + } + + if (*(++Buffer) == ' ') Buffer++; // Space isn't needed + + memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); + + TNC->Streams[Stream].Connecting = TRUE; + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cI%s%c%c%c%c%s", Stream, 1, 1, + TNC->Streams[Stream].MyCall, 0, Stream, 1, 1, (char *)buffptr+8); + + ReleaseBuffer(buffptr); + + TNC->Streams[Stream].InternalCmd = FALSE; + return; + } + + Poll[2] = datalen - 1; + memcpy(&Poll[3], buffptr+2, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + return; + } + } + + if (TNC->TNCOK && TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char CCMD[80] = "C"; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + datalen = buffptr->LENGTH - 7; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(500); + +// sprintf(TNC->Streams[Stream].CmdSet, "I%s\r%s\r", TNC->Streams[Stream].MyCall, buffptr+2); + + // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address + // before sending the beacon + + ConvFromAX25(Buffer, &Call[1]); // Dest + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 14; // Skip Origin + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + Poll[0] = 0; // UI Channel + Poll[1] = 1; // Data + Poll[2] = (int)strlen(CCMD) - 1; + strcpy(&Poll[3], CCMD); + StuffAndSend(TNC, Poll, Poll[2] + 4); + + sprintf(TNC->Streams[0].CmdSet, "%c%c%c%s", 0, 0, 1, Buffer); + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + // if frames outstanding, issue a poll (but not too often) + + TNC->IntCmdDelay++; + + if (TNC->IntCmdDelay > 10) + { + TNC->IntCmdDelay = 0; + + Poll[0] = TNC->Streams[0].DEDStream; + Poll[1] = 0x1; // Command + TNC->Streams[0].InternalCmd = TRUE; + + Poll[2] = 1; // Len-1 + Poll[3] = '@'; + Poll[4] = 'B'; // Buffers + StuffAndSend(TNC, Poll, 5); + return; + } + + // Need to poll all channels . Just Poll zero here, the ProcessMessage will poll next + + Poll[0] = 0; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + + return; + + + Stream = TNC->StreamtoPoll; + + STREAM = &TNC->Streams[Stream]; + + STREAM->IntCmdDelay++; + + if (STREAM->IntCmdDelay > 10) + { + STREAM->IntCmdDelay = 0; + + if (STREAM->FramesOutstanding) + { + Poll[0] = STREAM->DEDStream; + Poll[1] = 0x1; // Command + STREAM->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + + return; + } + } + + + Poll[0] = Stream; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + STREAM->InternalCmd = FALSE; + + return; + +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + TNC->TNCOK = FALSE; + + memcpy(&TNC->TXBuffer[0], "\x18\x1b\r", 2); + TNC->TXLen = 2; + + if (WriteCommBlock(TNC) == FALSE) + { + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + } + + return; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + // Put into Host Mode + + memcpy(Poll, "\x18\x1bJHOST1\r", 9); + + TNC->TXLen = 9; + WriteCommBlock(TNC); + + TNC->ReinitState = 5; + return; + } + + if (TNC->ReinitState == 5) + TNC->ReinitState = 0; + +} + +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; + ExitHost(TNC); + + return; + } + + if (TNC->ReinitState == 1) + { + // No Response to trying to enter term mode - do error recovery + + Debugprintf("TRK - Starting Resync Port %d", TNC->Port); + + TNC->ReinitState = 10; + TNC->ReinitCount = 256; + TNC->HostMode = TRUE; // Must be in Host Mode if we need recovery + + Poll[0] = 1; + TNC->TXLen = 1; + WriteCommBlock(TNC); + TNC->Timeout = 10; // 2 secs + + return; + } + + if (TNC->ReinitState == 10) + { + // Continue error recovery + + TNC->ReinitCount--; + + if (TNC->ReinitCount) + { + Poll[0] = 1; + TNC->TXLen = 1; + WriteCommBlock(TNC); + TNC->Timeout = 3; // 0.3 secs + + return; + } + + // Try Again + + Debugprintf("TRK Continuing recovery Port %d", TNC->Port); + + TNC->ReinitState = 0; + + // Close and re-open TNC + + ExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE) 0; + TNC->ReopenTimer = 290; + TNC->HostMode = FALSE; + + return; + } + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +static VOID ExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[0] = 1; + TNC->TXBuffer[1] = 1; + TNC->TXBuffer[2] = 1; + memcpy(&TNC->TXBuffer[3], "%R", 2); + + StuffAndSend(TNC, Poll, 5); + + return; +} + +static VOID ProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', TNC->RXBuffer, TNC->RXLen); + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + } + + if (TNC->ReinitState == 1) + { + // trying to set term mode + + // If already in Term Mode, TNC echos command, with control chars replaced with '.' + + if (memcmp(TNC->RXBuffer, "....%R", 6) == 0) + { + // In term mode, Need to put into Host Mode + + TNC->ReinitState = 2; + DoTNCReinit(TNC); + return; + } + } + + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } + + if (TNC->ReinitState == 5) // Waiting for response to JHOST1 + { + if (TNC->RXBuffer[TNC->RXLen-1] == 10 || TNC->RXBuffer[TNC->RXLen-1] == 13) // NewLine + { + TNC->HostMode = TRUE; + TNC->Timeout = 0; + } + return; + } +} + +static VOID ProcessDEDFrame(struct TNCINFO * TNC) +{ + UINT * buffptr; + char * Buffer; // Data portion of frame + UINT Stream = 0; + UCHAR * Msg = TNC->DEDBuffer; + int framelen = TNC->InputLen; + struct STREAMINFO * STREAM; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', Msg, framelen); + + if (TNC->ReinitState == 10) + { + // Recovering from Sync Failure + + // Any Response indicates we are in host mode, and back in sync + + TNC->HostMode = TRUE; + TNC->Timeout = 0; + TNC->ReinitState = 0; + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + return; + } + + // Any valid frame is an ACK + + TNC->Timeout = 0; + TNC->TNCOK = TRUE; + + if (TNC->InitPtr) // Response to Init Script + return; + + if (TNC->MSGCHANNEL > 26) + return; + + Stream = TNC->MSGCHANNEL; + + // See if Poll Reply or Data + + if (TNC->MSGTYPE == 0) + { + // Success - Nothing Follows + + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set or Init Script + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + { + UCHAR * Poll = TNC->TXBuffer; + + // Poll Next Channel (we need to scan all channels every DEDPOLL cycle + + Stream++; + + if (Stream > MaxStreams) + return; + + STREAM = &TNC->Streams[Stream]; + + STREAM->IntCmdDelay++; + + if (STREAM->IntCmdDelay > 10) + { + STREAM->IntCmdDelay = 0; + + if (STREAM->FramesOutstanding) + { + Poll[0] = STREAM->DEDStream; + Poll[1] = 0x1; // Command + STREAM->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + return; + } + } + + Poll[0] = Stream; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + STREAM->InternalCmd = FALSE; + + return; + } + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"TRK} Ok\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE > 0 &&TNC->MSGTYPE < 6) + { + // Success with message - null terminated + + char * ptr; + int len; + + Buffer = Msg; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return; + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (TNC->MSGTYPE < 3) // 1 or 2 - Success or Fail + { + // See if a response to internal command + + if (TNC->Streams[Stream].InternalCmd) + { + // Process it + + char LastCmd = TNC->TXBuffer[3]; + + if (LastCmd == 'L') // Status + { + int s1, s2, s3, s4, s5, s6, num; + + num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6); + + TNC->Streams[Stream].FramesOutstanding = s3; + return; + } + + if (LastCmd == '@') // @ Commands + { + if (TNC->TXBuffer[4]== 'B') // Buffer Status + { + TNC->Buffers = atoi(Buffer); +// SetDlgItemText(TNC->hDlg, IDC_BUFFERS, Buffer); + return; + } + } + + return; + } + + // Not Internal Command, so send to user + + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + return; + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"TRK} %s", Buffer); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE") || strstr(Buffer, "BUSY")) + { + if ((STREAM->Connecting | STREAM->Connected) == 0) + return; + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + if (strstr(Buffer, "BUSY")) + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Busy from %s\r", TNC->Streams[Stream].RemoteCall); + else + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesOutstanding = 0; + + STREAM->DiscWhenAllSent = 15; // Dont want to leave session attached. Causes too much confusion + + 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; + return; + } + + if (strstr(Buffer, "CONNECTED")) + { + char * Call = strstr(Buffer, " to "); + char * ptr; + char MHCall[30]; + + Call += 4; + + if (Call[1] == ':') + Call +=2; + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strchr(Call, 13); + if (ptr) *ptr = 0; + + STREAM->Connected = TRUE; // Subsequent data to data channel + STREAM->Connecting = FALSE; + + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + memcpy(MHCall, Call, 9); + MHCall[9] = 0; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + +// APPLCALLS * APPL; +// char * ApplPtr = &APPLS; +// int App; +// char Appl[10]; +// char DestCall[10]; + + UpdateMH(TNC, MHCall, '+', 'I'); + + ProcessIncommingConnect(TNC, Call, Stream, TRUE); + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + int Len = CTEXTLEN, CTPaclen = 100; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = CTPaclen; + memcpy(&buffptr[2], &CTEXTMSG[Next], CTPaclen); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + Next += CTPaclen; + Len -= CTPaclen; + } + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = Len; + memcpy(&buffptr[2], &CTEXTMSG[Next], Len); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + } + + return; + } + else + { + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + } + } + return; + } + + if (TNC->MSGTYPE == 4 || TNC->MSGTYPE == 5) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; // RP Stream + + // Monitor + +/* + if (TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Attached == FALSE) + { + // See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL + + char DestCall[10] = "NOCALL "; + char * ptr1 = strstr(&Msg[7], "to "); + int i; + APPLCALLS * APPL; + char Appl[11]; + char Status[80]; + + if (ptr1) memcpy(DestCall, &ptr1[3], 10); + + ptr1 = strchr(DestCall, ' '); + if (ptr1) *(ptr1) = 0; // Null Terminate + + Debugprintf("RP SABM Received for %s" , DestCall); + + if (strcmp(TNC->NodeCall, DestCall) != 0) + { + // Not Calling NodeCall/Portcall + + if (strcmp(NodeCall, DestCall) == 0) + goto SetThisCall; + + // See if to one of our ApplCalls + + 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 = 0; + + if (strcmp(Appl, DestCall) == 0) + { + SetThisCall: + Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall); + + sprintf(Status, "%d SCANSTART 60", TNC->Port); // Pause scan for 60 secs + Rig_Command(-1, Status); + TNC->SwitchToPactor = 600; // Don't change modes for 60 secs + + strcpy(STREAM->MyCall, DestCall); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "I%s\r", DestCall); + break; + } + } + } + } + } +*/ + DoMonitorHddr(TNC, Msg, framelen, TNC->MSGTYPE); + return; + + } + + // 1, 2, 4, 5 - pass to Appl + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"Trk} %s", &Msg[4]); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 6) + { + // Monitor Data With length) + + DoMonitorData(TNC, Msg, framelen); + return; + } + + if (TNC->MSGTYPE == 7) + { + //char StatusMsg[60]; + //int Status, ISS, Offset; + + // Connected Data + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr[1] = framelen; // Length + TNC->Streams[Stream].BytesRXed += buffptr[1]; + memcpy(&buffptr[2], Msg, buffptr[1]); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // Queue it as we may have just sent data + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "%c%c%cD", Stream, 1, 1); +} + + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ +} + + diff --git a/SCSTracker.c b/SCSTracker.c new file mode 100644 index 0000000..907a7fb --- /dev/null +++ b/SCSTracker.c @@ -0,0 +1,2631 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to inteface DED Host Mode TNCs to BPQ32 switch +// +// Uses BPQ EXTERNAL interface + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#define MaxStreams 1 + +#include "CHeaders.h" +#include "tncinfo.h" + +//#include "bpq32.h" + +static char ClassName[]="TRACKERSTATUS"; +static char WindowTitle[] = "SCS Tracker"; +static int RigControlRow = 140; + +#define NARROWMODE 30 +#define WIDEMODE 30 // Robust + +extern UCHAR LogDirectory[]; + +extern APPLCALLS APPLCALLTABLE[]; +extern char * PortConfig[33]; +extern char LOC[]; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +VOID __cdecl Debugprintf(const char * format, ...); +char * strlop(char * buf, char delim); + +char NodeCall[11]; // Nodecall, Null Terminated + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +BOOL TrkWriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +static void DEDCheckRX(struct TNCINFO * TNC); +VOID DEDPoll(int Port); +VOID StuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); +unsigned short int compute_crc(unsigned char *buf,int len); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +VOID TrkProcessDEDFrame(struct TNCINFO * TNC); +VOID TrkProcessTermModeResponse(struct TNCINFO * TNC); +VOID TrkExitHost(struct TNCINFO * TNC); +VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID DoMonitorHddr(struct TNCINFO * TNC, UCHAR * Msg, int Len, int Type); +VOID DoMonitorData(struct TNCINFO * TNC, UCHAR * Msg, int Len); +int Switchmode(struct TNCINFO * TNC, int Mode); +VOID SwitchToRPacket(struct TNCINFO * TNC, char * Baud); +VOID SwitchToNormPacket(struct TNCINFO * TNC, char * Baud); +VOID SendRPBeacon(struct TNCINFO * TNC); +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); +extern VOID TrkTidyClose(struct TNCINFO * TNC, int Stream); +extern VOID TrkForcedClose(struct TNCINFO * TNC, int Stream); +extern VOID TrkCloseComplete(struct TNCINFO * TNC, int Stream); +VOID RPRWriteCOMBlock(struct TNCINFO * TNC, UCHAR * Msg, int Len); +void WriteDebugLogLine(int Port, char Dirn, char * Msg, int MsgLen); +VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction); + + +BOOL TrkWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->Hardware == H_WINRPR) + RPRWriteCOMBlock(TNC, TNC->TXBuffer, TNC->TXLen); + else + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'T', TNC->TXBuffer, TNC->TXLen); + + TNC->Timeout = 20; // 2 secs + return TRUE; +} + + +VOID TRKSuspendPort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "\1\1\1IDSPTNC"); +} + +VOID TRKReleasePort(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "\1\1\1I%s", TNC->NodeCall); +} + +static FILE * LogHandle[32] = {0}; +time_t LogTime[32] = {0}; + +static char BaseDir[MAX_PATH]="c:\\"; + +VOID CloseDebugLogFile(int Port) +{ + fclose(LogHandle[Port]); + LogHandle[Port] = NULL; +} + +BOOL OpenDebugLogFile(int Port) +{ + UCHAR FN[MAX_PATH]; + + time_t T; + struct tm * tm; + + if (LogHandle[Port]) + return TRUE; // already open + + T = LogTime[Port] = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s/logs/Port%02dDebugLog_%02d%02d.txt", LogDirectory, Port, tm->tm_mon + 1, tm->tm_mday); + + LogHandle[Port] = fopen(FN, "ab"); + + return (LogHandle[Port] != NULL); +} + +void WriteDebugLogLine(int Port, char Dirn, char * Msg, int MsgLen) +{ + if (LogHandle[Port] == NULL) + OpenDebugLogFile(Port); + + if (LogHandle[Port]) + { + time_t T; + struct tm * tm; + char hddr[32]; + char hex[4]; + int len; + UCHAR c; + char textbit[33] = " "; + int i; + T = time(NULL); + tm = gmtime(&T); + + len = sprintf(hddr,"%02d:%02d:%02d %c Len %3d ", tm->tm_hour, tm->tm_min, tm->tm_sec, Dirn, MsgLen); + + fwrite(hddr, 1, len, LogHandle[Port]); + + // Write up to 32 bytes of Text - replace ctrl with . + + for (i = 0; i < 33 && i < MsgLen; i++) + { + c = Msg[i]; + if (c < 32) c = '.'; + textbit[i] = c; + } + fwrite(textbit, 1, 32, LogHandle[Port]); + + // display fiorsy 16 in hex + + if (MsgLen > 16) + MsgLen = 16; + + while (MsgLen--) + { + c = *(Msg++); + sprintf(hex, " %02X", c); + fwrite(hex, 1, 3, LogHandle[Port]); + } + fwrite("\r\n", 1, 2, LogHandle[Port]); + + if (T - LogTime[Port] > 30) + CloseDebugLogFile(Port); + + } +} + + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_port = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + BPQport = Port; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + strcpy(TNC->NormSpeed, "300"); // HF Packet + strcpy(TNC->RobustSpeed, "R600"); + + goto ConfigLine; + + 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, "APPL", 4) == 0) + { + p_cmd = strtok(&buf[4], " \t\n\r"); + + if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(_strupr(p_cmd)); + } + else + +// if (_mem`icmp(buf, "PACKETCHANNELS", 14) == 0) + + +// // Packet Channels + +// TNC->PacketChannels = atoi(&buf[14]); +// else + + if (_memicmp(buf, "SWITCHMODES", 11) == 0) + { + // Switch between Normal and Robust Packet + + double Robust = atof(&buf[12]); + #pragma warning(push) + #pragma warning(disable : 4244) + TNC->RobustTime = Robust * 10; + #pragma warning(pop) + } + if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log + TNC->WRITELOG = atoi(&buf[9]); + else if (_memicmp(buf, "BEACONAFTERSESSION", 18) == 0) // Send Beacon after each session + TNC->RPBEACON = TRUE; + else + if (_memicmp(buf, "USEAPPLCALLS", 12) == 0) + TNC->UseAPPLCalls = TRUE; + else + if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) + TNC->RobustDefault = TRUE; + else + if (_memicmp(buf, "FORCE ROBUST", 12) == 0) + TNC->ForceRobust = TNC->RobustDefault = TRUE; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else + { + strcat (TNC->InitScript, buf); + + // If %B param,and not R300 or R600 extract speed + + if (_memicmp(buf, "%B", 2) == 0) + { + ptr = strchr(buf, '\r'); + if (ptr) *ptr = 0; + if (strchr(buf, 'R') == 0) + strcpy(TNC->NormSpeed, &buf[3]); + else + strcpy(TNC->RobustSpeed, &buf[3]); + + } + } + } + return (TRUE); + +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + int txlen = 0; + struct TNCINFO * TNC = TNCInfo[port]; + int Param; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + struct ScanEntry * Scan; + int NewMode; + + 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); + } + + if (fn > 3 && fn < 6) + goto ok; + + // Try to reopen every 30 secs + + 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. + + // See if waiting for connect after changing MYCALL + + if (TNC->SlowTimer) + { + TNC->SlowTimer--; + if (TNC->SlowTimer == 0) + { + // Not connected in 45 secs, set back to Port Call + + Debugprintf("RP No response after changing call - setting MYCALL back to %s", TNC->NodeCall); + TRKReleasePort(TNC); + } + } + + return 0; + + case 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + return -1; + } + } + + DEDCheckRX(TNC); + DEDPoll(port); + DEDCheckRX(TNC); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) + { + size_t datalen; + + buffptr = Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = 22; + memcpy(buffptr->Data, "No Connection to TNC\r", 22); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + TNC->Streams[Stream].FramesOutstanding++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + TNCOK = (TNC->HostMode == 1 && TNC->ReinitState != 10); + + STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + if (STREAM->FramesOutstanding > 4) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + } + else + { + if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); } + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + + case 4: // reinit + + TrkExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE)0; + TNC->ReopenTimer = 250; + TNC->HostMode = FALSE; + + return (0); + + case 5: // Close + + // Ensure in Pactor + + TrkExitHost(TNC); + + Sleep(25); + + CloseCOMPort(TNC->hDevice); + return (0); + + case 6: // Scan Interface + + Param = (int)(size_t)buff; + + switch (Param) + { + case 1: // Request Permission + + if (TNC->TNCOK) + { + TNC->WantToChangeFreq = TRUE; + TNC->OKToChangeFreq = TRUE; + return 0; + } + return 0; // Don't lock scan if TNC isn't reponding + + + case 2: // Check Permission + return TNC->OKToChangeFreq; + + case 3: // Release Permission + + TNC->WantToChangeFreq = FALSE; + TNC->DontWantToChangeFreq = TRUE; + return 0; + + + default: // Change Mode. Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + // If no change, just return + + NewMode = Scan->RPacketMode | (Scan->HFPacketMode << 8); + + if (TNC->CurrentMode == NewMode) + return 0; + + TNC->CurrentMode = NewMode; + + if (Scan->RPacketMode == '1') + { + SwitchToRPacket(TNC, "R300"); + return 0; + } + if (Scan->RPacketMode == '2') + { + SwitchToRPacket(TNC, "R600"); + return 0; + } + + if (Scan->HFPacketMode == '1') + { + SwitchToNormPacket(TNC, "300"); + return 0; + } + + if (Scan->HFPacketMode == '2') + { + SwitchToNormPacket(TNC, "1200"); + return 0; + } + if (Scan->HFPacketMode == '3') + { + SwitchToNormPacket(TNC, "9600"); + return 0; + } + } + } + return 0; +} + +int TrkWebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Interval = 15; + int Len; + + if (LOCAL) + { + if (TNC->WEB_CHANGED) + Interval = 1; + else + Interval = 4; + } + else + { + if (TNC->WEB_CHANGED) + Interval = 4; + else + Interval = 15; + } + + if (TNC->WEB_CHANGED) + { + TNC->WEB_CHANGED -= Interval; + if (TNC->WEB_CHANGED < 0) + TNC->WEB_CHANGED = 0; + } + + Len = sprintf(Buff, "" + "SCSTracker Status

SCSTracker Status

", Interval); + + 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_BUFFERS); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Buffers%s
Traffic%s
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +void * TrackerExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int Stream = 0; + char * TempScript; + + // + // Will be called once for each DED Host TNC Port + // The COM port number is in IOBASE + // + + sprintf(msg,"SCSTRK %s", PortEntry->PORTCONTROL.SerialPortName); + + WritetoConsoleLocal(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsoleLocal(msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_TRK; + + // Set up DED addresses for streams + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream + 1; // DED Stream = BPQ Stream + 1 + } + + if (TNC->PacketChannels > MaxStreams) + TNC->PacketChannels = MaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = 1; //TNC->PacketChannels + 1; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = NONE; // Scan Control 3 stage/conlock + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.UICAPABLE = 1; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + TNC->SuspendPortProc = TRKSuspendPort; + TNC->ReleasePortProc = TRKReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // get NODECALL for RP tests + + memcpy(NodeCall, MYNODECALL, 10); + + ptr=strchr(NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TempScript = malloc(1000); + + strcpy(TempScript, "M UISC\r"); + strcat(TempScript, "F 200\r"); // Sets SABM retry time to about 5 secs + strcat(TempScript, "%F 1500\r"); // Tones may be changed but I want this as standard + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "Z 0\r"); // No Flow Control + strcat(TNC->InitScript, "Y 1\r"); // One Channel + strcat(TNC->InitScript, "E 1\r"); // Echo - Restart process needs echo + + sprintf(msg, "I %s\r", TNC->NodeCall); + strcat(TNC->InitScript, msg); + + strcpy(TNC->Streams[0].MyCall, TNC->NodeCall); // For 1st Connected Test + + PortEntry->PORTCONTROL.TNC = TNC; + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded as part of main config section + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + + TNC->WebWindowProc = TrkWebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_MODE = zalloc(20); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, TrkForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,10,120,24, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,10,386,26, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,36,106,24, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,36,520,24, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,62,80,24, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,62,200,24, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Buffers", WS_CHILD | WS_VISIBLE, 10,88,80,24, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 116,88,144,24, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,114,80,24, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0", WS_CHILD | WS_VISIBLE,116,114,374,24 , 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; + + + MoveWindows(TNC); + +#endif + + if (TNC->RobustDefault) + { + char Cmd[40]; + TNC->Robust = TRUE; + sprintf(Cmd, "%%B %s\r", TNC->RobustSpeed); + strcat(TNC->InitScript, Cmd); + SetWindowText(TNC->xIDC_MODE, "Robust Packet"); + strcpy(TNC->WEB_MODE, "Robust Packet"); + TNC->WEB_CHANGED = TRUE; + } + else + { + char Cmd[40]; + sprintf(Cmd, "%%B %s\r", TNC->NormSpeed); + strcat(TNC->InitScript, Cmd); + SetWindowText(TNC->xIDC_MODE, "HF Packet"); + strcpy(TNC->WEB_MODE, "HF Packet"); + TNC->WEB_CHANGED = TRUE; + + } + + strcpy(TNC->WEB_TNCSTATE, "Idle"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + OpenCOMMPort(TNC,PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + TNC->InitPtr = TNC->InitScript; + + if (TNC->RIG == &TNC->DummyRig || TNC->RIG == NULL) + TNC->SwitchToPactor = TNC->RobustTime; // Don't alternate Modes if using Rig Control + + WritetoConsoleLocal("\n"); + + return ExtProc; +} + +static void DEDCheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * ptr; + UCHAR character; + UCHAR * CURSOR; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; // Nothing doing + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + ptr = TNC->RXBuffer; + + CURSOR = &TNC->DEDBuffer[TNC->InputLen]; + + if ((TNC->HostMode == 0 || TNC->ReinitState == 10) && Length > 80) + { + // Probably Signon Message + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + ptr[Length] = 0; + Debugprintf("TRK %s", ptr); + TNC->RXLen = 0; + return; + } + + if (TNC->HostMode == 0) + { + // If we are just restarting, and TNC is in host mode, we may get "Invalid Channel" Back + + if (memcmp(ptr, "\x18\x02INVALID", 9) == 0) + { + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + + TNC->HostMode = TRUE; + TNC->HOSTSTATE = 0; + TNC->Timeout = 0; + TNC->RXLen = 0; + return; + } + + // Command is echoed as * command * + + if (strstr(ptr, "*") || TNC->ReinitState == 5) // 5 is waiting for reponse to JHOST1 + { + TrkProcessTermModeResponse(TNC); + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + + return; + } + } + + if (TNC->ReinitState == 10) + { + if (Length == 1 && *(ptr) == '.') // 01 echoed as . + { + // TNC is in Term Mode + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', ptr, Length); + TNC->ReinitState = 0; + TNC->HostMode = 0; + + return; + } + } + + + while (Length--) + { + character = *(ptr++); + + if (TNC->HostMode) + { + // n 0 Success (nothing follows) + // n 1 Success (message follows, null terminated) + // n 2 Failure (message follows, null terminated) + // n 3 Link Status (null terminated) + // n 4 Monitor Header (null terminated) + // n 5 Monitor Header (null terminated) + // n 6 Monitor Information (preceeded by length-1) + // n 7 Connect Information (preceeded by length-1) + + + switch(TNC->HOSTSTATE) + { + case 0: // SETCHANNEL + + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + break; + + case 1: // SETMSGTYPE + + TNC->MSGTYPE = character; + + if (character == 0) + { + // Success, no more info + + TrkProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + break; + } + + if (character > 0 && character < 6) + { + // Null Terminated Response) + + TNC->HOSTSTATE = 5; + CURSOR = &TNC->DEDBuffer[0]; + break; + } + + if (character > 5 && character < 8) + { + TNC->HOSTSTATE = 2; // Get Length + break; + } + + // Invalid + + Debugprintf("TRK - Invalid MsgType %d %x %x %x", character, *(ptr), *(ptr+1), *(ptr+2)); + break; + + case 2: // Get Length + + TNC->MSGCOUNT = character; + TNC->MSGCOUNT++; // Param is len - 1 + TNC->MSGLENGTH = TNC->MSGCOUNT; + CURSOR = &TNC->DEDBuffer[0]; + TNC->HOSTSTATE = 3; // Get Data + + break; + + case 5: // Collecting Null Terminated Response + + *(CURSOR++) = character; + + if (character) + continue; // MORE TO COME + + TrkProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + + break; + + default: + + // RECEIVING Counted Response + + *(CURSOR++) = character; + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + continue; // MORE TO COME + + TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); + TrkProcessDEDFrame(TNC); + + TNC->HOSTSTATE = 0; + TNC->InputLen = 0; + } + } + } + + // End of Input - Save buffer position + + TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); + TNC->RXLen = 0; +} + +VOID DEDPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + int Stream = 0; + int nn; + struct STREAMINFO * STREAM; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach + + int calllen = 0; + + TNC->CurrentMode = 0; // Mode may be changed manually + + + TNC->Streams[Stream].Attached = TRUE; + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER[6] |= 0x60; // Ensure P or T aren't used on ax.25 + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + // Set call to null to stop inbound connects (We only support one stream) + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "\1\1\1IDSPTNC"); + + if (Stream == 0) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + TNC->WEB_CHANGED = TRUE; + + // Stop Scanner + + TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch + + sprintf(Status, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Status); + + SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop + } + } + } + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Can't use retries, as we have no way of detecting lost chars. Have to re-init on timeout + + if (TNC->HostMode == 0 || TNC->ReinitState == 10) // 10 is Recovery Mode + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + Debugprintf("DEDHOST - Link to TNC Lost Port %d", TNC->Port); + TNC->TNCOK = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->WEB_CHANGED = TRUE; + + TNC->HostMode = 0; + TNC->ReinitState = 0; + + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + TNC->InitPtr = TNC->InitScript; + TNC->HOSTSTATE = 0; + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + UINT * buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + } + + if (TNC->SwitchToPactor) + { + TNC->SwitchToPactor--; + + if (TNC->SwitchToPactor == 0) + { + TNC->SwitchToPactor = TNC->RobustTime; + if (TNC->Robust) + SwitchToNormPacket(TNC, ""); + else + SwitchToRPacket(TNC, TNC->RobustSpeed); + } + } + + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TrkTidyClose, TrkForcedClose, TrkCloseComplete); + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + STREAM->ReportDISC = TRUE; + + } + + if (TNC->Timeout) + return; // We've sent something + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->InitPtr) + { + char * start, * end; + int len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + TNC->InitPtr = NULL; + Debugprintf("TRK - Init Complete Port %d", TNC->Port); + } + else + { + end = strchr(start, 13); + len = (int)(++end - start) - 1; // exclude cr + + TNC->InitPtr = end; + + Poll[0] = 0; // Channel + Poll[1] = 1; // Command + Poll[2] = len - 1; + memcpy(&Poll[3], start, len); + + StuffAndSend(TNC, Poll, len + 3); + + return; + + } + } + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(100); + sprintf(TNC->Streams[0].CmdSet, "\1\1\1%%B %s%c\1\1\1I%s", (TNC->RobustDefault) ? TNC->RobustSpeed : TNC->NormSpeed, 0, TNC->NodeCall); + + strcpy(TNC->Streams[0].MyCall, TNC->NodeCall); + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].CmdSet) + { + char * start, * end; + int len; + + start = TNC->Streams[Stream].CmdSet; + + if (*(start + 2) == 0) // End of Script + { + free(TNC->Streams[Stream].CmdSave); + TNC->Streams[Stream].CmdSet = NULL; + } + else + { + end = strchr(start + 3, 0); + len = (int)(++end - start) - 1; // exclude cr + TNC->Streams[Stream].CmdSet = end; + +// Debugprintf("TRK Cmdset %s", start + 3); + + memcpy(&Poll[0], start, len); + Poll[2] = len - 4; + + StuffAndSend(TNC, Poll, len); + + return; + } + } + } + + for (nn = 0; nn <= MaxStreams; nn++) + { + Stream = TNC->LastStream++; + + if (TNC->LastStream > MaxStreams) TNC->LastStream = 0; + + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + + buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + datalen = (int)buffptr->Len; + Buffer = (char *)buffptr->Data; // Data portion of frame + + Poll[0] = TNC->Streams[Stream].DEDStream; // Channel + + if (TNC->Streams[Stream].Connected) + { + if (TNC->SwallowSignon && Stream == 0) + { + TNC->SwallowSignon = FALSE; + if (strstr(Buffer, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + Poll[1] = 0; // Data + TNC->Streams[Stream].BytesTXed += datalen; + + Poll[2] = datalen - 1; + memcpy(&Poll[3], Buffer, datalen); + + WritetoTrace(TNC, Buffer, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TrkTidyClose(TNC, 0); + + // 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; + } + } + + ShowTraffic(TNC); + return; + } + + // Command. Do some sanity checking and look for things to process locally + + Poll[1] = 1; // Command + datalen--; // Exclude CR + + if (datalen == 0) // Null Command + { + ReleaseBuffer(buffptr); + return; + } + + Buffer[datalen] = 0; // Null Terminate + _strupr(Buffer); + + if (_memicmp(Buffer, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if (memcmp(Buffer, "RADIO ", 6) == 0) + { + sprintf(&Buffer[40], "%d %s", TNC->Port, &Buffer[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &Buffer[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr->Len = sprintf(buffptr->Data, "%s", &Buffer[40]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return; + } + + if ((Stream == 0) && memcmp(Buffer, "RPACKET", 7) == 0) + { + TNC->Robust = TRUE; + buffptr->Len = sprintf(buffptr->Data, "TRK} OK\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + SetWindowText(TNC->xIDC_MODE, "Robust Packet"); + strcpy(TNC->WEB_MODE, "Robust Packet"); + TNC->WEB_CHANGED = TRUE; + + return; + } + + if ((Stream == 0) && memcmp(Buffer, "HFPACKET", 8) == 0) + { + if (TNC->ForceRobust) + { + buffptr->Len = sprintf(buffptr->Data, "TRK} HF Packet Disabled\r"); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + return; + } + + if (strlen(Buffer) > 10) + { + // Speed follows HFPACKET + + Buffer += 9; + + Buffer = strtok(Buffer, " \r"); + + if (strlen(Buffer) < 6) + strcpy(TNC->NormSpeed, Buffer); + } + + buffptr->Len = sprintf(buffptr->Data, "TRK} OK\r"); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + TNC->Robust = FALSE; + SetWindowText(TNC->xIDC_MODE, "HF Packet"); + strcpy(TNC->WEB_MODE, "HF Packet"); + TNC->WEB_CHANGED = TRUE; + return; + } + + if (Buffer[0] == 'C' && datalen > 2) // Connect + { + if (*(++Buffer) == ' ') Buffer++; // Space isn't needed + + memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); + + TNC->Streams[Stream].Connecting = TRUE; + + if (Stream == 0) + { + // Send MYCall, Mode Command followed by connect + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(100); + + sprintf(TNC->Streams[0].CmdSet, "\1\1\1%%B %s%c\1\1\1I%s%c\1\1\1C%s", + (TNC->Robust) ? TNC->RobustSpeed : TNC->NormSpeed, 0, TNC->Streams[0].MyCall,0, Buffer); + + ReleaseBuffer(buffptr); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[Stream].MyCall, TNC->Streams[Stream].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + TNC->WEB_CHANGED = TRUE; + + TNC->Streams[0].InternalCmd = FALSE; + return; + } + } + + Poll[2] = datalen - 1; + memcpy(&Poll[3], buffptr + 2, datalen); + + ReleaseBuffer(buffptr); + + StuffAndSend(TNC, Poll, datalen + 3); + + TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; + + return; + } + } + + if (TNC->TNCOK && TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char CCMD[80] = "C"; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + datalen = buffptr->LENGTH - 7; + Buffer = &buffptr->DEST[0]; // Raw Frame + + Buffer[datalen] = 0; + + // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address + // before sending the beacon + + ConvFromAX25(Buffer, &Call[1]); // Dest + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 14; // Skip Origin + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(CCMD, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + Poll[0] = 0; // UI Channel + Poll[1] = 1; // CMD + Poll[2] = (int)strlen(CCMD) - 1; + strcpy(&Poll[3], CCMD); + StuffAndSend(TNC, Poll, Poll[2] + 4); + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400); + sprintf(TNC->Streams[0].CmdSet, "%c%c%c%s", 0, 0, 1, Buffer); + } + + ReleaseBuffer((UINT *)buffptr); + return; + } + + // if frames outstanding, issue a poll (but not too often) + + TNC->IntCmdDelay++; + + if (TNC->IntCmdDelay == 5 && TNC->Streams[0].FramesOutstanding) + { + Poll[0] = TNC->Streams[0].DEDStream; + Poll[1] = 0x1; // Command + TNC->InternalCmd = TRUE; + + Poll[2] = 1; // Len-1 + Poll[3] = '@'; + Poll[4] = 'B'; // Buffers + StuffAndSend(TNC, Poll, 5); + return; + } + + if (TNC->IntCmdDelay > 10) + { + TNC->IntCmdDelay = 0; + + if (TNC->Streams[0].FramesOutstanding) + { + Poll[0] = TNC->Streams[0].DEDStream; + Poll[1] = 0x1; // Command + TNC->InternalCmd = TRUE; + + Poll[2] = 0; // Len-1 + Poll[3] = 'L'; // Status + StuffAndSend(TNC, Poll, 4); + + return; + } + } + // Need to poll channels 0 and 1 in turn + + TNC->StreamtoPoll++; + + if (TNC->StreamtoPoll > 1) + TNC->StreamtoPoll = 0; + + Poll[0] = TNC->StreamtoPoll; // Channel + Poll[1] = 0x1; // Command + Poll[2] = 0; // Len-1 + Poll[3] = 'G'; // Poll + + StuffAndSend(TNC, Poll, 4); + TNC->InternalCmd = FALSE; + + return; + +} + +VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE, "%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->WEB_CHANGED = TRUE; + + memcpy(&TNC->TXBuffer[0], "\x18\x1b\r", 2); + TNC->TXLen = 2; + + if (TrkWriteCommBlock(TNC) == FALSE) + { + CloseCOMPort(TNC->hDevice); + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + } + return; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + // Put into Host Mode + + memcpy(Poll, "\x18\x1bJHOST1\r", 9); + + TNC->TXLen = 9; + TrkWriteCommBlock(TNC); + + TNC->ReinitState = 5; + return; + } + + if (TNC->ReinitState == 5) + TNC->ReinitState = 0; + +} + +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; + TrkExitHost(TNC); + + return; + } + + if (TNC->ReinitState == 1) + { + // No Response to trying to enter term mode - do error recovery + + Debugprintf("TRK - Starting Resync Port %d", TNC->Port); + + TNC->ReinitState = 10; + TNC->ReinitCount = 256; + TNC->HostMode = TRUE; // Must be in Host Mode if we need recovery + + Poll[0] = 1; + TNC->TXLen = 1; + TrkWriteCommBlock(TNC); + TNC->Timeout = 10; // 2 secs + + return; + } + + if (TNC->ReinitState == 10) + { + // Continue error recovery + + TNC->ReinitCount--; + + if (TNC->ReinitCount) + { + Poll[0] = 1; + TNC->TXLen = 1; + TrkWriteCommBlock(TNC); + TNC->Timeout = 3; // 0.3 secs + + return; + } + + // Try Again + + Debugprintf("TRK Continuing recovery Port %d", TNC->Port); + + TNC->ReinitState = 0; + + // Close and re-open TNC + + TrkExitHost(TNC); + Sleep(50); + CloseCOMPort(TNC->hDevice); + TNC->hDevice =(HANDLE)0; + TNC->ReopenTimer = 290; + TNC->HostMode = FALSE; + + return; + } + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +//#include "Mmsystem.h" + +VOID TrkExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[0] = 1; + TNC->TXBuffer[1] = 1; + TNC->TXBuffer[2] = 1; + memcpy(&TNC->TXBuffer[3], "%R", 2); + + StuffAndSend(TNC, Poll, 5); + + return; +} + +VOID StuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + TNC->TXLen = Len; + TrkWriteCommBlock(TNC); +} + +VOID TrkProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', TNC->RXBuffer, TNC->RXLen); + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + } + + if (TNC->ReinitState == 1) + { + // trying to set term mode + + // If already in Term Mode, TNC echos command, with control chars replaced with '.' + + if (memcmp(TNC->RXBuffer, "....%R", 6) == 0) + { + // In term mode, Need to put into Host Mode + + TNC->ReinitState = 2; + DoTNCReinit(TNC); + return; + } + } + + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } + + if (TNC->ReinitState == 5) // Waiting for response to JHOST1 + { + if (TNC->RXBuffer[TNC->RXLen-1] == 10 || TNC->RXBuffer[TNC->RXLen-1] == 13) // NewLine + { + TNC->HostMode = TRUE; + TNC->Timeout = 0; + } + return; + } +} + +VOID TrkProcessDEDFrame(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + char * Buffer; // Data portion of frame + char Status[80]; + UINT Stream = 0; + UCHAR * Msg = TNC->DEDBuffer; + int framelen = TNC->InputLen; + + if (TNC->WRITELOG) + WriteDebugLogLine(TNC->Port, 'R', Msg, framelen); + + if (TNC->ReinitState == 10) + { + // Recovering from Sync Failure + + // Any Response indicates we are in host mode, and back in sync + + TNC->HostMode = TRUE; + TNC->Timeout = 0; + TNC->ReinitState = 0; + TNC->RXLen = 0; + TNC->HOSTSTATE = 0; + + Debugprintf("TRK - Resync Complete"); + return; + } + + // Any valid frame is an ACK + + TNC->Timeout = 0; + + if (TNC->TNCOK == FALSE) + { + // Just come up + + TNC->TNCOK = TRUE; + sprintf(TNC->WEB_COMMSSTATE, "%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->WEB_CHANGED = TRUE; + } + + if (TNC->InitPtr) // Response to Init Script + return; + + if (TNC->MSGCHANNEL > 26) + return; + + Stream = TNC->MSGCHANNEL - 1; + + // See if Poll Reply or Data + + if (TNC->MSGTYPE == 0) + { + // Success - Nothing Follows + + if (Stream < 32) + if (TNC->Streams[Stream].CmdSet) + return; // Response to Command Set or Init Script + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + return; + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + return; + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "TRK} Ok\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE > 0 && TNC->MSGTYPE < 6) + { + // Success with message - null terminated + + char * ptr; + int len; + + Buffer = Msg; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return; + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (TNC->MSGTYPE < 3) // 1 or 2 - Success or Fail + { + // See if a response to internal command + + if (TNC->InternalCmd) + { + // Process it + + char LastCmd = TNC->TXBuffer[3]; + + if (LastCmd == 'L') // Status + { + int s1, s2, s3, s4, s5, s6, num; + + num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6); + + TNC->Streams[Stream].FramesOutstanding = s3; + return; + } + + if (LastCmd == '@') // @ Commands + { + if (TNC->TXBuffer[4]== 'B') // Buffer Status + { + TNC->Buffers = atoi(Buffer); + SetWindowText(TNC->xIDC_BUFFERS, Buffer); + strcpy(TNC->WEB_BUFFERS, Buffer); + return; + } + } + + if (LastCmd == '%') // % Commands + { + if (TNC->TXBuffer[4]== 'T') // TX count Status + { + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %s", TNC->Streams[Stream].BytesRXed, TNC->Streams[Stream].BytesTXed, Buffer); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + TNC->WEB_CHANGED = TRUE; + return; + } + + if (TNC->TXBuffer[4] == 'W') // Scan Control + { + if (Msg[4] == '1') // Ok to Change + TNC->OKToChangeFreq = 1; + else + TNC->OKToChangeFreq = -1; + } + } + return; + } + + // Not Internal Command, so send to user + + if (TNC->Streams[Stream].CmdSet || TNC->InitPtr) + return; // Response to Command Set or Init Script + + if ((TNC->TXBuffer[1] & 1) == 0) // Data + { + // Should we look for "CHANNEL NOT CONNECTED" here (or somewhere!) + return; + } + + // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc + + if (TNC->TXBuffer[3] == 'G') // Poll + return; + + if (TNC->TXBuffer[3] == 'C') // Connect - reply we need is async + return; + + if (TNC->TXBuffer[3] == 'L') // Shouldnt happen! + return; + + if (TNC->TXBuffer[3] == 'J') // JHOST + { + if (TNC->TXBuffer[8] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return; + } + } + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "TRK} %s", Buffer); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE") || strstr(Buffer, "BUSY")) + { + if ((STREAM->Connecting | STREAM->Connected) == 0) + return; + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + if (strstr(Buffer, "BUSY")) + buffptr->Len = sprintf(buffptr->Data, "*** Busy from %s\r", TNC->Streams[Stream].RemoteCall); + else + buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesOutstanding = 0; + + if (Stream == 0) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + TNC->WEB_CHANGED = TRUE; + } + + if (TNC->RPBEACON) + SendRPBeacon(TNC); + + 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 (TNC->RPBEACON) + SendRPBeacon(TNC); + + return; + } + + if (strstr(Buffer, "CONNECTED")) + { + char * Call = strstr(Buffer, " to "); + char * ptr; + char MHCall[30]; + + Call += 4; + + if (Call[1] == ':') + Call +=2; + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strchr(Call, 13); + if (ptr) *ptr = 0; + + STREAM->Connected = TRUE; // Subsequent data to data channel + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + if (TNC->SlowTimer) + Debugprintf("RP Incoming call to APPLCALL completed"); + + TNC->SlowTimer = 0; // Cancel Reset MYCALL timer + + // Stop Scanner + + if (Stream == 0) + { + TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch + + sprintf(Status, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Status); + + memcpy(MHCall, Call, 9); + MHCall[9] = 0; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + char DestCall[10]; + TRANSPORTENTRY * SESS; + struct WL2KInfo * WL2K = TNC->WL2K; + struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; + int Totallen = 0; + + if (Stream == 0) + { + char Save = TNC->RIG->CurrentBandWidth; + TNC->RIG->CurrentBandWidth = 'R'; + UpdateMH(TNC, MHCall, '+', 'I'); + TNC->RIG->CurrentBandWidth = Save; + } + + ProcessIncommingConnect(TNC, Call, Stream, FALSE); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN > 0) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + else if (FULL_CTEXT) + { + Totallen = CTEXTLEN; + ptr = CTEXTMSG; + } + + while (Totallen) + { + int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + + if (sendLen == 0) + sendLen = 80; + + if (Totallen < sendLen) + sendLen = Totallen; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sendLen; + memcpy(&buffptr->Data[0], ptr, sendLen); + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + } + Totallen -= sendLen; + ptr += sendLen; + } + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + 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->Streams[0].MyCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0); // Convert to Centre Freq + if (SESS->Frequency == 0) + { + // try to get from WL2K record + + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->Streams[0].MyCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + { + strcpy(SESS->RMSCall, WL2K->RMSCall); + SESS->Mode = WL2K->mode; + } + + if (Stream == 0) + { + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + TNC->WEB_CHANGED = TRUE; + + // If an autoconnect APPL is defined, send it + // See which application the connect is for + + strcpy(DestCall, STREAM->MyCall); + + if (TNC->UseAPPLCalls && strcmp(DestCall, TNC->NodeCall) != 0) // Not Connect to Node Call + { + if (strcmp(DestCall, NodeCall) == 0) // Call to NodeCall (when not PortCall) + { + goto DontUseAPPLCmd; + } + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(DestCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + int MsgLen = sprintf(Buffer, "%s\r", AppName); + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = strlen(Msg); + memcpy(buffptr->Data, Msg, strlen(Msg)); + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->NeedDisc = 100; // 10 secs + } + return; + } + + // Not to a known appl - drop through to Node + } + + // if (TNC->UseAPPLCalls) + // goto DontUseAPPLCmd; + + 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(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + + } // End of Stream 0 or RP or Drop through from not APPL Connect + + DontUseAPPLCmd: + 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); + + if (Stream == 0) + { + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + TNC->WEB_CHANGED = TRUE; + + if (STREAM->DEDStream == 30) // Robust Mode + { + char Save = TNC->RIG->CurrentBandWidth; + TNC->RIG->CurrentBandWidth = 'R'; + UpdateMH(TNC, Call, '+', 'O'); + TNC->RIG->CurrentBandWidth = Save; + } + else + { + UpdateMH(TNC, Call, '+', 'O'); + } + } + return; + } + } + return; + } + + if (TNC->MSGTYPE == 4 || TNC->MSGTYPE == 5) + { + + // 4 Is header only - Null Terminated + // 5 Is header with data following - Null Terminated + + struct STREAMINFO * STREAM = &TNC->Streams[0]; // RP Stream + + // Monitor + + if (TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Attached == FALSE) + { + // See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL + + char DestCall[10] = "NOCALL "; + char * ptr1 = strstr(&Msg[7], "to "); + int i; + APPLCALLS * APPL; + char Appl[11]; + char Status[80]; + + if (ptr1) memcpy(DestCall, &ptr1[3], 10); + + ptr1 = strchr(DestCall, ' '); + if (ptr1) *(ptr1) = 0; // Null Terminate + + Debugprintf("RP SABM Received for %s" , DestCall); + + if (strcmp(TNC->NodeCall, DestCall) != 0 && TNC->SlowTimer == 0) + { + // Not Calling NodeCall/Portcall + + if (strcmp(NodeCall, DestCall) == 0) + goto SetThisCall; + + // See if to one of our ApplCalls + + 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 = 0; + + if (strcmp(Appl, DestCall) == 0) + { + SetThisCall: + + TNC->SlowTimer = 450; // Allow 45 seconds for connect to complete + Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall); + + sprintf(Status, "%d SCANSTART 60", TNC->Port); // Pause scan for 60 secs + Rig_Command(-1, Status); + + if ((TNC->RIG == &TNC->DummyRig || TNC->RIG == NULL) && TNC->RobustTime) + TNC->SwitchToPactor = 600; // Don't change modes for 60 secs + + strcpy(STREAM->MyCall, DestCall); + STREAM->CmdSet = STREAM->CmdSave = zalloc(100); + sprintf(STREAM->CmdSet, "\1\1\1I%s", DestCall); + break; + } + } + } + } + } + + DoMonitorHddr(TNC, Msg, framelen, TNC->MSGTYPE); + return; + + } + + // 1, 2, 4, 5 - pass to Appl + + if (TNC->MSGCHANNEL == 0) // Unproto Channel + return; + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"TRK} %s", &Msg[4]); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (TNC->MSGTYPE == 6) + { + // Monitor Data With length) + + DoMonitorData(TNC, Msg, framelen); + return; + } + + if (TNC->MSGTYPE == 7) + { + //char StatusMsg[60]; + //int Status, ISS, Offset; + + // Connected Data + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + buffptr->Len = framelen; // Length + TNC->Streams[Stream].BytesRXed += (int)buffptr->Len; + memcpy(buffptr->Data, Msg, buffptr->Len); + + WritetoTrace(TNC, Msg, (int)buffptr->Len); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + ShowTraffic(TNC); + + return; + } +} + + +static MESSAGE Monframe[32]; // I frames come in two parts. + +static MESSAGE * AdjMsg[32]; // Adjusted for digis + + +VOID DoMonitorHddr(struct TNCINFO * TNC, UCHAR * Msg, int Len, int Type) +{ + // Convert to ax.25 form and pass to monitor + + // Only update MH on UI, SABM, UA + + UCHAR * ptr, * starptr; + char * context; + int Port = TNC->Port; + char * MHCall = Monframe[Port].ORIGIN; + + Monframe[Port].LENGTH = MSGHDDRLEN + 16; // Control Frame + Monframe[Port].PORT = TNC->Port; + + AdjMsg[Port] = &Monframe[Port]; // Adjusted fir digis + ptr = strstr(Msg, "fm "); + + ConvToAX25(&ptr[3], Monframe[Port].ORIGIN); + +// memcpy(MHCall, &ptr[3], 11); +// strlop(MHCall, ' '); + + ptr = strstr(ptr, "to "); + + ConvToAX25(&ptr[3], Monframe[Port].DEST); + + ptr = strstr(ptr, "via "); + + if (ptr) + { + // We have digis + + char Save[100]; + char * fiddle; + + memcpy(Save, &ptr[4], 60); + + ptr = strtok_s(Save, " ", &context); +DigiLoop: + + fiddle = (char *)AdjMsg[Port]; + fiddle += 7; + AdjMsg[Port] = (MESSAGE *)fiddle; + + Monframe[Port].LENGTH += 7; + + starptr = strchr(ptr, '*'); + if (starptr) + *(starptr) = 0; + + ConvToAX25(ptr, AdjMsg[Port]->ORIGIN); + + if (starptr) + AdjMsg[Port]->ORIGIN[6] |= 0x80; // Set end of address + + ptr = strtok_s(NULL, " ", &context); + + if (memcmp(ptr, "ctl", 3)) + goto DigiLoop; + } + AdjMsg[Port]->ORIGIN[6] |= 1; // Set end of address + + ptr = strstr(Msg, "ctl "); + + if (memcmp(&ptr[4], "SABM", 4) == 0) + { + AdjMsg[Port]->CTL = 0x2f; + if (TNC->Robust) + UpdateMHwithDigis(TNC, MHCall, '.', 0); + else + UpdateMHwithDigis(TNC, MHCall, ' ', 0); + } + else + if (memcmp(&ptr[4], "DISC", 4) == 0) + AdjMsg[Port]->CTL = 0x43; + else + if (memcmp(&ptr[4], "UA", 2) == 0) + { + AdjMsg[Port]->CTL = 0x63; + if (TNC->Robust) + UpdateMHwithDigis(TNC, MHCall, '.', 0); + else + UpdateMHwithDigis(TNC, MHCall, ' ', 0); + } + else + if (memcmp(&ptr[4], "DM", 2) == 0) + AdjMsg[Port]->CTL = 0x0f; + else + if (memcmp(&ptr[4], "UI", 2) == 0) + { + AdjMsg[Port]->CTL = 0x03; + if (TNC->Robust) + UpdateMHwithDigis(TNC, MHCall, '.', 0); + else + UpdateMHwithDigis(TNC, MHCall, ' ', 0); + } + else + if (memcmp(&ptr[4], "RR", 2) == 0) + AdjMsg[Port]->CTL = 0x1 | (ptr[6] << 5); + else + if (memcmp(&ptr[4], "RNR", 3) == 0) + AdjMsg[Port]->CTL = 0x5 | (ptr[7] << 5); + else + if (memcmp(&ptr[4], "REJ", 3) == 0) + AdjMsg[Port]->CTL = 0x9 | (ptr[7] << 5); + else + if (memcmp(&ptr[4], "FRMR", 4) == 0) + AdjMsg[Port]->CTL = 0x87; + else + if (ptr[4] == 'I') + { + AdjMsg[Port]->CTL = (ptr[5] << 5) | (ptr[6] & 7) << 1 ; + } + + if (strchr(&ptr[4], '+')) + { + AdjMsg[Port]->CTL |= 0x10; + Monframe[Port].DEST[6] |= 0x80; // SET COMMAND + } + + if (strchr(&ptr[4], '-')) + { + AdjMsg[Port]->CTL |= 0x10; + Monframe[Port].ORIGIN[6] |= 0x80; // SET COMMAND + } + + if (Type == 5) // More to come + { + ptr = strstr(ptr, "pid "); + sscanf(&ptr[3], "%x", (UINT *)&AdjMsg[Port]->PID); + return; + } + + time(&Monframe[Port].Timestamp); + + BPQTRACE((MESSAGE *)&Monframe[Port], TRUE); +} + +VOID DoMonitorData(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + int Port = TNC->Port; + + // // Second part of I or UI + + if (AdjMsg[Port]) + { + memcpy(AdjMsg[Port]->L2DATA, Msg, Len); + Monframe[Port].LENGTH += Len; + + time(&Monframe[Port].Timestamp); + + BPQTRACE((MESSAGE *)&Monframe[Port], TRUE); + } + return; +} + + +//1:fm G8BPQ to KD6PGI-1 ctl I11^ pid F0 +//fm KD6PGI-1 to G8BPQ ctl DISC+ + +VOID TrkTidyClose(struct TNCINFO * TNC, int Stream) +{ + // Queue it as we may have just sent data + + TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = zalloc(100); + sprintf(TNC->Streams[Stream].CmdSet, "\1\1\1D"); +} + + +VOID TrkForcedClose(struct TNCINFO * TNC, int Stream) +{ + TrkTidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID TrkCloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Status[80]; + + TNC->NeedPACTOR = 20; // Delay a bit for UA to be sent before changing mode and call + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + strcpy(TNC->WEB_TNCSTATE, "Idle"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + TNC->WEB_CHANGED = TRUE; + + + Rig_Command(-1, Status); + + if (TNC->RIG == &TNC->DummyRig) // Not using Rig control + TNC->SwitchToPactor = TNC->RobustTime; + + ReleaseOtherPorts(TNC); +} + +VOID SwitchToRPacket(struct TNCINFO * TNC, char * Baud) +{ + if (TNC->Robust == FALSE) + { + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(100); + sprintf(TNC->Streams[0].CmdSet, "\1\1\1%%B %s", Baud); + TNC->Robust = TRUE; + SetWindowText(TNC->xIDC_MODE, "Robust Packet"); + strcpy(TNC->WEB_MODE, "Robust Packet"); + TNC->WEB_CHANGED = TRUE; + } +} +VOID SwitchToNormPacket(struct TNCINFO * TNC, char * Baud) +{ + if (TNC->ForceRobust) + return; + + TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(100); + + if (Baud[0] == 0) + sprintf(TNC->Streams[0].CmdSet, "\1\1\1%%B %s", TNC->NormSpeed); + else + sprintf(TNC->Streams[0].CmdSet, "\1\1\1%%B %s", Baud); + + TNC->Robust = FALSE; + + SetWindowText(TNC->xIDC_MODE, "HF Packet"); + strcpy(TNC->WEB_MODE, "HF Packet"); + TNC->WEB_CHANGED = TRUE; +} + +VOID SendRPBeacon(struct TNCINFO * TNC) +{ + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char BEACONMSG[80]; + + int DataLen = sprintf(BEACONMSG, "QRA %s %s", TNC->NodeCall, LOC); + + // Block includes the Msg Header (7 bytes), Len Does not! + + ConvToAX25("BEACON", AXPTR->DEST); + ConvToAX25(TNC->NodeCall, AXPTR->ORIGIN); + + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, BEACONMSG, DataLen); + + Send_AX(&AXMSG, DataLen + 16, TNC->Port); + return; + +} + + + diff --git a/SerialPort.c b/SerialPort.c new file mode 100644 index 0000000..490c550 --- /dev/null +++ b/SerialPort.c @@ -0,0 +1,1142 @@ +/* +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 +*/ + +// +// Interface to allow G8BPQ switch to use Serial TNC in character mode + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD +#include +#include +#endif +#endif +#endif + + +#include "CHeaders.h" + + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL SerialWriteCommBlock(struct TNCINFO * TNC); +void SerialCheckRX(struct TNCINFO * TNC); +int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int SerialGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); + +static char ClassName[]="SERIALSTATUS"; +static char WindowTitle[] = "SERIAL"; +static int RigControlRow = 165; + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + // Read Initialisation lines + + while(TRUE) + { + if (SerialGetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else if (_memicmp(buf, "DRAGON", 6) == 0) + TNC->Dragon = TRUE; + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int SerialGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +BOOL SerialReadConfigFile(int Port, int ProcLine()) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} + + + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID SerialChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); + SerialSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->hDevice == 0) + { + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + // Try to reopen every 30 secs + + if (fn > 3 && fn < 7) + goto ok; + + TNC->ReopenTimer++; + + if (TNC->ReopenTimer < 300) + return 0; + + TNC->ReopenTimer = 0; + + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + if (TNC->hDevice == 0) + return 0; + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + Debugprintf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif +#endif + SendInitScript(TNC); + + } +ok: + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SerialCheckRX(TNC); + return 0; + + case 1: // poll + + while (TNC->PortRecord->UI_Q) + { + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + ReleaseBuffer(buffptr); + continue; + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + SerialSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("Serial New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + SerialChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->BytesTXed += txlen; + + bytes=SerialSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->hDevice) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + // for now just send, but allow sending control + // characters with \\ or ^ escape + +// if (STREAM->Connected) + { + STREAM->PacketsSent++; + + if (strstr(TXMsg, "\\\\") || strchr(TXMsg, '^')) + { + txlen = ProcessEscape(TXMsg); + + if (txlen == 0) // Must be \\D + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + } + + bytes=SerialSendData(TNC, TXMsg, txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->BytesTXed += bytes; +// WritetoTrace(TNC, &buff->L2DATA[0], txlen); + + return 1; + } +/* + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); + + SerialChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SerialSendCommand(TNC, Connect); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + + SerialSendData(TNC, TXMsg, txlen); + + return 0; +*/ + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + Outstanding = Queued = 0; + + if (TNC->Mode == 'O') // OFDM version has more buffer space + { + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else + { + if (Queued > 4 || Outstanding > 2000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + } + if (TNC->Streams[Stream].Attached == 0) + return (TNC->hDevice != 0) << 8 | 1; + + return ((TNC->hDevice != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (TNC->ARDOPCommsMode == 'T') // TCP Mode + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + } + else + { + // Serial Modes + + if (!TNC->HostMode) + return 0; // No connection so no interlock + } + + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + SerialSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + SerialSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID SerialReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + SerialChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID SerialSuspendPort(struct TNCINFO * TNC) +{ + SerialSendCommand(TNC, "CONOK OFF\r"); +} + +VOID SerialReleasePort(struct TNCINFO * TNC) +{ + SerialSendCommand(TNC, "CONOK ON\r"); +} + + +VOID * SerialExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + + sprintf(Msg,"Serial TNC %s\n", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(Msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + if (TNCInfo[port]) // If restarting, free old config + free(TNCInfo[port]); + + TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (PortConfig[port]) // May not have config + SerialReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->Hardware = H_SERIAL; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = SerialSuspendPort; + TNC->ReleasePortProc = SerialReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + printf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif +#endif + + SendInitScript(TNC); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + SerialSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + SerialSendCommand(TNC, "DISCONNECT\r"); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + SerialReleaseTNC(TNC); + } +} + +VOID SerialAbort(struct TNCINFO * TNC) +{ + SerialSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID SerialDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +VOID SendInitScript(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + char * ptr1, * ptr2; + int len; + + // Send INIT script + + ptr1 = &TNC->InitScript[0]; + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + + if (ptr2 == 0) + { + TNC->ReinitState = 3; + break; + } + + len = (int)(ptr2 - ptr1) + 1; + + memcpy(Poll, ptr1, len); + + TNC->TXLen = len; + SerialWriteCommBlock(TNC); + + Sleep(50); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } +} + + + +VOID SerialProcessTNCMessage(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +void SerialCheckRX(struct TNCINFO * TNC) +{ + int Length, Len = 0; + + if (TNC->RXLen == 250) + TNC->RXLen = 0; + + if (TNC->hDevice) + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 250 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + // Also need timeout for incomplete lines + +// if ((strchr(TNC->RXBuffer, 10) == 0) && (strchr(TNC->RXBuffer, 13) == 0)) +// return; // Wait for rest of frame + + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); +// CloseLogFile(TNC->Port); + + TNC->RXLen = 0; // Ready for next frame + + SerialProcessTNCMessage(TNC); + return; +} + +int SerialWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, txlen); + + return 0; +} + +int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); + + return 0; +} + +int ProcessEscape(UCHAR * TXMsg) +{ + UCHAR * Orig = TXMsg; + UCHAR * ptr1 = TXMsg; + UCHAR * ptr2 = strstr(TXMsg, "\\\\"); + UCHAR * ptr3 = strchr(TXMsg, '^'); + + BOOL HexEscape = FALSE; + int NewLen; + + // Now using ^C for ctrl/c, etc + // Still use \\d + // ^^ for ^, \\\ for \\ + + while (ptr2) + { + UCHAR Next; + + ptr1 = ptr2; // over stuff before escape + + ptr2 += 2; // over \\ + + Next = *ptr2; + + if (Next == '\\') + { + ptr1 = ptr2; // put \\ in message + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + } + else if (Next == 'd' || Next == 'D') + return 0; + + ptr2 = strstr(ptr2, "\\\\"); + } + + // now look for ^ + + ptr1 = Orig; + ptr2 = strchr(Orig, '^'); + + while (ptr2) + { + UCHAR Next; + + ptr1 = ptr2; // over stuff before escape + ptr2 ++; // over ^ + + Next = *ptr2; + + if (Next != '^') + { + Next &= 0x1F; // Mask to control char + HexEscape = TRUE; + } + + *ptr1 = Next; + + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + + ptr2 = strchr(ptr2, '^'); + } + + NewLen = (int)strlen(Orig); + + if (HexEscape) + { + // remove trailing CR + + NewLen --; + + Orig[NewLen] = 0; + } + + return NewLen; +} + \ No newline at end of file diff --git a/StdVer.inc b/StdVer.inc new file mode 100644 index 0000000..c7d86c7 --- /dev/null +++ b/StdVer.inc @@ -0,0 +1,59 @@ + + +1 VERSIONINFO + FILEVERSION Vers + PRODUCTVERSION Vers + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", VerComments + VALUE "CompanyName", "G8BPQ\0" + VALUE "FileDescription", VerDesc + VALUE "FileVersion", Verstring + // VALUE "InternalName", "bpq32\0" + VALUE "LegalCopyright", VerCopyright +// VALUE "OriginalFilename", "bpq32.dll\0" + VALUE "ProductName", "BPQ32\0" + VALUE "ProductVersion", Verstring + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + +#define BPQICON 2 + +BPQICON ICON DISCARDABLE "..\\bpqicon.ico" + +#ifdef MAILCHAT + +IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 202, 69 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "System" +BEGIN + ICON 107,BPQICON,14,9,20,20 + LTEXT "G8BPQ Mail Version ",-1,45,15,101,8, + SS_NOPREFIX | NOT WS_GROUP + LTEXT Verstring,-1,115,15,58,8,NOT WS_GROUP + LTEXT VerCopyright,-1,10,32,180,13,NOT WS_GROUP | ES_CENTER + + DEFPUSHBUTTON "OK",IDOK,88,51,30,11,BS_CENTER | BS_VCENTER | WS_GROUP +END + +#endif + + diff --git a/Strucs.inc b/Strucs.inc new file mode 100644 index 0000000..df7fbcd --- /dev/null +++ b/Strucs.inc @@ -0,0 +1,572 @@ + +EXCLUDEBITS EQU 0 ; INCLUDE ExcludeList SUPPORT + +TNCBUFFLEN EQU 400h +BPQHOSTSTREAMS EQU 64 +;BUFFLEN EQU 400 ; ?? + +MAXAPPLS EQU 32 ; Max Application Commands +ApplOffset EQU 80000 ; Applications string in buffer +C_INFOMSG EQU 85000 ; Info Msg in buffer +ApplStringLen EQU 48 ; Length of each config entry + +ALIASLEN EQU 32 ; Max chars in command alias + +MHENTRIES EQU 30 ; Entries in MH List + +; +; BUFFLEN-4 = L2 POINTER (FOR CLEARING TIMEOUT WHEN ACKMODE USED) +; BUFFLEN-8 = TIMESTAMP +; BUFFLEN-12 = BUFFER ALLOCATED FLAG (ADDR OF ALLOCATING ROUTINE) + +;MAXDATA EQU BUFFLEN-16 + +MAXDIGIS EQU 8 +; +; BPQHOST MODE VECTOR STRUC +; +BPQVECSTRUC STRUC + + +HOSTSESSION DD 0 +HOSTFLAGS DB 0 ; ALLOCATED AND STATE CHANGE FLAGS +HOSTAPPLMASK DD 0 +HOSTAPPLFLAGS DB 0 +HOSTSTREAM DB 0 ; STREAM NUMBER +HOSTTRACEQ DD 0 + +HOSTHANDLE DD 0 ; HANDLE FOR POSTING MSGS TO + +HOSTAPPLNUM DD 0 ; Application Number + +STREAMOWNER DD 0 ; PID of Process owning stream +HOSTPGMNAME DB 32 dup (0); + +BPQVECSTRUC ENDS + +; HOSTFLAGS = Bit 80 = Allocated +; Bit 40 = Disc Request +; Bit 20 = Stay Flag +; Bit 02 and 01 State Change Bits + + +VECTORLENGTH EQU TYPE BPQVECSTRUC + +; +; BASIC LINK LEVEL MESSAGE BUFFER LAYOUT +; +MESSAGE STRUC + +MSGCHAIN DD ? ; CHAIN WORD +MSGPORT DB ? ; PORT +MSGLENGTH DW ? ; LENGTH + +MSGDEST DB 7 DUP (?) ; DESTINATION +MSGORIGIN DB 7 DUP (?) ; ORIGIN +; +; MAY BE UP TO 56 BYTES OF DIGIS +; +MSGCONTROL DB ? ; CONTROL BYTE +MSGPID DB ? ; PROTOCOL IDENTIFIER +MSGDATA DB ? ; START OF LEVEL 2 MESSAGE +; +MESSAGE ENDS +; + +L3MESSAGE STRUC +; +; NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO +; +L3HEADER DB 7 DUP (?) ; CHAIN, PORT, LENGTH +L3PID DB ? ; PID + +L3SRCE DB 7 DUP (?) ; ORIGIN NODE +L3DEST DB 7 DUP (?) ; DEST NODE +L3MONR DB ? ; TX MONITOR FIELD - TO PREVENT MESSAGE GOING + ; ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +; +; NETROM LEVEL 4 DATA +; +L4INDEX DB ? ; TRANSPORT SESSION INDEX +L4ID DB ? ; TRANSPORT SESSION ID +L4TXNO DB ? ; TRANSMIT SEQUENCE NUMBER +L4RXNO DB ? ; RECEIVE (ACK) SEQ NUMBER +L4FLAGS DB ? ; FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + +L4DATA DB ? ; DATA +L4CALLS DB 14 DUP (?) ; CALLS IN CONNECT REQUEST +L4_BPQ DB ? ; THENODE EXTENDED CONNECT PARAMS + +L3MESSAGE ENDS + +; +; L4FLAGS DEFINITION +; +L4BUSY EQU 80H ; BNA - DONT SEND ANY MORE +L4NAK EQU 40H ; NEGATIVE RESPONSE FLAG +L4MORE EQU 20H ; MORE DATA FOLLOWS - FRAGMENTATION FLAG + +L4CREQ EQU 1 ; CONNECT REQUEST +L4CACK EQU 2 ; CONNECT ACK +L4DREQ EQU 3 ; DISCONNECT REQUEST +L4DACK EQU 4 ; DISCONNECT ACK +L4INFO EQU 5 ; INFORMATION +L4IACK EQU 6 ; INFORMATION ACK +; +; PORT CONTROL TABLE +; +PORTCONTROL STRUC + +PORTCALL DB 7 DUP (0) +PORTALIAS DB 7 DUP (0) ; USED FOR UPLINKS ONLY +PORTNUMBER DB ? +PORTPOINTER DD ? ; NEXT IN CHAIN +PORTQUALITY DB ? ; 'STANDARD' QUALITY FOR THIS PORT +PORTRX_Q DD ? ; FRAMES RECEIVED ON THIS PORT +PORTTX_Q DD ? ; FRAMES TO BE SENT ON THIS PORT +PORTTXROUTINE DD ? ; POINTER TO TRANSMIT ROUTINE FOR THIS PORT +PORTRXROUTINE DD ? ; POINTER TO RECEIVE ROUTINE FOR THIS PORT +PORTINITCODE DD ? ; INITIALISATION ROUTINE +PORTTIMERCODE DD ? +PORTCLOSECODE DD ? +PORTTXCHECK DD ? ; OK to Send Check + +PORTDESCRIPTION DB 30 DUP (0) ; TEXT DESCRIPTION OF FREQ/SPEED ETC +PORTBBSFLAG DB ? ; NZ MEANS PORT CALL/ALIAS ARE FOR BBS +PORTL3FLAG DB ? ; NZ RESTRICTS OUTGOING L2 CONNECTS +; +; CWID FIELDS +; +CWID DW 9 DUP (0) ; 8 ELEMENTS + FLAG +ELEMENT DW ? ; REMAINING BITS OF CURRENT CHAR +CWPOINTER DD ? ; POINTER TO NEXT CHAR +CWIDTIMER DW ? ; TIME TO NEXT ID +CWSTATE DB ? ; STATE MACHINE FOR CWID +CWTYPE DB ? ; SET TO USE ON/OFF KEYING INSTEAD OF + ; FSK (FOR RUH MODEMS) +PORTMINQUAL DB ? ; MIN QUAL TO BRAOCAST ON THIS PORT + +; STATS COUNTERS +; +L2DIGIED DD ? +L2FRAMES DD ? +L2FRAMESFORUS DD ? +L2FRAMESSENT DD ? +L2TIMEOUTS DD ? +L2ORUNC DD ? ; OVERRUNS +L2URUNC DD ? ; UNDERRUNS +L1DISCARD DD ? ; FRAMES DISCARDED (UNABLE TO TX DUE TO DCD) +L2FRMRRX DD ? +L2FRMRTX DD ? +RXERRORS DD ? ; RECEIVE ERRORS +L2REJCOUNT DD ? ; REJ FRAMES RECEIVED +L2OUTOFSEQ DD ? ; FRAMES RECEIVED OUT OF SEQUENCE +L2RESEQ DD ? ; FRAMES RESEQUENCED +SENDING DW 0 ; LINK STATUS BITS +ACTIVE DW 0 + +AVSENDING DB 0 ; LAST MINUTE +AVACTIVE DB 0 + +PORTTYPE DB 0 ; H/W TYPE + ; 0 = ASYNC, 2 = PC120, 4 = DRSI + ; 6 = TOSH, 8 = QUAD, 10 = RLC100 + ; 12 = RLC400 14 = INTERNAL 16 = EXTERNAL ; 18 = i2c + +PROTOCOL DB 0 ; PORT PROTOCOL + ; 0 = KISS, 2 = NETROM, 4 = BPQKISS + ; 6 = HDLC, 8 = L2, 10 = PACTOR + +IOBASE DW ? ; CONFIG PARAMS FOR HARDWARE DRIVERS +INTLEVEL DB ? ; FIRST 4 SAME FOR ALL H/W TYPES +BAUDRATE DD ? ; SPEED +CHANNELNUM DB ? ; ON MULTICHANNEL H/W +INTCHAIN DD ? ; POINTER TO NEXT PORT USING THIS LEVEL +PORTWINDOW DB ? ; L2 WINDOW FOR THIS PORT +PORTTXDELAY DW ? ; TX DELAY FOR THIS PORT +PORTPERSISTANCE DB ? ; PERSISTANCE VALUE FOR THIS PORT +FULLDUPLEX DB ? ; FULL DUPLEX IF SET +SOFTDCDFLAG DB ? ; IF SET USE 'SOFT DCD' - IF MODEM CANT GIVE A REAL ONE +PORTSLOTTIME DB ? ; SLOT TIME +PORTTAILTIME DB ? ; TAIL TIME +BBSBANNED DB ? ; SET IF PORT CAN'T ACCEPT L2 CALLS TO BBS CALLSIGN +PORTT1 DB ? ; L2 TIMEOUT +PORTT2 DB ? ; L2 DELAYED ACK TIMER +PORTN2 DB ? ; RETRIES +PORTPACLEN DB ? ; DEFAULT PACLEN FOR INCOMING SESSIONS +PORTINTERRUPT DD ? ; ADDRESS OF INTERRUPT HANDLER + +QUAL_ADJUST DB ? ; % REDUCTION IN QUALITY IF ON SAME PORT + +PERMITTEDCALLS DD ? ; POINTER TO PERMITED CALLS LIST +PORTUNPROTO DD ? ; POINTER TO UI DEST AND DIGI LIST +PORTDISABLED DB 0 ; PORT TX DISABLE FLAG +DIGIFLAG DB 0 ; ENABLE/DISABLE/UI ONLY +DIGIPORT DB 0 ; CROSSBAND DIGI PORT +DIGIMASK DW 0 ; CROSSBAND DIGI MASK +USERS DB 0 ; MAX USERS ON PORT +KISSFLAGS DB 0 ; KISS SPECIAL MODE BITS +PORTINTERLOCK DB 0 ; TO DEFINE PORTS WHICH CANT TX AT SAME TIME +NODESPACLEN DB 0 ; MAX LENGTH OF 'NODES' MSG +TXPORT DB 0 ; PORT FOR SHARED TX OPERATION +PORTMHEARD DD 0 ; POINTER TO MH DATA + +PARAMTIMER DW 0 ; MOVED FROM HW DATA FOR SYSOPH +PORTMAXDIGIS DB 0 ; DIGIS ALLOWED ON THIS PORT +PORTALIAS2 DB 7 DUP (0) ; 2ND ALIAS FOR DIGIPEATING FOR APRS +PORTBCALL DB 7 DUP (0) ; Source for Beacons +PortNoKeepAlive DB 0; ; Default to No Keepalives +PortUIOnly DB 0; +UICAPABLE DB 0; ; Pactor-style port that can do UI +WL2K DB 189 DUP (0) ; WL2K Report Data +PORTIPADDR DD 0; +SerialPortName DD 0 ; Serial Port Name for Unix +XDIGIS DD 0; ; Cross Port Digi Definitions + +NormalizeQuality DD 0 ; Normalise Node Qualities +IgnoreUnlocked DD 0 ; Ignore Unlocked Routes + +HARDWAREDATA DB 200 DUP (?) ; WORK AREA FOR HARDWARE DRIVERS + +PORTCONTROL ENDS + +; +; DEFINE MAPPING FOR EXTERNAL DRIVER +; +EXTDATA STRUC + DB HARDWAREDATA DUP (0) ; REMAP HARDWARE INFO + +PORT_EXT_ADDR DD ? ; ADDR OF RESIDENT ROUTINE +PORT_DLL_NAME DB 16 DUP (0); +EXTRESTART DB ? ; FLAG FOR DRIVER REINIT +DLLHANDLE DD ? +MAXHOSTMODESESSIONS DD ? ; Max Host Sessions supported (Used for KAM Pactor + ax.25 support) +ATTACHEDSESSIONS DD 27 DUP (0); Owning Sessions for PACTOR, etc +PERMITGATEWAY DD 0 ; Set if ax.25 ports can change callsign (ie SCS, not KAM +SCANCAPABILITIES DD 0 ; Type of scan control Controller supports (None, Simple, Connect Lock) +UI_Q DD 0 ; Unproto Frames for Session Mode Drivers (TRK, etc) + +EXTDATA ENDS + + IF TYPE EXTDATA GT TYPE PORTCONTROL + .ERR2 TOO MUCH PORT DATA + ENDIF + +EXTERNAL EQU 16 +L2 EQU 8 +; +; CW STATE MACHINE EQUATES +; +dot equ 1b +dash equ 10b +dotspace equ 100b +letterspace equ 1000b +IDPENDING EQU 10000B +; +; LEVEL 2 LINK CONTROL TABLE +; +LINKTABLE STRUC + +LINKCALL DB 7 DUP (?) ; CALLSIGN OF STATION +OURCALL DB 7 DUP (?) ; CALLSIGN OF OUR END +DIGIS DB MAXDIGIS*7 DUP (?) ; LEVEL 2 DIGIS IN PATH + +LINKPORT DD ? ; PORT POINTER +LINKTYPE DB ? ; 1 = UP, 2= DOWN, 3 = INTERNODE + +LINKNR DB ? +LINKNS DB ? ; LEV 2 SEQUENCE COUNTS +LINKWS DB ? ; WINDOW START +LINKOWS DB ? ; OLD (LAST ACKED) WINDOW START +LINKWINDOW DB ? ; LEVEL 2 WINDOW SIZE + +L2FLAGS DB ? ; CONTROL BITS +VER1FLAG DB ? ; SET IF OTHER END RUNNING VERSION 1 + +RX_Q DD ? ; PACKETS RECEIVED ON THIS LINK + +TX_Q DD ? ; PACKETS TO SEND +FRAMES DD 8 DUP (?) ; FRAMES WAITING ACK +RXFRAMES DD 8 DUP (?) ; Frames received out of sequence + +L2STATE DB ? ; PROCESSING STATE +L2TIMER DW ? ; FRAME RETRY TIMER +L2TIME DB ? ; RETRY TIMER INITIAL VALUE +L2SLOTIM DW ? ; DELAY FOR LINK VALIDATION POLL +L2ACKREQ DB ? ; DELAYED TEXT ACK TIMER +REJTIMER DB ? ; TO TIME OUT REJ IN VERSION 1 +LAST_F_TIME DW ? ; TIME LAST R(F) SENT +SDREJF DB ? ; FLAGS FOR FRMR +SDRBYTE DB ? ; SAVED CONTROL BYTE FOR FRMR + +SDTSLOT DB ? ; POINTER TO NEXT TXSLOT TO USE + +L2RETRIES DB ? ; RETRY COUNTER + +SESSACTIVE DB ? ; SET WHEN WE ARE SURE SESSION IS UP + +KILLTIMER DW ? ; TIME TO KILL IDLE LINK + +CIRCUITPOINTER DD ? ; POINTER TO L4 CIRCUIT TABLE ENTRY + ; (IF UP/DOWN) +NEIGHBOUR DD ? ; POINTER TO NEIGHBOUR (IF CROSSLINK) + +L2FRAG_Q DD ? ; DEFRAGMENTATION QUEUE + +LINKTABLE ENDS +; +; L2FLAGS EQUATES +; +REJSENT EQU 1B ; SET WHEN FIRST REJ IS SENT IN REPLY + ; TO AN I(P) +RNRSET EQU 10B ; RNR RECEIVED FROM OTHER END +;DISCPENDING EQU 1000B ; SEND DISC WHEN ALL DATA ACK'ED +RNRSENT EQU 10000B ; WE HAVE SEND RNR +POLLSENT EQU 100000B ; POLL BIT OUTSTANDING +; +; FRMR REJECT FLAGS +; +SDINVC EQU 1B ; INVALID COMMAND +SDNRER EQU 1000B ; INVALID N(R) + +TRANSPORTENTRY STRUC + +L4USER DB 7 DUP (?) ; CALL OF ORIGINATING USER +L4TARGET DD ? ; POINTER TO TARGET LINK/DEST +L4MYCALL DB 7 DUP (0) ; CALL WE ARE USING + +CIRCUITINDEX DB ? ; OUR CIRCUIT INFO +CIRCUITID DB ? + +FARINDEX DB ? +FARID DB ? ; OTHER END'S INFO + +L4WINDOW DB ? ; WINDOW SIZE +L4WS DB ? ; WINDOW START - NEXT FRAME TO ACK +TXSEQNO DB ? +RXSEQNO DB ? ; TRANSPORT LEVEL SEQUENCE INFO +L4LASTACKED DB ? ; LAST SEQUENCE ACKED + +FLAGS DB ? ; TRANSPORT LEVEL FLAGS +NAKBITS DB ? ; NAK & CHOKE BITS TO BE SENT +L4CROSSLINK DD ? ; POINTER TO LINKED L4 SESSION ENTRY +L4CIRCUITTYPE DB ? ; BIT SIGNIFICANT - SEE BELOW +KAMSESSION DB ? ; Session Number on KAM Host Mode TNC +L4TX_Q DD ? +L4RX_Q DD ? +L4HOLD_Q DD ? ; FRAMES WAITING TO BE ACKED +L4RESEQ_Q DD ? ; FRAMES RECEIVED OUT OF SEQUENCE + +L4STATE DB ? +L4TIMER DW ? +L4ACKREQ DB ? ; DATA ACK NEEDED +L4RETRIES DB ? ; RETRY COUNTER +L4KILLTIMER DW 0 ; IDLE CIRCUIT TIMER +SESSIONT1 DW 0 ; TIMEOUT FOR SESSIONS FROM HERE +SESSPACLEN DB 0 ; PACLEN FOR THIS SESSION +BADCOMMANDS DB 0 ; SUCCESSIVE BAD COMMANDS +STAYFLAG DB 0 ; STAY CONNECTED FLAG +SPYFLAG DB 0 ; SPY - CONNECT TO NODE VIA BBS CALLSIGN + +RTT_SEQ DB 0 ; SEQUENCE NUMBER BEING TIMED +RTT_TIMER DD 0 ; TIME ABOVE SEQUENCE WAS SENT + +PASSWORD DW 0 ; AUTHORISATION CODE FOR REMOTE SYSOP +; +SESS_APPLFLAGS DB 0 ; APPL FLAGS FOR THIS SESSION + +Authorised_Session DB 0; // Set if Host session from BPQTerminal or BPQMailChat + +DUMPPTR DD 0 ; POINTER FOR REMOTE DUMP MODE +PARTCMDBUFFER DD 0 ; Save area for incomplete commmand + +Frequency DD 0 ; If known - for CMS Reporting Hz +RMSCall DB 10 DUP (0); +Mode DB 0 ; ditto + +UNPROTO DD 0 ; Unproto Mode flag - port number if in unproto mode +UAddrLen DD 0 ; +UADDRESS DB 64 DUP (0); Unproto Address String - Dest + Digis + +LISTEN DD 0 ; Listen Mode flag - port number of Listen Mode + +APPL DB 16 DUP (0); Set if session initiated by an APPL +L4LIMIT dd 0 ; Idle Timout for this session + +TRANSPORTENTRY ENDS +; +; CIRCUITTYPE EQUATES +; +L2LINK EQU 1 +SESSION EQU 10B +UPLINK EQU 100B +DOWNLINK EQU 1000B +BPQHOST EQU 100000B +PACTOR EQU 1000000B +; +; FLAGS EQUATES +; +DISCPENDING EQU 1000B ; SEND DISC WHEN ALL DATA ACK'ED +; +; TOP 4 BITS MATCH L4FLAGS BITS - ALSO USED FOR NAKBITS +; +;L4BUSY EQU 80H ; BNA - DONT SEND ANY MORE +;L4NAK EQU 40H ; NEGATIVE RESPONSE FLAG +;L4MORE EQU 20H ; MORE DATA FOLLOWS - FRAGMENTATION FLAG + + + +DEST_LIST STRUC + +DEST_CHAIN DD ? ; SORTED LIST CHAIN + +DEST_CALL DB 7 DUP (?) ; DESTINATION CALLSIGN (AX25 FORMAT) +DEST_ALIAS DB 6 DUP (?) + +DEST_STATE DB ? ; CONTROL BITS - SETTING UP, ACTIVE ETC + +DEST_ROUTE DB ? ; CURRENTY ACTIVE DESTINATION + +INP3FLAGS DB ? + +ROUT1_NEIGHBOUR DD ? ; POINTER TO NEXT NODE IN PATH +ROUT1_QUALITY DB ? ; QUALITY +ROUT1_OBSCOUNT DB ? + db 5 dup (?); Padding + +ROUT2_NEIGHBOUR DD ? +ROUT2_QUALITY DB ? +ROUT2_OBSCOUNT DB ? + db 5 dup (?); Padding + +ROUT3_NEIGHBOUR DD ? +ROUT3_QUALITY DB ? +ROUT3_OBSCOUNT DB ? + db 5 dup (?); Padding + +INPROUT1_NEIGHBOUR DD ? +LastRTT1 DW ?; // Last Value Reported +RTT1 DW ?; // Current +SRTT1 DW ?; // Smoothed RTT +Hops1 DB ?; + +INPROUT2_NEIGHBOUR DD ? +LastRTT2 DW ?; // Last Value Reported +RTT2 DW ?; // Current +SRTT2 DW ?; // Smoothed RTT +Hops2 DB ?; + +INPROUT3_NEIGHBOUR DD ? +LastRTT3 DW ?; // Last Value Reported +RTT3 DW ?; // Current +SRTT3 DW ?; // Smoothed RTT +Hops3 DB ?; +DEST_Q DD ? ; QUEUE OF FRAMES FOR THIS DESTINATION + +DEST_RTT DD ? ; SMOOTHED ROUND TRIP TIMER +DEST_COUNT DD ? ; FRAMES SENT + +DEST_LIST ENDS + + +; +; MODEFLAG DEFINITIONS +; +COMMAND EQU 1B +TRANS EQU 10B +CONV EQU 100B +; +; APPL DEFINITIONS +; +BBSAPPL EQU 1B +HOSTAPPL EQU 10B +SYSOPAPPL EQU 100B +; +; HOSTFLAG DEFINITIONS +; +;HOSTMODE EQU 1B ; PK232 HOSTMODE ENABLED +;HOSTESCBIT EQU 10B ; IN ESCAPE SEQUENCE +;UFQ EQU 100B ; UFQ MODE (IE NOT AA4RE) +;POLLED EQU 1000B ; POLL RECEIVED + +;KISSMODE EQU 10000B ; KISS MODE +;KISSESC EQU 100000B ; IN ESCAPE SEQUENCE +; +; APPLFLAGS BITS +; +CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION +MSG_TO_USER EQU 10B ; SEND 'CONNECTED' TO USER +MSG_TO_APPL EQU 100B ; SEND 'CONECTED' TO APPL +; +; HDLC COMMANDS (WITHOUT P/F) +; +UI EQU 3 +SABM EQU 2FH +DISC EQU 43H +DM EQU 0FH +UA EQU 63H +FRMR EQU 87H +RR EQU 1 +RNR EQU 5 +REJ EQU 9 +; +PFBIT EQU 10H ; POLL/FINAL BIT IN CONTROL BYTE +; +; MH DATA AREA +; +;MHSTRUC STRUC + +;MHCALL DB 7 DUP (0) +;MHTIME DD 0 ; ? FROM BIOS +;MHDIGI DB 0 +;MHFreq DB 12 DUP (0) +;MHLocator DB 6 DUP (0) + +;MHSTRUC ENDS + +; Application Calls/Alias Supports multiple L4 application calls + +APPLCALLS STRUC + +APPLCALL DB 7 DUP (0) ; ax.25 +APPLALIAS_TEXT DB 10 DUP (0) ; TEXT, WITH APPENDED SPACE + +APPLCALL_TEXT DB 10 DUP (0) +APPLALIAS DB 6 DUP (0) +APFiller DB 0 +APPLQUAL DW 0 +APPNODEPOINTER DD 0 ; Pointer to "NODES" entry for this App (if L4) +APPLCMDNAME DB 13 DUP (0) ; Command to invoke this APPL +APPLHASALIAS DD 0 +APPLPORT DD 0 ; Port if Appl has an alias +APPLALIASPTR DD 0 ; Pointer to Alias if defined + +APPLCALLS ENDS + +; New Style APPL configuration + +APPLCONFIG STRUC + +ACCommand DB 12 DUP (0); +ACCommandAlias DB 48 DUP (0); +ACApplCall DB 10 DUP (0); +ACApplAlias DB 10 DUP (0); +ACApplQual DD 0 + +APPLCONFIG ENDS +; +; HARDWARE TYPE EQUATES +; +KISS EQU 0 +PC120 EQU 2 +DRSI EQU 4 +TOSH EQU 6 +QUADRAM EQU 8 +RLC100 EQU 0AH +RLC400 EQU 0CH +INTERNAL EQU 0EH +EXTERNAL EQU 10H +BAYCOM EQU 12H +PA0HZP EQU 14H + + diff --git a/TNCCode.c b/TNCCode.c new file mode 100644 index 0000000..96b6851 --- /dev/null +++ b/TNCCode.c @@ -0,0 +1,362 @@ +/* +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 +*/ + +// +// 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 SENDIDMSG() +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * ID = IDMSG; + struct _MESSAGE * Buffer; + + while (PORT) + { + if (PORT->PROTOCOL < 10) // Not Pactor-style + { + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer, ID, ID->LENGTH); + + Buffer->PORT = PORT->PORTNUMBER; + + // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD + + if (PORT->PORTCALL[0] > 0x40) + { + memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); + Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT + } + C_Q_ADD(&IDMSG_Q, Buffer); + } + } + PORT = PORT->PORTPOINTER; + } +} + + + +VOID SENDBTMSG() +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * Buffer; + char * ptr1, * ptr2; + + while (PORT) + { + if (PORT->PROTOCOL >= 10 || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? + { + PORT = PORT->PORTPOINTER; + continue; + } + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); + Buffer->DEST[6] |= 0xC0; // Set COmmand bits + + // Send from BBSCALL unless PORTBCALL defined + + if (PORT->PORTBCALL[0] > 32) + memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); + else if (APPLCALLTABLE->APPLCALL[0] > 32) + memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); + else + memcpy(Buffer->ORIGIN, MYCALL, 7); + + ptr1 = &PORT->PORTUNPROTO[7]; // First Digi + ptr2 = &Buffer->CTL; // Digi field in buffer + + // Copy any digis + + while (*(ptr1)) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // Set End of Address + *(ptr2++) = UI; + + memcpy(ptr2, &BTHDDR.PID, BTHDDR.LENGTH); + ptr2 += BTHDDR.LENGTH; + Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); + Buffer->PORT = PORT->PORTNUMBER; + + C_Q_ADD(&IDMSG_Q, Buffer); + } + PORT = PORT->PORTPOINTER; + } +} + +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg) +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * Buffer; + char * ptr1, * ptr2; + + Msg->LENGTH -= MSGHDDRLEN; // Remove Header + + while (PORT) + { + if ((PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? + { + PORT = PORT->PORTPOINTER; + continue; + } + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); + Buffer->DEST[6] |= 0xC0; // Set Command bits + + // Send from BBSCALL unless PORTBCALL defined + + if (PORT->PORTBCALL[0] > 32) + memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); + else if (APPLCALLTABLE->APPLCALL[0] > 32) + memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); + else + memcpy(Buffer->ORIGIN, MYCALL, 7); + + ptr1 = &PORT->PORTUNPROTO[7]; // First Digi + ptr2 = &Buffer->CTL; // Digi field in buffer + + // Copy any digis + + while (*(ptr1)) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // Set End of Address + *(ptr2++) = UI; + + memcpy(ptr2, &Msg->PID, Msg->LENGTH); + ptr2 += Msg->LENGTH; + Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT->PROTOCOL == 10) + { + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + C_Q_ADD(&EXTPORT->UI_Q, Buffer); + } + else + C_Q_ADD(&IDMSG_Q, Buffer); + } + PORT = PORT->PORTPOINTER; + } +} + +Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port) +{ + // Block included the 7/11 byte header, Len does not + + struct PORTCONTROL * PORT; + PMESSAGE Copy; + + if (Len > 360 - 15) + return; + + if (QCOUNT < 50) + return; // Running low + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == 0) + return; + + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(&Copy->DEST[0], &Block[MSGHDDRLEN], Len); + + Copy->LENGTH = (USHORT)Len + MSGHDDRLEN; + + if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF) + { + // Pactor Style. Probably will only be used for Tracker uneless we do APRS over V4 or WINMOR + + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + C_Q_ADD(&EXTPORT->UI_Q, Copy); + return; + } + + Copy->PORT = Port; + + PUT_ON_PORT_Q(PORT, Copy); +} + + +TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask) +{ + // Create a Transport (L4) session linked to an incoming HOST (API) Session + + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + UCHAR * ourcall = &MYCALL[0]; + + // IF APPL PORT USE APPL CALL, ELSE NODE CALL + + if (ApplMask) + { + // Circuit for APPL - look for an APPLCALL + + APPLCALLS * APPL = APPLCALLTABLE; + + while ((ApplMask & 1) == 0) + { + ApplMask >>= 1; + APPL++; + } + if (APPL->APPLCALL[0] > 0x40) // We have an applcall + ourcall = &APPL->APPLCALL[0]; + } + + memcpy(NewSess->L4USER, ourcall, 7); + memcpy(NewSess->L4MYCALL, ourcall, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // Keep Non-Zero + + NewSess->L4TARGET.HOST = HOST; + NewSess->L4STATE = 5; + + + NewSess->SESSIONT1 = L4T1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + NewSess->SESSPACLEN = PACLEN; // Default; + + return NewSess; + } + Index++; + NewSess++; + } + + // Table Full + + return NULL; +} + + + + diff --git a/TNCEmulators.c b/TNCEmulators.c new file mode 100644 index 0000000..a6e0f04 --- /dev/null +++ b/TNCEmulators.c @@ -0,0 +1,6261 @@ +/* +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 +*/ + + +// TNC Emulator Module for BPQ32 switch + +// Supports TNC2 and WA8DED Hostmode interfaces + + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +#define LF 10 +#define CR 13 + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int APIENTRY SendMsg(int stream, char * msg, int len); + +VOID SENDPACKET(struct TNCDATA * TNC); +VOID CHECKCTS(struct TNCDATA * TNC); +VOID SETCOMMANDMODE(struct TNCDATA * TNC); +VOID SETCOMM00(struct TNCDATA * TNC); +VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len); +VOID TNCCOMMAND(struct TNCDATA * TNC); +VOID TNC2PutChar(struct TNCDATA * TNC, int Char); +VOID KBECHO(struct TNCDATA * TNC, int Char); +VOID KBNORM(struct TNCDATA * TNC, int Char); +VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char); +VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome); +VOID CONNECTTONODE(struct TNCDATA * TNC); +DllImport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); +DllImport int APIENTRY GetCallsign(int stream, char * callsign); +VOID GETDATA(struct TNCDATA * TNC); +VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream); +VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream); +VOID READCHANGE(int Stream); +VOID DOMONITORING(int NeedTrace); +int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp); +time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +BOOL TfPut(struct TNCDATA * TNC, UCHAR character); +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, unsigned long long Mask, BOOL APRS, BOOL MCTL); +int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); +int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); +int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC); +VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); +VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len);; +int KANTConnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); +int KANTDisconnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); +VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); +VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length); +VOID TNCPoll(); +VOID DisableAppl(struct TNCDATA * TNC); +int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay); +int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen); +int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen); + +extern struct TNCDATA * TNCCONFIGTABLE; + +struct TNCDATA * TNC2TABLE; // malloc'ed +extern int NUMBEROFTNCPORTS; + +// MODEFLAG DEFINITIONS + +#define COMMAND 1 +#define TRANS 2 +#define CONV 4 + +// APPLFLAGS BITS + +//CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION +//MSG_TO_USER EQU 10B ; SEND "CONNECTED" TO USER +//MSG_TO_APPL EQU 100B ; SEND "CONECTED" TO APPL + +extern char pgm[256]; + +int CloseDelay = 10; // Close after connect fail delay + +MESSAGE MONITORDATA; // RAW FRAME FROM NODE + +char NEWCALL[11]; + +//TABLELEN DW TYPE TNCDATA + +char LNKSTATEMSG[] = "Link state is: "; +char CONNECTEDMSG[] = "CONNECTED to "; +char WHATMSG[] = "Eh?\rcmd:"; +char CMDMSG[] = "cmd:"; + + +char DISCONNMSG[] = "\r*** DISCONNECTED\r"; + +char CONMSG1[] = "\r*** CONNECTED to "; +char CONCALL[10]; + + +char SIGNON[] = "\r\rG8BPQ TNC2 EMULATOR\r\r"; + +char CONMSG[] ="\r*** CONNECTED to "; +char SWITCH[] = "SWITCH\r"; +char SWITCHSP[] = "SWITCH "; + +char WAS[] = " was "; +char VIA[] = " via "; +char OFF[] = "OFF\r"; +char ON[] = "ON \r"; + +// BPQ Serial Device Support + +// On W2K and above, BPQVIrtualCOM.sys provides a pair of cross-connected devices, and a control channel +// to enumerate, add and delete devices. + +// On Win98 BPQVCOMM.VXD provides a single IOCTL interface, over which calls for each COM device are multiplexed + +#ifdef WIN32 + +#define IOCTL_SERIAL_SET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 1,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_QUEUE_SIZE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 2,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT, 3,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_ON CTL_CODE(FILE_DEVICE_SERIAL_PORT, 4,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_OFF CTL_CODE(FILE_DEVICE_SERIAL_PORT, 5,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_IMMEDIATE_CHAR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 6,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 7,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 8,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 9,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT,10,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_RESET_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,11,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,12,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,13,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XOFF CTL_CODE(FILE_DEVICE_SERIAL_PORT,14,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XON CTL_CODE(FILE_DEVICE_SERIAL_PORT,15,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,16,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,17,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_WAIT_ON_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,18,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_PURGE CTL_CODE(FILE_DEVICE_SERIAL_PORT,19,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT,20,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,21,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,22,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,23,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,24,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,25,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_MODEMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,26,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_COMMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,27,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_XOFF_COUNTER CTL_CODE(FILE_DEVICE_SERIAL_PORT,28,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_PROPERTIES CTL_CODE(FILE_DEVICE_SERIAL_PORT,29,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_DTRRTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,30,METHOD_BUFFERED,FILE_ANY_ACCESS) + + +#define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_LIST_DEVICES CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80b,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_SET_POLLDELAY CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80c,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_SET_DEBUGMASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80d,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define W98_SERIAL_IS_COM_OPEN 0x800 +#define W98_SERIAL_GETDATA 0x801 +#define W98_SERIAL_SETDATA 0x802 + +#define W98_SERIAL_SET_CTS 0x803 +#define W98_SERIAL_SET_DSR 0x804 +#define W98_SERIAL_SET_DCD 0x805 + +#define W98_SERIAL_CLR_CTS 0x806 +#define W98_SERIAL_CLR_DSR 0x807 +#define W98_SERIAL_CLR_DCD 0x808 + +#define W98_BPQ_ADD_DEVICE 0x809 +#define W98_BPQ_DELETE_DEVICE 0x80a +#define W98_BPQ_LIST_DEVICES 0x80b + +#define W98_BPQ_SET_POLLDELAY 0x80c +#define W98_BPQ_SET_DEBUGMASK 0x80d + +#define W98_SERIAL_GET_COMMSTATUS 27 +#define W98_SERIAL_GET_DTRRTS 30 + +#define DebugModemStatus 1 +#define DebugCOMStatus 2 +#define DebugWaitCompletion 4 +#define DebugReadCompletion 8 + + +HANDLE hControl; + +BOOL Win98; + +typedef struct _SERIAL_STATUS { + ULONG Errors; + ULONG HoldReasons; + ULONG AmountInInQueue; + ULONG AmountInOutQueue; + BOOL EofReceived; + BOOL WaitForImmediate; +} SERIAL_STATUS,*PSERIAL_STATUS; + +#endif + +#ifndef WIN32 +// #include + +extern int posix_openpt (int __oflag); +extern int grantpt (int __fd); +extern int unlockpt (int __fd); +extern char *ptsname (int __fd); +extern int ptsname_r (int __fd, char *__buf, size_t __buflen); +extern int getpt (void); + +HANDLE LinuxOpenPTY(char * Name) +{ + // Open a Virtual COM Port + + HANDLE hDevice, slave;; + char slavedevice[80]; + int ret; + u_long param=1; + struct termios term; + +#ifdef MACBPQ + + // Create a pty pair + + openpty(&hDevice, &slave, &slavedevice[0], NULL, NULL); + close(slave); + +#else + + hDevice = posix_openpt(O_RDWR|O_NOCTTY); + + if (hDevice == -1) + { + perror("posix_openpt Create PTY pair failed"); + return -1; + } + if (grantpt (hDevice) == -1) + { + perror("grantpt Create PTY pair failed"); + return -1; + } + if (unlockpt (hDevice) == -1) + { + perror("unlockpt Create PTY pair failed"); + return -1; + } + if (ptsname_r(hDevice, slavedevice, 80) != 0) + { + perror("ptsname_r Create PTY pair failed"); + return -1; + } + +#endif + + printf("slave device: %s. ", slavedevice); + + if (tcgetattr(hDevice, &term) == -1) + { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfmakeraw(&term); + + if (tcsetattr(hDevice, TCSANOW, &term) == -1) + { + perror("tcsetattr"); + return -1; + } + + ioctl(hDevice, FIONBIO, ¶m); + + chmod(slavedevice, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP|S_IROTH|S_IWOTH); + + unlink (Name); + + ret = symlink (slavedevice, Name); + + if (ret == 0) + printf ("symlink to %s created\n", Name); + else + printf ("symlink to %s failed\n", Name); + + return hDevice; +} +#else + +HANDLE BPQOpenSerialPort(struct TNCDATA * TNC, DWORD * lasterror) +{ + // Open a Virtual COM Port + + int port = TNC->ComPort; + char szPort[80]; + HANDLE hDevice; + int Err; + + *lasterror=0; + + if (Win98) + { + sprintf( szPort, "\\\\.\\COM%d",port) ; + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hDevice == (HANDLE) -1 ) + { + // If In Use(5) ok, else fail + + if (GetLastError() == 5) + return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device + + return (HANDLE)(ptrdiff_t) - 1; + } + + CloseHandle(hDevice); + + return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device + } + + // Try New Style VCOM firsrt + + sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", port ) ; + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + Err = GetLastError(); + + if (hDevice != (HANDLE) -1) + { + TNC->NewVCOM = TRUE; + TNC->PortEnabled = TRUE; + Err = GetFileType(hDevice); + } + else + { + + // Try old style + + sprintf(szPort, "\\\\.\\BPQ%d", port ) ; + + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (TNC->PollDelay) + BPQSerialSetPollDelay(hDevice, TNC->PollDelay); + + } + if (hDevice == (HANDLE) -1 ) + { + *lasterror=GetLastError(); + } + + return hDevice; +} +#endif + +int BPQSerialSetCTS(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl,(DWORD)hDevice | W98_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + +#endif +} + +int BPQSerialSetDSR(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DSR,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DSR, NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialSetDCD(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialClrCTS(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} +int BPQSerialClrDSR(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialClrDCD(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DCD,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DCD, NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +static int SendDataToTNC(struct TNCDATA * TNC, UCHAR * TXMsg, int n) +{ + // Used for all port types + +#ifdef WIN32 + + // WIN32 VCOM Used one of my Drivers + + if (TNC->VCOM) + BPQSerialSendData(TNC, TXMsg, n); + +#endif + + // Linux VCOM uses SOCAT Pairs and normal write + + return WriteCOMBlock(TNC->hDevice, TXMsg, n); +} + +int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen) +{ + HANDLE hDevice = TNC->hDevice; + ULONG bytesReturned; + + // Host Mode code calls BPQSerialSendData for all ports, so it a real port, pass on to real send routine + + if (!TNC->VCOM) + return WriteCOMBlock(TNC->hDevice, Message, MsgLen); + +#ifndef WIN32 + + // Linux usies normal IO for all ports + return WriteCOMBlock(TNC->hDevice, Message, MsgLen); + +#else + + if (MsgLen > 4096 ) return ERROR_INVALID_PARAMETER; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + else + { + if (TNC->NewVCOM) + { + // Have to escape all oxff chars, as these are used to get status info + + UCHAR NewMessage[1000]; + UCHAR * ptr1 = Message; + UCHAR * ptr2 = NewMessage; + UCHAR c; + + int Length = MsgLen; + + while (Length != 0) + { + c = *(ptr1++); + *(ptr2++) = c; + + if (c == 0xff) + { + *(ptr2++) = c; + MsgLen++; + } + Length--; + } + + return WriteFile(hDevice, NewMessage, MsgLen, &bytesReturned, NULL); + } + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + } +#endif +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int GetDataFromTNC(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) +{ + // Used for all port types + +#ifdef WIN32 + + // WIN32 VCOM Used one of my Drivers + + if (TNC->VCOM) + return BPQSerialGetData(TNC, Message, BufLen, MsgLen); + + *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); + return 0; + +#else + + int Error = 0; + + if (TNC->VCOM == 0) + { + *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); + return 0; + } + + // Linux VCOM uses SOCAT Pairs and normal Read + + // If the slave closes connection read returns 5. Need to trap and + // close/reopen. So use ReadCOMBlockEx + + *MsgLen = ReadCOMBlockEx(TNC->hDevice, Message, BufLen, &Error); + + if (Error == 5) + { + printf("Read error on TNCPORT %s - Restarting\n", TNC->PORTNAME); + close(TNC->hDevice); + TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); + } + return 0; + +#endif +} + + +int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) +{ +#ifdef WIN32 + DWORD dwLength = 0; + DWORD Available = 0; + HANDLE hDevice = TNC->hDevice; + int Length, RealLen = 0; + + if (BufLen > 4096 ) return ERROR_INVALID_PARAMETER; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); + + if (TNC->NewVCOM) + { + int ret = PeekNamedPipe(hDevice, NULL, 0, NULL, &Available, NULL); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret == ERROR_BROKEN_PIPE) + { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + return 0; + } + } + + if (Available > BufLen) + Available = BufLen; + + if (Available) + { + UCHAR * ptr1 = Message; + UCHAR * ptr2 = Message; + UCHAR c; + + ReadFile(hDevice, Message, Available, &Length, NULL); + + // Have to look for FF escape chars + + RealLen = Length; + + while (Length != 0) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) + { + c = c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + RealLen--; + } + else + { + // This is connection statua from other end + + RealLen -= 2; +// TNC->PortEnabled = c; + continue; + } + } + *(ptr2++) = c; + } + } + *MsgLen = RealLen; + return 0; + } + + return DeviceIoControl(hDevice,IOCTL_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); +} +#else + return 0; +} +#endif + +int BPQSerialGetQCounts(HANDLE hDevice,ULONG * RXCount, ULONG * TXCount) +{ +#ifndef WIN32 + return 0; +#else + + SERIAL_STATUS Resp; + int MsgLen; + int ret; + + if (Win98) + ret = DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); + else + ret = DeviceIoControl(hDevice,IOCTL_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); + + *RXCount=Resp.AmountInInQueue; + *TXCount=Resp.AmountInOutQueue; + + return ret; +#endif +} + +int BPQSerialGetDeviceList(HANDLE hDevice,ULONG * Slot,ULONG * Port) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + return DeviceIoControl (hDevice,IOCTL_BPQ_LIST_DEVICES,Slot,4,Port,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialIsCOMOpen(HANDLE hDevice,ULONG * Count) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialGetDTRRTS(HANDLE hDevice, ULONG * Flags) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); + +#endif +} + +int BPQSerialSetDebugMask(HANDLE hDevice, int DebugMask) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + return DeviceIoControl(hDevice, IOCTL_BPQ_SET_DEBUGMASK, &DebugMask, 4, NULL, 0, &bytesReturned,NULL); +#endif +} + +void CheckForStreamChange(struct TNCDATA * TNC, int ToStream) +{ + // Send Stream Switched Message if changed + + char Msg[80]; + int Len; + + if (ToStream == TNC->RXStream) + return; + + TNC->RXStream = ToStream; + + // Send Message + + // |B:WA7GXD: + + if (TNC->StreamCall) + Len = sprintf(Msg, "%c%c:%s:", TNC->StreamSW, ToStream + 'A', TNC->TNC2Stream[ToStream]->RemoteCall); + else + Len = sprintf(Msg, "%c%c", TNC->StreamSW, ToStream + 'A'); + + SENDREPLY(TNC, Msg, Len); +} + +int LocalSessionState(int stream, int * state, int * change, BOOL ACK) +{ + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + // Local version without semaphore or checktimer + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + // HOSTFLAGS = Bit 80 = Allocated + // Bit 40 = Disc Request + // Bit 20 = Stay Flag + // Bit 02 and 01 State Change Bits + + if ((HOST->HOSTFLAGS & 3) == 0) + // No Chaange + *change = 0; + else + *change = 1; + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + if (ACK) + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits + + return 0; +} + + + + +VOID ONOFF(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // PROCESS COMMANDS WITH ON/OFF PARAM + + char Param; + UCHAR * valueptr; + UCHAR oldvalue, newvalue = 0xff; + + char Response[80]; + int len; + + _strupr(Tail); + Param = *Tail; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + oldvalue = (UCHAR)*valueptr; + + switch(Param) + { + case ' ': + break; + case 'Y': + newvalue = 1; + break; + case 'N': + newvalue = 0; + break; + case 'O': + if (Tail[1] == 'N') + newvalue = 1; + else + newvalue = 0; + break; + } + + if (newvalue == 255) + { + len = sprintf(Response, "%s %s\r", CMD->String, (oldvalue)?"ON":"OFF"); + } + else + { + len = sprintf(Response, "%s was %s\r", CMD->String, (oldvalue)?"ON":"OFF"); + *valueptr = newvalue; + } + SENDREPLY(TNC, Response, len); +} + + + +VOID ONOFF_CONOK(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + ONOFF(TNC, Tail, CMD); + + // UPDATE APPL FLAGS ON NODE PORT + + if (TNC->CONOK) + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); +} + +VOID SETMYCALL(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + char Response[80]; + int len; + char Call[10] = " "; + + _strupr(Tail); + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "MYCALL %s\r", TNC->MYCALL); + } + else + { + strlop(Tail,' ');; + memcpy(Call, Tail, (int)strlen(Tail) + 1); + len = sprintf(Response, "MYCALL was %s\r", TNC->MYCALL); + memcpy(TNC->MYCALL, Call, 10); + } + + SENDREPLY(TNC, Response, len); +} +VOID CTEXTCMD(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + char Response[256]; + int len, n; + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "CTEXT %s\r", TNC->CTEXT); + } + else + { + Tail[TNC->MSGLEN] = 0; + n = strlen(Tail) - 1; + while(n > 0 && Tail[n] == ' ') + Tail[n--] = 0; + + if (strlen(Tail) > 119) + Tail[119] = 0; + + len = sprintf(Response, "CTEXT was %s\r", TNC->CTEXT); + strcpy(TNC->CTEXT, Tail); + } + + SENDREPLY(TNC, Response, len); +} + +VOID BTEXT(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ +} +VOID VALUE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // PROCESS COMMANDS WITH decimal value + + char Param = *Tail; + UCHAR * valueptr; + int oldvalue, newvalue; + + char Response[80]; + int len; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + oldvalue = *valueptr; + + strlop(Tail, ' '); + + if (Tail[0]) + { + newvalue = atoi(Tail); + len = sprintf(Response, "%s was %d\r", CMD->String, oldvalue); + *valueptr = newvalue; + } + else + { + len = sprintf(Response, "%s %d\r", CMD->String, oldvalue); + } + SENDREPLY(TNC, Response, len); +} + +VOID VALHEX(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // PROCESS COMMANDS WITH decimal value + + char Param = *Tail; + + UCHAR * valueptr; + UINT * intvalueptr; + UINT oldvalue, newvalue; + + char Response[80]; + int len; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + intvalueptr = (UINT *)valueptr; + + oldvalue = *intvalueptr; + + strlop(Tail, ' '); + + if (Tail[0]) + { + if (Tail[0] == '$') + newvalue = (UINT)strtol(Tail + 1, NULL, 16); + else + newvalue = (UINT)strtol(Tail, NULL, 0); + + len = sprintf(Response, "%s was $%x\r", CMD->String, oldvalue); + *intvalueptr = newvalue; + } + else + { + len = sprintf(Response, "%s $%x\r", CMD->String, oldvalue); + } + SENDREPLY(TNC, Response, len); +} + +VOID APPL_VALHEX(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + int ApplNum = 1; + UINT APPLMASK; + + VALHEX(TNC, Tail, CMD); + + // UPDATE APPL FLAGS ON NODE PORT + + if (TNC->CONOK) + SetAppl(TNC->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNC->BPQPort, TNC->APPLFLAGS, 0); + + // Set MYCALL to APPLCALL + + APPLMASK = TNC->APPLICATION; + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + if (TNC->CONOK && TNC->APPLICATION) + memcpy(TNC->MYCALL, GetApplCall(ApplNum), 10); + +} +VOID CSWITCH(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + char Response[80]; + int len; + + len = sprintf(Response, "%s", CMDMSG); + SENDREPLY(TNC, Response, len); + + CONNECTTONODE(TNC); + +} +VOID CONMODE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + SENDREPLY(TNC, CMDMSG, 4); +} + +VOID TNCCONV(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->MODEFLAG |= CONV; + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); +} + +VOID TNCNODE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + // CONNECT TO NODE + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->VMSR |= 0x88; // SET CONNECTED + + TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); + + CONNECTTONODE(TNC); +} + +VOID CStatus(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + char Response[80]; + int i, len; + char Call[10] = ""; + char Selected[3] = " "; + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + char Selected[3] = " "; + TNCStream = TNC->TNC2Stream[i]; + + if (TNC->RXStream == i) + Selected[0] = 'I'; + + if (TNC->TXStream == i) + Selected[1] = 'O'; + + if (TNCStream->VMSR & 0x80) + { + GetCallsign(TNCStream->BPQPort, Call); + strlop(Call, ' '); + + len = sprintf(Response, "%c stream - %s CONNECTED to %s\r", i + 'A', Selected, Call); + } + else + { + len = sprintf(Response, "%c stream - %s DISCONNECTED\r", i + 'A', Selected); + } + + SENDREPLY(TNC, Response, len); + } +} + + +VOID TNCCONNECT(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + char Response[80]; + int len; + char Call[10] = ""; + + _strupr(Tail); + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "%s", LNKSTATEMSG); + + if (TNCStream->VMSR & 0x80) + { + GetCallsign(TNCStream->BPQPort, Call); + strlop(Call, ' '); + + len = sprintf(Response, "%c Link state is: CONNECTED to %s\r", TNC->TXStream + 'A', Call); + } + else + { + len = sprintf(Response, "%c Link state is: DISCONNECTED\r", TNC->TXStream + 'A'); + } + + SENDREPLY(TNC, Response, len); + return; + } + + // CONNECT, BUT NOT TO SWITCH - CONNECT TO NODE, THEN PASS TO IT FOR PROCESSING + + TNCNODE(TNC, Tail, CMD); + READCHANGE(TNCStream->BPQPort); //CLEAR STATUS CHANGE (TO AVOID SUPURIOUS "CONNECTED TO") + + strcat(TNC->TONODEBUFFER, "\r"); + TNC->MSGLEN = (int)strlen(TNC->TONODEBUFFER); + + SENDPACKET(TNC); // Will now go to node + +} +VOID TNCDISC(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + Disconnect(TNCStream->BPQPort); + + SENDREPLY(TNC, CMDMSG, 4); +} + +VOID READCHANGE(int Stream) +{ + int dummy; + LocalSessionState(Stream, &dummy, &dummy, TRUE); +} + +VOID TNCRELEASE(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + ReturntoNode(TNC->BPQPort); + + TNC->VMSR &= 0x7F; // DROP DCD + TNC->VMSR |= 8; //DELTA DCD + + SENDREPLY(TNC, CMDMSG, 4); +} +VOID TNCTRANS(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + // MAKE PRETTY SURE THIS ISNT A BIT OF STRAY DATA + + if (TNC->MSGLEN > 6) + return; + + TNCStream->MODEFLAG |= TRANS; + TNCStream->MODEFLAG &= ~(COMMAND+CONV); +} +static VOID RESTART(struct TNCDATA * TNC) +{ + // REINITIALISE CHANNEL + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + TNC->RXCOUNT = 0; + + TNC->VLSR = 0x20; + TNC->VMSR = 0x30; + + TNCStream->MODEFLAG = COMMAND; + TNC->SENDPAC = 13; + TNC->CRFLAG = 1; + TNC->MALL = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + TNC->COMCHAR = 3; + TNC->CMDTIME = 10; // SYSTEM TIMER = 100MS + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + SENDREPLY(TNC, SIGNON, 23); +} + + +static VOID UNPROTOCMD(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +{ +} + + +CMDX COMMANDLIST[] = +{ + "AUTOLF ",2, ONOFF, offsetof(struct TNCDATA, AUTOLF), + "BBSMON ",6, ONOFF, offsetof(struct TNCDATA, BBSMON), + "BTEXT ",2,BTEXT,0, + "CONOK ",4,ONOFF_CONOK,offsetof(struct TNCDATA, CONOK), + "C SWITCH",8,CSWITCH,0, + "CBELL ",2,ONOFF,offsetof(struct TNCDATA, CBELL), + "CMDTIME ",2,VALUE, offsetof(struct TNCDATA, CMDTIME), + "CMSG ",4,ONOFF,offsetof(struct TNCDATA, CMSG), + "COMMAND ",3,VALHEX, offsetof(struct TNCDATA, COMCHAR), + "CONMODE ",4,CONMODE,0, + "CPACTIME",2,ONOFF,offsetof(struct TNCDATA, CPACTIME), + "CR ",2,ONOFF,offsetof(struct TNCDATA, CRFLAG), + "CSTATUS ",2,CStatus,0, + "CTEXT ",2,CTEXTCMD,0, + "APPLFLAG",5,APPL_VALHEX, offsetof(struct TNCDATA, APPLFLAGS), + "APPL ",4,APPL_VALHEX, offsetof(struct TNCDATA, APPLICATION), + "CONVERS ",4,TNCCONV,0, + "CONNECT ",1,TNCCONNECT,0, + "DISCONNE",1,TNCDISC,0, + "ECHO ",1,ONOFF,offsetof(struct TNCDATA, ECHOFLAG), + "FLOW ",4,ONOFF,offsetof(struct TNCDATA, FLOWFLAG), + "HEADERLN",2,ONOFF,offsetof(struct TNCDATA, HEADERLN), + "K ",1,TNCNODE,0, + "MTXFORCE",4,ONOFF,offsetof(struct TNCDATA, MTXFORCE), + "LCSTREAM",8,ONOFF, offsetof(struct TNCDATA, LCStream), + "LFIGNORE",3,ONOFF,offsetof(struct TNCDATA, LFIGNORE), + "MTX ",3,ONOFF,offsetof(struct TNCDATA, MTX), + "MALL ",2,ONOFF,offsetof(struct TNCDATA, MALL), + "MCOM ",4,ONOFF,offsetof(struct TNCDATA, MCOM), + "MCON ",2,ONOFF,offsetof(struct TNCDATA, MCON), + "MMASK ",2,VALHEX, offsetof(struct TNCDATA, MMASK), + "MONITOR ",3,ONOFF,offsetof(struct TNCDATA, TRACEFLAG), + "MYCALL ",2,SETMYCALL,0, + "NEWMODE ",2,ONOFF,offsetof(struct TNCDATA, NEWMODE), + "NODE ",3,TNCNODE,0, + "NOMODE ",2,ONOFF,offsetof(struct TNCDATA, NOMODE), + "SENDPAC ",2,VALHEX, offsetof(struct TNCDATA, SENDPAC), + "STREAMCA",8,ONOFF, offsetof(struct TNCDATA, StreamCall), + "STREAMDBL",7,ONOFF, offsetof(struct TNCDATA, StreamDbl), + "STREAMSW",3,VALHEX, offsetof(struct TNCDATA, StreamSW), + "PACLEN ",1,VALUE, offsetof(struct TNCDATA, TPACLEN), + "PASS ",3,VALHEX, offsetof(struct TNCDATA, PASSCHAR), + "RELEASE ",3,TNCRELEASE,0, + "RESTART ",7,RESTART,0, + "TRANS ",1,TNCTRANS,0, + "UNPROTO ",1,UNPROTOCMD,0, + "USERS ",2,VALUE, offsetof(struct TNCDATA, Users), +}; + +static CMDX * CMD = NULL; + +int NUMBEROFTNCCOMMANDS = sizeof(COMMANDLIST)/sizeof(CMDX); + +/*NEWVALUE DW 0 +HEXFLAG DB 0 + + +NUMBER DB 4 DUP (0),CR +NUMBERH "$0000",CR + +BADMSG "?bad parameter",CR,0 + +BTHDDR ,0 ; CHAIN + DB 0 ; PORT + DW 7 ; LENGTH + DB 0F0H ; PID +BTEXTFLD DB 0DH,256 DUP (0) + +CMDENDADDR,0 ; POINTER TO END OF COMMAND + +MBOPTIONBYTE DB 0 + +NORMCALL DB 10 DUP (0) +AX25CALL DB 7 DUP (0) + +CONNECTCALL DB 10 DUP (0) ; CALLSIGN IN CONNECT MESSAGE +DIGICALL DB 10 DUP (0) ; DIGI IN CONNECT COMMAND +AX25STRING DB 64 DUP (0) ; DIGI STRING IN AX25 FORMAT +DIGIPTR ,0 ; POINTER TO CURRENT DIGI IN STRING + +NORMLEN ,0 + + EVEN +*/ + +int TRANSDELAY = 10; // 1 SEC + +//UNPROTOCALL DB "UNPROTO",80 DUP (0) + +char MONBUFFER[1000]; + +VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome) +{ + // Poll Node + + if (TNC->Mode == 0) + GETDATA(TNC); + + *returnedchar = -1; + *moretocome = 0; + + if (TNC->RXCOUNT == 0) + return; + + *returnedchar = *(TNC->GETPTR++); + + if (TNC->GETPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + + *moretocome = --TNC->RXCOUNT; //ANY MORE? + + if (TNC->RXCOUNT < 128) // GETTING LOW? + { + if (TNC->RTSFLAG & 1) // RTS UP? + { + // RTS HAD BEEN DROPPED TO STOP OTHER END SENDING - RAISE IT AGAIN + + TNC->RTSFLAG &= 0xFE; + } + } +} + +int TNCGetVMSR(struct TNCDATA * TNC, struct TNC2StreamInfo * TNCStream, BOOL ClearDeltas) +{ + // On TNC2 Connected bit should come from current stream + // flow control bits from TNC record + + int val = TNC->VMSR; + + if (TNC->Mode == TNC2) + val |= TNCStream->VMSR; + + if (ClearDeltas) + { + TNC->VMSR &= 0xF0; // CLEAR DELTA BITS + + if (TNC->Mode == TNC2) + TNCStream->VMSR &= 0xF0; + } + return val; +} + +BOOL TNCRUNNING;; + +VOID TNCBGThread(void * unused) +{ + TNCRUNNING = TRUE; + + Sleep(5000); + + while (TNCRUNNING) + { + TNCPoll(); + Sleep(50); + } +} + +VOID AllocateDEDChannel(struct TNCDATA * TNC, int Num) +{ + struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); + char * PNptr; + + // Only show last element of name on Streams display + + PNptr = &TNC->PORTNAME[0]; + + while (strchr(PNptr, '/')) + PNptr = strchr(PNptr, '/') + 1; + + sprintf(pgm, "DED %s", PNptr); + + TNC->Channels[Num] = Channel; + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + Debugprintf("BPQ32 DED Stream %d BPQ Stream %d", Num, Channel->BPQStream ); + + if (TNC->MODE) // if host mode, set appl + SetAppl(Channel->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + + strcpy(pgm, "bpq32.exe"); +} + + +BOOL InitializeTNCEmulator() +{ + int resp, i; + ULONG OpenCount = 0; + DWORD Errorval; + int ApplNum = 1; + UINT APPLMASK; + struct TNC2StreamInfo * TNCStream; + + struct TNCDATA * TNC = TNCCONFIGTABLE; + + TNC2TABLE = TNCCONFIGTABLE; + + while (TNC) + { + // Com Port may be a hardware device (ie /dev/ttyUSB0) COMn or VCOMn (BPQ Virtual COM) + + char * Baud = strlop(TNC->PORTNAME, ','); + char * PNptr; + + PNptr = &TNC->PORTNAME[0]; + + // Only show last element of name on Streams display + + while (strchr(PNptr, '/')) + { + PNptr = strchr(PNptr, '/') + 1; + } + switch (TNC->Mode) + { + case TNC2: + + sprintf(pgm, "TNC2 %s", PNptr); + + // Start with number of streams, can add or remove with USRES command + + if (TNC->Users == 0) + TNC->Users = TNC->HOSTSTREAMS; + + if (TNC->Users == 0) + TNC->Users = TNC->HOSTSTREAMS = 1; + else + TNC->HOSTSTREAMS = TNC->Users; + + Debugprintf("TNC2 USers = %d, HOSTSTREAMS = %d\n", TNC->Users, TNC->HOSTSTREAMS); + + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + TNCStream = TNC->TNC2Stream[i] = zalloc(sizeof(struct TNC2StreamInfo)); + + TNCStream->BPQPort = FindFreeStream(); + + if + (TNCStream->BPQPort == 0) + { + Debugprintf("Insufficient free Streams for TNC2 Emulator"); + return FALSE; + } + + READCHANGE(TNCStream->BPQPort); // Prevent Initial *** disconnected + + TNCStream->MODEFLAG = COMMAND; + + if (TNC->CONOK) + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); + + } + + strcpy(pgm, "bpq32.exe"); + + if (TNC->TPACLEN == 0) + TNC->TPACLEN = PACLEN; // TNC PACLEN + + break; + + case DED: + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 4; // Default + + TNC->MALL = 1; + TNC->MTX = 1; + TNC->MCOM = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + AllocateDEDChannel(TNC, i); // Also used by Y command handler + } + + TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); + memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring + + break; + + case KANTRONICS: + + sprintf(pgm, "KANT %s", PNptr); + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 1; // Default + + for (i = 0; i <= TNC->HOSTSTREAMS; i++) + { + struct StreamInfo * Channel; + + // Use Stream zero for defaults + + TNC->Channels[i] = malloc(sizeof (struct StreamInfo)); + memset(TNC->Channels[i], 0, sizeof (struct StreamInfo)); + + Channel = TNC->Channels[i]; + + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + + Debugprintf("BPQ32 KANT Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); + + // channel->Chan_TXQ = 0; + // channel->BPQStream = 0; + // channel->Connected = FALSE; + // channel->MYCall[0] = 0; + + } + break; + + case SCS: + + TNC->ECHOFLAG = 1; + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 1; // Default + + TNC->MALL = 1; + TNC->MCOM = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + sprintf(pgm, "SCS %s", PNptr); + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); + + TNC->Channels[i] = Channel; + + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + + Debugprintf("BPQ32 SCS Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); + } + + TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); + memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring + + strcpy(pgm, "bpq32.exe"); + + break; + + } + + if (Baud) + TNC->Speed = atoi(Baud); + else + TNC->VCOM = TRUE; + + if (_memicmp(TNC->PORTNAME, "COM", 3) == 0) + { + TNC->VCOM = FALSE; + } + else + { + if (_memicmp(TNC->PORTNAME, "VCOM", 4) == 0) + TNC->ComPort = atoi(&TNC->PORTNAME[4]); + } + if (TNC->VCOM == 0) + { + // Real port + + TNC->hDevice = OpenCOMPort(TNC->PORTNAME, TNC->Speed, TRUE, TRUE, FALSE, 0); + + TNC->PortEnabled = 1; + + TNC->RTS = 1; +// TNC->DTR = 1; + } + else + { + // VCOM Port +#ifdef WIN32 + TNC->hDevice = BPQOpenSerialPort(TNC, &Errorval); +#else + TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); +#endif + if (TNC->hDevice != (HANDLE) -1) + { + if (TNC->NewVCOM == 0) + { + resp = BPQSerialIsCOMOpen(TNC->hDevice, &OpenCount); + TNC->PortEnabled = OpenCount; + } + + resp = BPQSerialSetCTS(TNC->hDevice); + resp = BPQSerialSetDSR(TNC->hDevice); + + TNC->CTS = 1; + TNC->DSR = 1; + } + else + { + Consoleprintf("TNC - Open Failed for Port %s", TNC->PORTNAME); + TNC->hDevice = 0; + } + } + + if (TNC->hDevice) + { + // Set up buffer pointers + + TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + TNC->RXCOUNT = 0; + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + TNC->VLSR = 0x20; + TNC->VMSR = 0x30; + +/* PUSH ECX + + MOV ESI,OFFSET UNPROTOCALL + CALL DECODECALLSTRING + + LEA EDI,UNPROTO[EBX] + MOV ECX,56 + REP MOVSB ; UNPROTO ADDR + + POP ECX +*/ + + APPLMASK = TNC->APPLICATION; + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + memcpy(TNC->MYCALL, &APPLCALLTABLE[ApplNum-1].APPLCALL_TEXT, 10); + + if (TNC->MYCALL[0] < '0') + memcpy(TNC->MYCALL, MYNODECALL, 10); + + strlop(TNC->MYCALL, ' '); + } + + TNC = TNC->Next; + } + +#ifdef LINBPQ + strcpy(pgm, "LinBPQ"); +#else + strcpy(pgm, "bpq32.exe"); +#endif + Consoleprintf("TNC Emulator Init Complete"); + + _beginthread(TNCBGThread,0,0); + + return TRUE; +} + +VOID CloseTNCEmulator() +{ + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + int i, Stream; + + TNCRUNNING = FALSE; + + while (TNC) + { + if (TNC->Mode == TNC2) + { + Stream = TNC->BPQPort; + + SetAppl(Stream, 0, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + DeallocateStream(Stream); + } + else + { + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + SetAppl(Stream, TNC->APPLFLAGS, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + DeallocateStream(Stream); + } + } + CloseCOMPort(TNC->hDevice); + + TNC = TNC->Next; + } +} + +VOID TNCTimer() +{ + // 100 Ms Timer + + struct TNCDATA * TNC = TNC2TABLE; + struct StreamInfo * channel; + int n; + + int NeedTrace = 0; + + while (TNC) + { + if (TNC->Mode != TNC2) + goto NotTNC2; + + NeedTrace |= TNC->TRACEFLAG; //SEE IF ANY PORTS ARE MONITORING + + // CHECK FOR PACTIMER EXPIRY AND CMDTIME + + if (TNC->CMDTMR) + { + TNC->CMDTMR--; + + if (TNC->CMDTMR == 0) + { + // CMDTMR HAS EXPIRED - IF 3 COMM CHARS RECEIVED, ENTER COMMAND MODE + + if (TNC->COMCOUNT == 3) + { + // 3 ESCAPE CHARS RECEIVED WITH GUARDS - LEAVE TRAN MODE + + SETCOMM00(TNC); + + goto TIM100; //DONT RISK TRANSTIMER AND CMDTIME FIRING AT ONCE + } + + TNC->CMDTMR = 0; // RESET COUNT + goto TIM100; + + } + } + + if (TNC->TRANSTIMER) + { + TNC->TRANSTIMER--; + if (TNC->TRANSTIMER == 0) + { + if (TNC->MSGLEN) // ?MESSAGE ALREADY SENT + SENDPACKET(TNC); + } + } +TIM100: + // CHECK FLOW CONTROL + + if ((TNC->VMSR & 0x20)) // ALREADY OFF? + { + CHECKCTS(TNC); // No, so check + } + + goto NextTNC; + +NotTNC2: + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + channel = TNC->Channels[n]; + + if (channel->CloseTimer) + { + channel->CloseTimer--; + if (channel->CloseTimer == 0) + Disconnect(channel->BPQStream); + } + } +NextTNC: + TNC = TNC->Next; + } + DOMONITORING(NeedTrace); +} + +/* +#ifndef WIN32 + +int TNCReadCOMBlock(HANDLE fd, char * Block, int MaxLength, int * err) +{ + int Length; + + *err = 0; + + Length = read(fd, Block, MaxLength); + + if (Length < 0) + { + if (errno != 11 && errno != 35) // Would Block + *err = errno; + + return 0; + } + + return Length; +} + +#endif +*/ + +void CheckForConnectStatusChange(struct TNCDATA * TNC); +void CheckForHostStatusChange(struct TNCDATA * TNC); +void CheckForDataFromHost(struct TNCDATA * TNC); +void CheckForDataFromTerminal(struct TNCDATA * TNC); + +int isTNCBusy(struct TNCDATA * TNC) +{ + // if using old VCOM check Q + +#ifdef WIN32 + + if (TNC->VCOM) + { + if (TNC->NewVCOM == 0) + { + int TXCount, RXCount; + + BPQSerialGetQCounts(TNC->hDevice, &RXCount, &TXCount); + + if (TXCount > 4096) + return TRUE; // Busy + + return FALSE; + } + + // Windows New VCOM cant check (I think!) + } + +#endif + + // + + return FALSE; + +} + +VOID TNCPoll() +{ + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + + // This logic had got very convoluted. This Tries + // to rationalize it + + while (TNC) + { + if (TNC->hDevice) + { + CheckForConnectStatusChange(TNC); + CheckForHostStatusChange(TNC); + CheckForDataFromHost(TNC); + CheckForDataFromTerminal(TNC); + } + + TNC = TNC->Next; + } +} + +void CheckForConnectStatusChange(struct TNCDATA * TNC) +{ +#ifdef WIN32 + if (TNC->VCOM && TNC->NewVCOM == 0) + { + // Can check if other end is connected + + int ConCount = 0; + + BPQSerialIsCOMOpen(TNC->hDevice, &ConCount); + + if (TNC->PortEnabled == 1 && ConCount == 0) + + // Connection has just closed - if connected, disconnect stream + + // This should close all streams on multistream port + + SessionControl(TNC->BPQPort, 2, 0); + + if (TNC->PortEnabled != ConCount) + { + TNC->PortEnabled = ConCount; + } + } +#endif +} + +void CheckForHostStatusChange(struct TNCDATA * TNC) +{ + if (TNC->Mode == KANTRONICS) + { + // Have to poll for Data and State changes + + int n, state, change; + struct StreamInfo * Channel; + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + Channel = TNC->Channels[n]; + + SessionState(Channel->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + KANTConnected(TNC, Channel, n); + else + KANTDisconnected(TNC, Channel, n); + } + } + return; + } + + // ?? Should other modes check here ?? +} + +void CheckForDataFromHost(struct TNCDATA * TNC) +{ + unsigned int n; + int retval, more; + char TXMsg[1000]; + ULONG Read = 0; + + // I think we should check for space in TNC to Terminal Buffer + + if (TNC->RXCOUNT + 500 > TNCBUFFLEN) + return; + + // Only KANTRONICS and TNC2 check for data here + // DED and SCS check when polled by Host Program + + if (TNC->Mode == KANTRONICS) + { + // Have to poll for Data and State changes + + int n, len, count; + struct StreamInfo * Channel; + UCHAR Buffer[400]; + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + Channel = TNC->Channels[n]; + + do + { + if (TNC->RXCOUNT + 500 > TNCBUFFLEN) + return; + + GetMsg(Channel->BPQStream, &Buffer[3], &len, &count); + + if (len > 0) + { + // If a failure, set a close timer (for Airmail, etc) + + if (strstr(&Buffer[3], "} Downlink connect needs port number") || + strstr(&Buffer[3], "} Failure with ") || + strstr(&Buffer[3], "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + + else + Channel->CloseTimer = 0; // Cancel Timer + + if (TNC->MODE) + { + Buffer[0] = 'D'; + Buffer[1] = '1'; + Buffer[2] = n + '@'; + SendKISSData(TNC, Buffer, len+3); + } + else + SendDataToTNC(TNC, &Buffer[3], len); + } + } + while (0); //(count > 0); + } + return; + } + + if (TNC->Mode == SCS) + return; + + n = 0; + +getloop: + + TNC2GetChar(TNC, &retval, &more); + + if (retval != -1) + TXMsg[n++] = retval; + + if (more > 0 && n < 1000) goto getloop; + + if (n > 0) + WriteCOMBlock(TNC->hDevice, TXMsg, n); + + return; + +} +/* Where does this go ?? + +// We look for change on current RX Stream + + retval = TNCGetVMSR(TNC, TNC->TNC2Stream[TNC->RXStream], TRUE); + + if ((retval & 8) == 8) //' DCD (Connected) Changed + { + TNC->DCD = (retval & 128) / 128; + + if (TNC->DCD == 1) + BPQSerialSetDCD(TNC->hDevice); + else + BPQSerialClrDCD(TNC->hDevice); + } + + if ((retval & 1) == 1) //' CTS (Flow Control) Changed + { + TNC->CTS = (retval & 16) / 16; + + if (TNC->CTS == 1) + BPQSerialSetCTS(TNC->hDevice); + else + BPQSerialClrCTS(TNC->hDevice); + + } + + BPQSerialGetDTRRTS(TNC->hDevice,&ModemStat); + + + if ((ModemStat & 1) != TNC->DTR) + { + TNC->DTR=!TNC->DTR; + } + + if ((ModemStat & 2) >> 1 != TNC->RTS) + { + TNC->RTS=!TNC->RTS; + } + + TNC = TNC->Next; + continue; + } +#endif + { + // Real Port or Linux Virtual + + int Read, n; + int retval, more; + char TXMsg[500]; +#ifndef WIN32 + int err; + + // We can tell if partner has gone on PTY Pair - read returns 5 + + if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) + Read = TNCReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &err); + else + Read = TNCReadCOMBlock(TNC->hDevice, rxbuffer, 1000, &err); + + if (err) + { + if (TNC->PortEnabled) + { + TNC->PortEnabled = FALSE; + DisableAppl(TNC); + Debugprintf("Device %s closed", TNC->PORTNAME); + } + } + else + TNC->PortEnabled = TRUE; + +#else + if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) + Read = ReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr); + else + Read = ReadCOMBlock(TNC->hDevice, rxbuffer, 1000); +#endif + + if (Read) + { + if (TNC->Mode == TNC2) + { + for (n = 0; n < Read; n++) + TNC2PutChar(TNC, rxbuffer[n]); + } + else if (TNC->Mode == DED) + { + for (n = 0; n < Read; n++) + TfPut(TNC, rxbuffer[n]); + } + else if (TNC->Mode == KANTRONICS) + { + TNC->RXBPtr += Read; + ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + else if (TNC->Mode == SCS) + { + TNC->RXBPtr += Read; + ProcessSCSPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + } + + n=0; + + getloopR: + + TNC2GetChar(TNC, &retval, &more); + + if (retval != -1) + TXMsg[n++] = retval; + + if (more > 0 && n < 500) goto getloopR; + + if (n > 0) + + { + resp = WriteCOMBlock(TNC->hDevice, TXMsg, n); + } + } + TNC = TNC->Next; + } +} +*/ + + +void CheckForDataFromTerminal(struct TNCDATA * TNC) +{ + unsigned int n; + char rxbuffer[1000]; + ULONG Read = 0, resp; + + if (TNC->Mode == KANTRONICS) + resp = GetDataFromTNC(TNC, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &Read); + + else if (TNC->Mode == SCS) + resp = GetDataFromTNC(TNC, &TNC->FROMUSERBUFFER[TNC->FROMUSERLEN], TNCBUFFLEN - TNC->FROMUSERLEN, &Read); + + else + resp = GetDataFromTNC(TNC, rxbuffer, 1000, &Read); + + if (Read) + { + if (TNC->Mode == TNC2) + { + for (n = 0; n < Read; n++) + TNC2PutChar(TNC, rxbuffer[n]); + } + else if (TNC->Mode == DED) + { + for (n = 0; n < Read; n++) + TfPut(TNC, rxbuffer[n]); + } + else if (TNC->Mode == KANTRONICS) + { + TNC->RXBPtr += Read; + ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + else if (TNC->Mode == SCS) + { + TNC->FROMUSERLEN += Read; + ProcessSCSPacket(TNC, TNC->FROMUSERBUFFER, TNC->FROMUSERLEN); + } + } +} + + +int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly); + + +VOID DOMONITORING(int NeedTrace) +{ + // IF ANY PORTS HAVE MONITOR ENABLED, SET MONITOR BIT ON FIRST PORT + + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + int Tracebit = 0, len, count, n; + time_t Stamp; + ULONG SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + + if (NeedTrace) + Tracebit = 0x80; + + if (TNC->CONOK) + SetAppl(TNC->BPQPort, TNC->APPLFLAGS | Tracebit, TNC->APPLICATION); + else + SetAppl(TNC->BPQPort, TNC->APPLFLAGS | Tracebit, 0); + + Stamp = GetRaw(TNC->BPQPort, (char *)&MONITORDATA, &len, &count); + + if (len == 0) + return; + + len = DecodeFrame(&MONITORDATA, MONBUFFER, Stamp); + + while (TNC) + { + if (TNC->Mode == TNC2 && TNC->TRACEFLAG) + { + SetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); + len = IntDecodeFrame(&MONITORDATA, MONBUFFER, Stamp, TNC->MMASK, FALSE, FALSE); +// printf("%d %d %d %d %d\n", len, MMASK, MTX, MCOM, MUIONLY); + SetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (len) + { + for (n = 0; n < len; n++) + { + PUTCHARINBUFFER(TNC, MONBUFFER[n]); + } + } + } + TNC=TNC->Next; + } +} + + +VOID TNC2PutChar(struct TNCDATA * TNC, int Char) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + if (TNCStream->MODEFLAG & COMMAND) + goto KEYB06C; // COMMAND MODE - SKIP TRANS TEST + + if (TNCStream->MODEFLAG & TRANS) + goto KEYB06T; // TRANS MODE + + // CONV MODE - SEE IF CPACTIME ON + + if (TNC->CPACTIME) + TNC->TRANSTIMER = TRANSDELAY; + + goto KEYB06; // PROCESS CHAR + +KEYB06T: + +// Transparent Mode - See if Escape Sequence Received (3 esc chars, with guard timer) + +// CHECK FOR COMMAND CHAR IF CMDTIME EXPIRED OR COMAND CHAR ALREADY RECEIVED + + if (TNC->COMCOUNT) + goto KBTRN3; // ALREADY GOT AT LEAST 1 + + if (TNC->CMDTMR) + goto KBTRN5; // LESS THAN CMDTIME SINCE LAST CHAR + +KBTRN3: + + if (Char != TNC->COMCHAR) + { + TNC->COMCOUNT = 0; + goto KBTRN5; // NOT COMMAND CHAR + } + + TNC->COMCOUNT++; + +KBTRN5: + + TNC->CMDTMR = TNC->CMDTIME; // REPRIME ESCAPE TIMER + + TNC->TRANSTIMER = TRANSDELAY; + + KBNORM(TNC, Char); + return; // TRANSPARENT MODE + +KEYB06: + +// STILL JUST CONV MODE + + if (Char != TNC->SENDPAC) + goto NOTSENDPAC; + +// SEND PACKET CHAR - SHOUD WE SEND IT? + + TNC->TRANSTIMER = 0; + + if (TNC->CRFLAG) + KBNORM(TNC, Char); // PUT CR IN BUFFER + + SENDPACKET(TNC); + return; + + +NOTSENDPAC: +KEYB06C: + + // COMMAND OR CONV MODE + + // Check for Escaped + + if (TNC->InEscape) + { + TNC->InEscape = 0; + KBNORM(TNC, Char); // Process as normal chars + return; + } + + if (TNC->InStreamSW) + { + TNC->InStreamSW = 0; + + if (Char != TNC->StreamSW) + { + // Switch TX Stream if valid + + int n; + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + if (TNC->LCStream) + Char = toupper(Char); + + n = Char - 'A'; + + if (n >= 0 && TNC->TNC2Stream[n]) + TNC->TXStream = n; + + return; + } + } + + if (Char == TNC->PASSCHAR) + { + TNC->InEscape = 1; + return; + } + + + + if (TNC->StreamSW && Char == TNC->StreamSW) + { + TNC->InStreamSW = 1; + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + return; + } + + if (Char < 32) // control + { + if (Char == 10 && TNC->LFIGNORE) + return; + + if (Char == 8) + { + if (TNC->MSGLEN == 0) + return; + + TNC->MSGLEN--; + TNC->CURSOR--; + + if (TNC->ECHOFLAG) + { + KBECHO(TNC, Char); // Delete char from display + KBECHO(TNC, ' '); + KBECHO(TNC, Char); + } + return; + } + + if (Char == 26) // Ctrl/Z + { + KBNORM(TNC, Char); // FOR MBX TERMINATOR + return; + } + + if (Char == TNC->COMCHAR) + { + SETCOMMANDMODE(TNC); + return; + } + + if (TNCStream->MODEFLAG & COMMAND) + { + if (Char == 0x14) // CTRL/T + { + TNC->TRACEFLAG ^= 1; + return; + } + + if (Char == 13) + { + KBNORM(TNC, 13); // PUT CR IN BUFFER + SENDPACKET(TNC); + return; + } + } + KBNORM(TNC, Char); // Process others as normal chars + } KBNORM(TNC, Char); +} + +VOID KBNORM(struct TNCDATA * TNC, int Char) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + if (TNC->MSGLEN > 256) + goto TOOLONG; // PROTECT BUFFER + + *(TNC->CURSOR++) = Char; + TNC->MSGLEN++; + +TOOLONG: + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + if (TNC->MSGLEN < TNCStream->TPACLEN) + return; + +// DONT APPLY PACLEN IN COMMAND MODE + + if (TNCStream->MODEFLAG & COMMAND) + return; + + SENDPACKET(TNC); // Send what we have +} + + +VOID SETCOMMANDMODE(struct TNCDATA * TNC) +{ + if (TNC->MSGLEN) + SENDPACKET(TNC); + + SETCOMM00(TNC); +} + +VOID SETCOMM00(struct TNCDATA * TNC) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->MODEFLAG |= COMMAND; // BACK TO COMMAND MODE + TNCStream->MODEFLAG &= ~(CONV+TRANS); + TNC->TRANSTIMER = 0; // CANCEL TRANS MODE SEND TIMER + TNC->AUTOSENDFLAG = 0; // IN CASE ALREADY SET + + CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed + + SENDREPLY(TNC, CMDMSG, 4); + + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; +} + + + +VOID SENDPACKET(struct TNCDATA * TNC) +{ + // SEE IF COMMAND STATE + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + int Stream = 0; // Unprooto + + if (TNCStream->MODEFLAG & COMMAND) + { + TNCCOMMAND(TNC); // COMMAND TO TNC + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + return; + } + + // IF CONNECTED, SEND TO L4 (COMMAND HANDLER OR DATA), + // OTHERWISE SEND AS AN UNPROTO FRAME (TO ALL PORTS) + + + if (TNCStream->VMSR & 0x80) // CONNECTED? + Stream = TNCStream->BPQPort; + + SendMsg(Stream, TNC->TONODEBUFFER, TNC->MSGLEN); + + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + CHECKCTS(TNC); // SEE IF NOW BUSY + + return; +} + +VOID KBECHO(struct TNCDATA * TNC, int Char) +{ + PUTCHARINBUFFER(TNC, Char); +} + +VOID TNCCOMMAND(struct TNCDATA * TNC) +{ + // PROCESS COMMAND TO TNC CODE + + char * ptr, * ptr1, * ptr2; + int n; + CMDX * CMD; + + *(--TNC->CURSOR) = 0; + + + strcat(TNC->TONODEBUFFER, " "); + + ptr = strchr(TNC->TONODEBUFFER, ' '); + + if (ptr) + { + // convert command to upper case, leave tail + + *ptr = 0; + _strupr(TNC->TONODEBUFFER); + *ptr = ' '; + } + + if (_memicmp(ptr, " switch", 7) == 0) + _strupr(ptr); // Special Case + + ptr1 = &TNC->TONODEBUFFER[0]; // + + n = 10; + + while ((*ptr1 == ' ' || *ptr1 == 0) && n--) + ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) + + if (n == -1) + { + // Null command + + SENDREPLY(TNC, CMDMSG, 4); + return; + } + + ptr2 = ptr1; // Save + + CMD = &COMMANDLIST[0]; + n = 0; + + for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + ptr1 = ptr2; + + // ptr1 is input command + + if (memcmp(CMD->String, ptr1, CL) == 0) + { + // Found match so far - check rest + + char * ptr2 = &CMD->String[CL]; + + ptr1 += CL; + + if (*(ptr1) != ' ') + { + while(*(ptr1) == *ptr2 && *(ptr1) != ' ') + { + ptr1++; + ptr2++; + } + } + + if (*(ptr1) == ' ') + { + ptr1++; // Skip space + + CMD->CMDPROC(TNC, ptr1, CMD); + SENDREPLY(TNC, CMDMSG, 4); + + return; + } + } + + CMD++; + + } + + SENDREPLY(TNC, WHATMSG, 8); + +} + +/* +; +UNPROTOCMD: +; +; EXTRACT CALLSIGN STRING +; + CMP BYTE PTR [ESI],20H + JE UNPROTODIS + + CMP BYTE PTR [ESI],"*" + JE CLEARUNPROTO + + CALL DECODECALLSTRING ; CONVERT TO AX25 FORMAT + + JZ UNPROTOOK + + JMP TNCDUFF + +CLEARUNPROTO: + + LEA EDI,UNPROTO[EBX] + MOV AL,0 + MOV ECX,63 + REP STOSB ; COPY IN + +UNPROTODIS: + + MOV AL,1 + CALL DISPLAYUNPROTO ; DISPLAY CURRENT SETTING + JMP SENDOK + +UNPROTOOK: + + PUSH ESI + MOV AL,0 + CALL DISPLAYUNPROTO ; DISPLAY OLD STRRING + POP ESI + + LEA EDI,UNPROTO[EBX] + MOV ECX,63 + REP MOVSB ; COPY IN +CONMODE: + JMP SENDOK +;CONMODE: + JMP KBRET + +DISPLAYUNPROTO: +; + PUSH EAX + + MOV ESI,OFFSET UNPROT + MOV ECX,8 + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET WAS ; DISPLAY "was" + MOV ECX,5 + + POP EAX + OR AL,AL + JZ DISPU00 ; NO + + MOV ECX,1 ; LEAVE OUT "WAS" + +DISPU00: + + CALL PUTSTRINGINBUFFER + + LEA ESI,UNPROTO[EBX] + CMP BYTE PTR [ESI],40H + JBE DISPUPRET + + CALL CONVFROMAX25 + + PUSH ESI + + MOV ESI,OFFSET NORMCALL + + CALL PUTSTRINGINBUFFER + + POP ESI + + CMP BYTE PTR [ESI],0 + JE DISPUPRET + + PUSH ESI + + MOV ESI,OFFSET VIA + MOV ECX,5 + CALL PUTSTRINGINBUFFER + + POP ESI + +DISPUPLOOP: + + CALL CONVFROMAX25 + + PUSH ESI + MOV ESI,OFFSET NORMCALL + INC ECX + CALL PUTSTRINGINBUFFER + POP ESI + + CMP BYTE PTR [ESI],0 + JNE DISPUPLOOP + + +DISPUPRET: + MOV AL,0DH + CALL PUTCHARINBUFFER + RET + + +BTEXT: +; + CMP BYTE PTR [ESI],20H + JE BTDIS +; + PUSH ESI + MOV AL,0 + CALL DISPLAYBT ; DISPLAY OLD STRING + POP ESI + + MOV EDI,OFFSET BTEXTFLD + MOV ECX,255 +BTLOOP: + LODSB + STOSB + CMP ESI,CMDENDADDR ; END? + JE BTEND + + LOOP BTLOOP +BTEND: + XOR AL,AL + STOSB ; NULL ON END +; +; SET UP TO SEND IT AS A UI +; + MOV ECX,EDI + MOV ESI,OFFSET BTHDDR + SUB ECX,ESI + MOV MSGLENGTH[ESI],CX +; +; PASS TO SWITCH +; + MOV ESI,OFFSET BTEXTFLD + SUB ECX,6 ; DONT NEED HEADER + + MOV AH,12 ; UPDATE FUNCTIONS + MOV DX,1 ; UPDATE BT + + CALL NODE ; PASS TO NODE + + JMP SENDOK + +BTDIS: + MOV AL,1 + CALL DISPLAYBT ; DISPLAY CURRENT SETTING + JMP SENDOK + + +DISPLAYBT: +; + PUSH EAX + + MOV ESI,OFFSET BTCMD + MOV ECX,8 + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET WAS ; DISPLAY "was" + MOV ECX,5 + + POP EAX + OR AL,AL + JZ DISPBT00 ; NO + + MOV ECX,1 ; LEAVE OUT "WAS" + +DISPBT00: + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET BTEXTFLD +DISPBT10: + LODSB + OR AL,AL + JZ DISPBT20 + + CALL PUTCHARINBUFFER + + JMP DISPBT10 + +DISPBT20: + MOV AL,0DH + CALL PUTCHARINBUFFER + RET + + +*/ + +VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[Stream]; + + TNCStream->VMSR |= 0x88; // SET CONNECTED + + // IF NOMODE IS ON LEAVE IN TNC COMMAND MODE, ELSE PUT INTO CONV MODE + // (MAY NEED TO IMPLEMENT CONMODE SOMETIME) + + if (TNC->NOMODE) + return; + + TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); +} + +VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len) +{ + int n = 0; + + CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed + + for (n= 0; n < Len; n++) + { + PUTCHARINBUFFER(TNC, Msg[n]); + } +} + + +VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream) +{ + // SEND TAPR-STYLE *** CONNECTED TO CURRENT PORT + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[ToStream]; + + int len; + char Response[128]; + char Call[11] = ""; + int paclen, dummy; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4 = NULL; + int stream; + + GetConnectionInfo(TNCStream->BPQPort, Call, &dummy, &dummy, &paclen, &dummy, &dummy); + + if (paclen) + TNCStream->TPACLEN = paclen; + + if (TNCStream->MODEFLAG & TRANS) + return; //NOT IF TRANSPARENT + + strlop(Call, ' '); + + strcpy(TNCStream->RemoteCall, Call); + + CheckForStreamChange(TNC, ToStream); // Send Stream Switched Message if changed + + if (TNC->CBELL) + len = sprintf(Response, "%s%s%c\r", CONMSG, Call, 7); // Add BELL char + else + len = sprintf(Response, "%s%s\r", CONMSG, Call); + + SENDREPLY(TNC, Response, len); + + // If incoming session Send CTEXT if set + + stream = TNCStream->BPQPort; + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return; + + SESS = &BPQHOSTVECTOR[stream]; + + if (SESS && SESS->HOSTSESSION) + L4 = SESS->HOSTSESSION; + + if (L4 && (L4->L4CIRCUITTYPE & DOWNLINK)) + { + if (TNC->CMSG && TNC->CTEXT[0]) + { + // Add CTEXT + int n; + char Msg[256]; + + n = sprintf(Msg, "%s\r", TNC->CTEXT); + SendMsg(TNCStream->BPQPort, Msg, n); + } + + // if CHECK_FOR_ESC set in applflags send "^d to disconnect msg + + if ((TNC->APPLFLAGS & CHECK_FOR_ESC)) // If incoming session + { + char Msg[] = "Send ^D to disconnect\r"; + + SendMsg(TNCStream->BPQPort, Msg, (int)strlen(Msg)); + } + } +} + +VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char) +{ + // CALLED BY L4 CODE TO PASS DATA TO VIRTUAL TNC + ; + if (TNC->RXCOUNT >= TNCBUFFLEN) + { + // OVERRUN - LOSE IT + + TNC->VLSR |= 2; // SET OVERRUN ERROR + return; + } + + TNC->VLSR &= ~2; // CLEAR OVERRRUN + + *(TNC->PUTPTR++) = Char; + TNC->RXCOUNT++; + + if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->PUTPTR = &TNC->TOUSERBUFFER[0]; + + if(TNC->RXCOUNT > TNCBUFFLEN-300) // ALLOW FOR FULL PACKET + { + // BUFFER GETTING FULL - DROP RTS/DTR + + TNC->RTSFLAG |= 1; // SET BUSY + } + + if (Char == 13 && TNC->AUTOLF) + PUTCHARINBUFFER(TNC, 10); // Add LF +} + + +VOID CHECKCTS(struct TNCDATA * TNC) +{ + // SEE IF CROSS-SESSION STILL BUSY + + if (RXCount(TNC->BPQPort) > 4) + { + // Busy + + if ((TNC->VMSR & 0x10) == 0) // ALREADY OFF? + return; // No Change + + TNC->VMSR &= 0xef; // Drop CTS + TNC->VMSR |= 1; // Delta bit + return; + } + + // Not busy + + if (TNC->VMSR & 0x10) // ALREADY ON? + return; // No Change + + TNC->VMSR |= 0x11; // CTS AND DELTA CTS +} + + + +VOID CONNECTTONODE(struct TNCDATA * TNC) +{ + char AXCALL[7]; + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + int SaveAuthProg = AuthorisedProgram; + + AuthorisedProgram = 1; + SessionControl(TNCStream->BPQPort, 1, TNC->APPLICATION); + AuthorisedProgram = SaveAuthProg; + + ConvToAX25(TNC->MYCALL, AXCALL); + ChangeSessionCallsign(TNCStream->BPQPort, AXCALL); + + // Set default Paclen + + TNCStream->TPACLEN = TNC->TPACLEN; +} + + +VOID GETDATA(struct TNCDATA * TNC) +{ + // I'm sure this should only be called for TNC2 devices + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + int state, change, InputLen, count, n, i; + char InputBuffer[512]; + + // LOOK FOR STATUS CHANGE + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + TNCStream = TNC->TNC2Stream[i]; + + LocalSessionState(TNCStream->BPQPort, &state, &change, TRUE); + + if (change == 1) + { + if (state == 1) // Connected + { + SEND_CONNECTED(TNC, i); + DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED + } + else + { + TNCStream->MODEFLAG |= COMMAND; + TNCStream->MODEFLAG &= ~(CONV+TRANS); + + TNCStream->VMSR &= 0x7F; // DROP DCD + TNCStream->VMSR |= 8; // DELTA DCD + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + SENDREPLY(TNC, DISCONNMSG, 18); + } + } + else + { + // No Change + + // VERIFY CURRENT STATE + + if (state == 1) // Connected + { + // SWITCH THINKS WE ARE CONNECTED + + if ((TNCStream->VMSR & 0x80) == 0) + { + // TNC Doesn't + + SEND_CONNECTED(TNC, i); + DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED + } + } + else + { + // SWITCH THINKS WE ARE DISCONNECTED + + if (TNCStream->VMSR & 0x80) + { + // We Disagree, so force off + + TNCStream->MODEFLAG |= COMMAND; + TNCStream->MODEFLAG &= ~(CONV+TRANS); + + TNCStream->VMSR &= 0x7F; // DROP DCD + TNCStream->VMSR |= 8; // DELTA DCD + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + SENDREPLY(TNC, DISCONNMSG, 18); + } + } + } + + // SEE IF ANYTHING QUEUED + + if (TNC->RTSFLAG & 1) + continue; + + GetMsg(TNCStream->BPQPort, InputBuffer, &InputLen, &count); + + if (InputLen == 0) + continue; + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + + // if CHECK_FOR_ESC set in APPLFLAGS looks for Disconnect Escape + + if (TNC->APPLFLAGS & CHECK_FOR_ESC) + { + // look for ^D (or ^d) + + if (InputLen == 3) + { + if (_memicmp(InputBuffer, "^D\r", 3) == 0) + { + Disconnect(TNCStream->BPQPort); + continue; + } + } + } + + for (n = 0; n < InputLen; n++) + { + char c = InputBuffer[n]; + + if (TNC->StreamDbl && c == TNC->StreamSW) + PUTCHARINBUFFER(TNC, TNC->StreamSW); + + PUTCHARINBUFFER(TNC, c); + } + } +} + +// DED Mode Support + +unsigned char PARAMREPLY[]="* 0 0 64 10 4 4 10 100 18000 30 2 0 2\r\n"; + +#define PARAMPORT PARAMREPLY[2] + +#define LPARAMREPLY 39 + +unsigned char BADCMDREPLY[]="\x2" "INVALID COMMAND\x0"; + +#define LBADCMDREPLY 17 //sizeof BADCMDREPLY + +unsigned char DATABUSYMSG[]="\x2" "TNC BUSY - LINE IGNORED\x0"; +#define LDATABUSY 25 + +unsigned char BADCONNECT[]="\x2" "INVALID CALLSIGN\x0"; +#define LBADCONNECT 18 + +unsigned char BUSYMSG[]="BUSY fm SWITCH\x0"; + +//unsigned char CONSWITCH[]="\x3" "(1) CONNECTED to \x0"; + +unsigned char DEDSWITCH[]="\x1" "0:SWITCH \x0"; +#define LSWITCH 14 + +unsigned char NOTCONMSG[]="\x1" "CHANNEL NOT CONNECTED\x0"; +#define LNOTCON 23 + +unsigned char ALREADYCONMSG[]="You are already connected on another port\r"; +#define ALREADYLEN 45 + + +byte * EncodeCall(byte * Call); +VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len); +VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len); +int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly); +int DOCOMMAND(struct TNCDATA * conn); +int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); + + + + +VOID PUTSTRING(struct TNCDATA * conn, UCHAR * Msg) +{ + int len = (int)strlen(Msg); + + while (len) + { + *(conn->PUTPTR++) = *(Msg++); + + if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) + conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; + + conn->RXCOUNT++; + + len--; + } +} + + +int PUTCHARx(struct TNCDATA * conn, UCHAR c) +{ + *(conn->PUTPTR++) = c; + + if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) + conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; + + conn->RXCOUNT++; + + return 0; +} + + + +VOID DisableAppl(struct TNCDATA * TNC) +{ + int i, Stream; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + SetAppl(Stream, TNC->APPLFLAGS, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + } +} + +VOID EnableAppl(struct TNCDATA * TNC) +{ + int i; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + } +} + +BOOL TfPut(struct TNCDATA * TNC, UCHAR character) +{ + struct StreamInfo * Channel; + TRANSPORTENTRY * L4 = NULL; + + if (!TNC->MODE) + goto CHARMODE; + +// HOST MODE + + if (TNC->HOSTSTATE == 0) + { + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + return TRUE; + } + + if (TNC->HOSTSTATE == 1) + { + TNC->MSGTYPE = character; + TNC->HOSTSTATE++; + return TRUE; + } + + if (TNC->HOSTSTATE == 2) + { + TNC->MSGCOUNT = character; + TNC->MSGLENGTH = character; + TNC->MSGCOUNT++; + TNC->MSGLENGTH++; + TNC->HOSTSTATE++; + + TNC->DEDCURSOR = &TNC->DEDTXBUFFER[0]; + return TRUE; + } + +// RECEIVING COMMAND/DATA + + *(TNC->DEDCURSOR++) = character; + + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + return TRUE; // MORE TO COME + + TNC->HOSTSTATE=0; + + if (TNC->MSGCHANNEL <= TNC->HOSTSTREAMS) + Channel = TNC->Channels[TNC->MSGCHANNEL]; + else + Channel = TNC->Channels[1]; + + DEDPROCESSHOSTPACKET(Channel, TNC); + + TNC->HOSTSTATE = 0; + + return TRUE; + + +CHARMODE: + + if (character == 0x11) return TRUE; + + if (character == 0x18) + { + // CANCEL INPUT + + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + + return(TRUE); + } + + *(TNC->CURSOR++) = character; + + if (character == 1 && (TNC->CURSOR > &TNC->TONODEBUFFER[4]) && *(TNC->CURSOR - 2) == 1 && *(TNC->CURSOR - 3) == 1) + { + // Looks like a resync request - Appl thinks we are in host mode + + TNC->MODE = 1; + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + EnableAppl(TNC); + + return(TRUE); + } + + + if (TNC->CURSOR == &TNC->TONODEBUFFER[300]) + TNC->CURSOR--; + + if (character == 0x0d) + { + // PROCESS COMMAND (UNLESS HOST MODE) + + *(TNC->CURSOR++) = 0; + + DOCOMMAND(TNC); + } + return TRUE; +} + + +int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC) +{ + UCHAR * TXBUFFERPTR; + int i; + int Work; + char WorkString[256]; + int State, Change, Count; + TRANSPORTENTRY * L4 = NULL; + unsigned char * MONCURSOR=0; + int SaveAuthProg = 0; + + TXBUFFERPTR = &TNC->DEDTXBUFFER[0]; + + if (Channel->Chan_TXQ == (UCHAR *)(ptrdiff_t) -1) + { + Channel->Chan_TXQ = 0; + } + + if (TNC->MSGTYPE != 0) + goto NOTDATA; + + goto HOSTDATAPACKET; + +//HOSTCMDS: +// DD 'G','I', 'J', 'C', 'D', 'L', '@', 'Y', 'M' +// DD POLL,ICMD,JCMD,CCMD,DCMD,LCMD,ATCOMMAND,YCMD,HOSTMON + +NOTDATA: + + if (TNC->DEDTXBUFFER[0] == 1) + { + // recovering + +// if (!TNC->Recovering) +// { +// sprintf(msg, "Port %d DED Recovery started\n", TNC->ComPort); +// OutputDebugString(msg); +// TNC->Recovering = TRUE; +// } + } + else + { + // Not recovery + +// if (TNC->Recovering) +// { +// sprintf(msg, "Port %d DED Recovery completed\n", TNC->ComPort); +// OutputDebugString(msg); +// TNC->Recovering = FALSE; +// } + + } + + if (TNC->DEDTXBUFFER[0] == 1) + goto DUFFHOSTCMD; + +// sprintf(msg,"DED CMD: Port %d CMD %c MSGCHANNEL %d\n", TNC->ComPort, TNC->TONODEBUFFER[0], MSGCHANNEL); +// OutputDebugString(msg); + + if (_memicmp(TNC->DEDTXBUFFER, "QRES", 4 == 0)) + goto SENDHOSTOK; + + switch (toupper(TNC->DEDTXBUFFER[0])) + { + case 1: + + goto DUFFHOSTCMD; + + case 'G': + + goto POLL; + + case 'I': + goto ICMD; + + case 'J': + + TNC->MODE = TNC->DEDTXBUFFER[5] & 1; + + if (TNC->MODE) + EnableAppl(TNC); + else + DisableAppl(TNC); + + goto SENDHOSTOK; + + case 'C': + goto CCMD; + + case 'D': + goto DCMD; + + case 'L': + goto LCMD; + + case '@': + goto ATCOMMAND; + + case 'Y': + goto YCMD; + + case 'E': + goto ECMD; + + case 'M': + + if (TNC->DEDTXBUFFER[1] == 'N') + goto DISABLEMONITOR; + + goto ENABLEMONITOR; + + case 'K': + case 'O': + goto SENDHOSTOK; + + case 'V': // Vesrion + + PUTCHARx(TNC, TNC->MSGCHANNEL); + PUTCHARx(TNC, 1); + PUTSTRING(TNC, "DSPTNC Firmware V.1.3a, (C) 2005-2010 SCS GmbH & Co."); + PUTCHARx(TNC, 0); + + return TRUE; + + default: + goto SENDHOSTOK; + +ATCOMMAND: + + if (TNC->DEDTXBUFFER[1] == 'B') + goto BUFFSTAT; + + if (TNC->DEDTXBUFFER[1] == 'M') + goto BUFFMIN; + +// Not recognised + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; + + +BUFFMIN: + + Work = MINBUFFCOUNT; + goto BUFFCOMM; + +BUFFSTAT: + + Work = QCOUNT; + +BUFFCOMM: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + + sprintf(WorkString, "%d", Work); // Buffer count + + PUTSTRING(TNC, WorkString); + + PUTCHARx(TNC, 0); + return TRUE; + +ICMD: + + { + char * Call = &TNC->DEDTXBUFFER[1]; + int len; + char Reply[80]; + char ReplyCall[10]; + + + if (TNC->MSGLENGTH > 2) + { + // Save callsign + + TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; + + if (*Call == ' ') + *Call++; // May have leading space + + _strupr(Call); + + memset(Channel->MYCall, ' ', 10); + memcpy(Channel->MYCall, Call, (int)strlen(Call)); + + Debugprintf("DED Host I chan %d call %s", TNC->MSGCHANNEL, Call); + + strcpy(ReplyCall, Call); + +/* + if (TNC->MSGCHANNEL == 0) // if setting zero, copy to all others + { + int i; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + memcpy(TNC->Channels[i]->MYCall, TNC->Channels[0]->MYCall, 10); + Debugprintf("DED Capy to chan %d call %s", i, Channel->MYCall); + } + } +*/ + + + + + + + } + else + { + memcpy(ReplyCall, Channel->MYCall, 10); + strlop(ReplyCall, ' '); + } + + len = sprintf(Reply, "\x2%s", ReplyCall); + + SENDCMDREPLY(TNC, Reply, len + 1); // include the null + + return TRUE; + } +ECMD: + + goto SENDHOSTOK; + +DUFFHOSTCMD: + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; + +ENABLEMONITOR: + + TNC->TRACEFLAG = 0x80; + goto MONCOM; + +DISABLEMONITOR: + + TNC->TRACEFLAG = 0; + +MONCOM: + + SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); + + goto SENDHOSTOK; + +CCMD: + +// CONNECT REQUEST + + if (TNC->MSGCHANNEL == 0) + goto SENDHOSTOK; // SETTING UNPROTO ADDR - JUST ACK IT + + *TNC->DEDCURSOR = 0; + + if (TNC->MSGLENGTH > 1) + goto REALCALL; + +// STATUS REQUEST - IF CONNECTED, GET CALL + + DEDSWITCH[3] = 0; + + GetCallsign(Channel->BPQStream, &DEDSWITCH[3]); + + Debugprintf("CCMD %d %d %s", TNC->MSGCHANNEL, TNC->Channels[TNC->MSGCHANNEL]->BPQStream, &DEDSWITCH[3]); + + if (DEDSWITCH[3] == 0) + SENDCMDREPLY(TNC, NOTCONMSG, LNOTCON); + else + SENDCMDREPLY(TNC, DEDSWITCH, LSWITCH); + + return TRUE; + +REALCALL: + +// If to Switch, just connect, else pass c command to Node + + Debugprintf("CCMD %d %s", TNC->MSGCHANNEL, TXBUFFERPTR); + + SaveAuthProg = AuthorisedProgram; + AuthorisedProgram =1; + Connect(Channel->BPQStream); + AuthorisedProgram = SaveAuthProg; + +// CONNECT WILL BE REPORTED VIA NORMAL STATUS CHANGE + + if (Channel->MYCall[0] > ' ') + ChangeSessionCallsign(Channel->BPQStream, EncodeCall(Channel->MYCall)); + else + ChangeSessionCallsign(Channel->BPQStream, EncodeCall(TNC->Channels[0]->MYCall)); + + _strupr(TXBUFFERPTR); + + if (strstr(TXBUFFERPTR, "SWITCH") == 0) // Not switch + { + char * Call, * Arg1; + char * Context; + char seps[] = " ,\r"; + + Call = strtok_s(TXBUFFERPTR + 1, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1) + { + // Have a digi string + + // First digi is used as a port number. Any others are rwal digis or WINMOR/PACTOR + + if (Context[0]) + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s v %s\r", Arg1, Call, Context); + else + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s\r", Call, Arg1); + } + else + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s\r", Call); + + strcpy(TXBUFFERPTR, TXBUFFERPTR + 100); + + SendMsg(Channel->BPQStream, TXBUFFERPTR, TNC->MSGLEN); + +// READCHANGE(Channel->BPQStream); // Suppress Connected to Switch + + goto SENDHOSTOK; + } + } + + goto SENDHOSTOK; + +DCMD: + +// DISCONNECT REQUEST + + Disconnect(Channel->BPQStream); + + goto SENDHOSTOK; + +LCMD: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + +// GET STATE AND QUEUED BUFFERS + + if (TNC->MSGCHANNEL) + goto NORM_L; + +// TO MONITOR CHANNEL + +// SEE IF MONITORED FRAMES AVAILABLE + + if (MONCount(TNC->Channels[0]->BPQStream)) + Work = 0x31; + else + Work = 0x30; + + goto MON_L; + +NORM_L: + + LocalSessionState(Channel->BPQStream, &State, &Change, FALSE); + + if (State == 0) + Work = '0'; + else + Work = '4'; // AX.25 STATE + + PUTCHARx(TNC, Change + '0'); // Status Messages + + PUTCHARx(TNC, ' '); + +// GET OTHER QUEUE COUNTS + + Count = RXCount(Channel->BPQStream); + + sprintf(WorkString, "%d", Count); // message count + + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + +// NOT SENT IS NUMBER ON OUR QUEUE, NOT ACKED NUMBER FROM SWITCH + + +// SEE HOW MANY BUFFERS ATTACHED TO Q HEADER IN BX + + Count = 0;// C_Q_COUNT(Channel->Chan_TXQ); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + if (Count > 8) + Work = '8'; // Busy + + Count = CountFramesQueuedOnSession(L4); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + +MON_L: + + PUTCHARx(TNC, '0'); + PUTCHARx(TNC, ' '); + PUTCHARx(TNC, Work); + PUTCHARx(TNC, 0); + + return TRUE; + +HOSTDATAPACKET: + +// } +// { +// UCHAR msg[100]; + +// sprintf(msg,"Host Data Packet: Port %d\n", TNC->ComPort); +// OutputDebugString(msg); +// } +// + + +// IF WE ALREADY HAVE DATA QUEUED, ADD THIS IT QUEUE + +// if (Channel->Chan_TXQ) +// { + +// // COPY MESSAGE TO A CHAIN OF BUFFERS + +// if (QCOUNT < 10) +// goto CANTSEND; // NO SPACE - RETURN ERROR (?) + +//QUEUEFRAME: + +// C_Q_ADD(Channel->Chan_TXQ, COPYMSGTOBUFFERS()); // RETURNS EDI = FIRST (OR ONLY) FRAGMENT + +// goto SENDHOSTOK; + + // MAKE SURE NODE ISNT BUSY + + if (TNC->MSGCHANNEL == 0) // UNPROTO Channel + goto SendUnproto; + + Count = CountFramesQueuedOnSession(L4); + +// if (Count > 4 || QCOUNT < 40) +// goto QUEUEFRAME; + + // OK TO PASS TO NODE + + SENDENFORCINGPACLEN(Channel, TNC->DEDTXBUFFER, TNC->MSGLENGTH); + goto SENDHOSTOK; + +SendUnproto: + + SendMsg(0, TXBUFFERPTR, TNC->MSGLENGTH); + goto SENDHOSTOK; + +POLL: + + PROCESSPOLL(TNC, Channel); + return TRUE; + +YCMD: + + *TNC->DEDCURSOR = 0; + + Work = atoi(&TXBUFFERPTR[1]); + + if (Work == 0) + Work = 1; // Mustn't release last stream + + if (Work >= 0 && Work <= MAXSTREAMS) + { + int Stream; + + if (Work < TNC->HOSTSTREAMS) + { + // Need to get rid of some streams + + for (i = Work + 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + if (Stream) + { + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + + DeallocateStream(Stream); + + Debugprintf("DED YCMD Release Stream %d", Stream); + } + + free(TNC->Channels[i]); + TNC->Channels[i] = 0; + } + } + else + { + for (i = TNC->HOSTSTREAMS+1; i <= Work; i++) + { + AllocateDEDChannel(TNC, i); // Also used by Y command handler + } + } + TNC->HOSTSTREAMS = Work; + } + + /* + + Why is this here? + { + int Len=0; + UCHAR Message[1000]; + + while (TNC->RXCOUNT > 0) + { + Message[Len++]= *(TNC->PUTPTR++); + + TNC->RXCOUNT--; + + if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->PUTPTR = (UCHAR *)&TNC->TOUSERBUFFER; + + if (Len > 900) + { + BPQSerialSendData(TNC, Message, Len); + Len = 0; + } + } + + if (Len > 0) + { + BPQSerialSendData(TNC, Message, Len); + } + } + _asm { + + popad + + + RET +*/ + +SENDHOSTOK: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; + +} +int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + int PollType; + + // ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA + + if (TNC->MSGLENGTH == 1) + goto GENERALPOLL; + + PollType = 0; + +// HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL + + if (TNC->TONODEBUFFER[1] == '0') + goto DATAONLY; + + STATUSPOLL(TNC, Channel); + +GENERALPOLL: + + if (STATUSPOLL(TNC, Channel)) + return TRUE; // Status was reported + +DATAONLY: + + if (DATAPOLL(TNC, Channel)) + return TRUE; // Data Sent + + goto SENDHOSTOK; // NOTHING DOING + + +SENDHOSTOK: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; +} + +int ConvertToDEDMonFormat(struct TNCDATA * TNC, char * Decoded, int Len, MESSAGE * Rawdata) +{ + // convert tnc2 format monitor to ded format + + unsigned char * MONCURSOR=0; + unsigned char MONHEADER[256]; + char * From, * To, * via, * ctl, *Context, *ptr, *iptr; + int pid, NR, NS, MonLen; + char rest[20]; + +/* + + From DEDHOST Documentation + + +Codes of 4 and 5 both signify a monitor header. This is a null-terminated +format message containing the + + fm {call} to {call} via {digipeaters} ctl {name} pid {hex} + +string that forms a monitor header. The monitor header is also identical to +the monitor header displayed in user mode. If the code was 4, the monitored +frame contained no information field, so the monitor header is all you get. If +you monitor KB6C responding to a connect request from me and then poll channel +0, you'll get: + + 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 + ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! + ! ! ! + ! +---- Code = 4 (Monitor, no info) Null termination ----+ + +------- Channel = 0 (Monitor info is always on channel 0) + + If the code was 5, the monitored frame did contain an information field. In +this case, another G command to channel 0 will return the monitored information +with a code of 6. Since data transmissions must be transparent, the monitored +information is passed as a byte-count format transmission. That is, it is +preceded by a count byte (one less than the number of bytes in the field). No +null terminator is used in this case. Since codes 4, 5, and 6 pertain to +monitored information, they will be seen only on channel 0. If you hear KB6C +say "Hi" to NK6K, and then poll channel 0, you'll get: + + 0005666D204B42364320746F204E4B364B2063746C204930302070494420463000 + ! ! f m K B 6 C t o N K 6 K c t l I 0 0 p i d F 0 ! + ! ! ! ! ! + ! ! or whatever ----+-+ ! + ! ! ! + ! +---- Code = 5 (Monitor, info follows) Null termination ----+ + +------ Channel = 0 (Monitor info is always on channel 0) + +and then the very next poll to channel 0 will get: + + 00 06 02 48 69 0D + ! ! ! H i CR + ! ! ! ! + ! ! ! +---- (this is a data byte) + ! ! +---- Count = 2 (three bytes of data) + ! +------- Code = 6 (monitored information) + +---------- Channel = 0 (Monitor info is always on channel 0) + + +*/ + + iptr = strchr(&Decoded[10], ':'); // Info if present + + MONHEADER[0] = 4; // NO DATA FOLLOWS + MONCURSOR = &MONHEADER[1]; + + if (strstr(Decoded, "NET/ROM") || strstr(Decoded, "NODES br") || strstr(Decoded, "INP3 RIF")) + pid = 0xcf; + else + pid = 0xf0; + + From = strtok_s(&Decoded[10], ">", &Context); + To = strtok_s(NULL, " ", &Context); + + via = strlop(To, ','); + + Context = strchr(Context, '<'); + if (Context == 0) + return 0; + + ctl = strtok_s(NULL, ">", &Context); + + if (via) + MONCURSOR += sprintf(MONCURSOR, "fm %s to %s via %s ctl ", From, To, via); + else + MONCURSOR += sprintf(MONCURSOR, "fm %s to %s ctl ", From, To); + + rest[0] = 0; + + switch (ctl[1]) + { + case 'R': + + NR = ctl[strlen(ctl)-1] - 48; + strlop(ctl, ' '); + sprintf(rest, "%s%d", &ctl[1], NR); + break; + + case 'I': + + ptr = strchr(ctl, 'S'); + if (ptr) NS = ptr[1] - 48; + ptr = strchr(ctl, 'R'); + if (ptr) NR = ptr[1] - 48; + sprintf(rest, "I%d%d pid %X", NS, NR, pid); + + if (pid == 0xcf) + { + // NETROM - pass the raw data + + MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame + memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); + + MONHEADER[0] = 5; // Data to follow + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + TNC->MONLENGTH = MonLen + 2; + TNC->MONBUFFER[1] = (MonLen - 1); + } + else + { + if (iptr) + { + iptr += 2; // Skip colon and cr + MonLen = Len - (int)(iptr - Decoded); + if (MonLen > 256) + MonLen = 256; + + memcpy(&TNC->MONBUFFER[2], iptr, MonLen); + + + if (MonLen == 0) // No data + { + MONHEADER[0] = 4; // No Data to follow + TNC->MONFLAG = 0; // No Data to follow + } + else + { + MONHEADER[0] = 5; // Data to follow + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + TNC->MONLENGTH = MonLen + 2; + TNC->MONBUFFER[1] = (MonLen - 1); + } + } + } + break; + + case 'C': + + strcpy(rest, "SABM"); + break; + + case 'D': + + if (ctl[1] == 'M') + strcpy(rest, "DM"); + else + strcpy(rest, "DISC"); + + break; + + case 'U': + + if (ctl[2] == 'A') + strcpy(rest, "UA"); + else + { + // UI + + size_t MonLen;; + + MONHEADER[0] = 5; // Data to follow + sprintf(rest, "UI pid %X", pid); + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + + if (pid ==0xcf) + { + // NETROM - pass th raw data + + MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame + memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); + } + else + { + ptr = strchr(Context, ':'); + + if (ptr == 0) + { + TNC->MONFLAG = 0; + return 0; + } + + ptr += 2; // Skip colon and cr + MonLen = Len - (ptr - Decoded); + memcpy(&TNC->MONBUFFER[2], ptr, MonLen); + } + + if (MonLen == 0) // No data + { + MONHEADER[0] = 4; // No Data to follow + TNC->MONFLAG = 0; // No Data to follow + } + else + { + TNC->MONLENGTH = (int)MonLen + 2; + TNC->MONBUFFER[1] = (int)(MonLen - 1); + } + break; + } + + default: + rest[0] = 0; + } + + MONCURSOR += sprintf(MONCURSOR, "%s", rest); + + if (MONCURSOR == &MONHEADER[1]) + return 0; // NOTHING DOING + + *MONCURSOR++ = 0; // NULL TERMINATOR + + SENDCMDREPLY(TNC, MONHEADER, (int)(MONCURSOR - &MONHEADER[0])); + return 1; +} + +// GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED +/* + +static char CTL_MSG[]=" ctl "; +static char VIA_MSG[]=" via "; +static char PID_MSG[]=" pid "; +static char SABM_MSG[]="SABM"; +static char DISC_MSG[]="DISC"; +static char UA_MSG[]="UA"; + +static char DM_MSG []="DM"; +static char RR_MSG []="RR"; +static char RNR_MSG[]="RNR"; +static char I_MSG[]="I pid "; +static char UI_MSG[]="UI pid "; +static char FRMR_MSG[]="FRMR"; +static char REJ_MSG[]="REJ"; + + PUSH EDI + MOV ECX,8 ; MAX DIGIS +CTRLLOOP: + TEST BYTE PTR (MSGCONTROL-1)[EDI],1 + JNZ CTRLFOUND + + ADD EDI,7 + LOOP CTRLLOOP +; +; INVALID FRAME +; + POP EDI + RET + +CTRLFOUND: + MOV AL,MSGCONTROL[EDI] + + and AL,NOT PFBIT ; Remove P/F bit + mov FRAME_TYPE,AL + + + POP EDI +; + TEST AL,1 ; I FRAME + JZ IFRAME + + CMP AL,3 ; UI + JE OKTOTRACE ; ALWAYS DO UI + + CMP AL,FRMR + JE OKTOTRACE ; ALWAYS DO FRMR +; +; USEQ/CONTROL - TRACE IF MCOM ON +; + CMP MCOM,0 + JNE OKTOTRACE + + RET + +;-----------------------------------------------------------------------------; +; Check for MALL ; +;-----------------------------------------------------------------------------; + +IFRAME: + cmp MALL,0 + jne OKTOTRACE + + ret + +OKTOTRACE: +; +;-----------------------------------------------------------------------------; +; Get the port number of the received frame ; +;-----------------------------------------------------------------------------; +; +; CHECK FOR PORT SELECTIVE MONITORING +; + MOV CL,MSGPORT[EDI] + mov PORT_NO,CL + + DEC CL + MOV EAX,1 + SHL EAX,CL ; SHIFT BIT UP + + TEST MMASK,EAX + JNZ TRACEOK1 + + RET + +TRACEOK1: + + MOV FRMRFLAG,0 + push EDI + mov AH,MSGDEST+6[EDI] + mov AL,MSGORIGIN+6[EDI] + +; +; Display Origin Callsign ; +; + +; 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 +; ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! +; ! ! ! +; ! +---- Code = 4 (Monitor, no info) Null termination ----+ + ; +------- Channel = 0 (Monitor info is always on channel 0) + + mov ESI,OFFSET FM_MSG + call NORMSTR + + lea ESI,MSGORIGIN[EDI] + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EDI + push EDI + + mov ESI,OFFSET TO_MSG + call NORMSTR +; +; Display Destination Callsign ; +; + lea ESI,MSGDEST[EDI] + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EDI + push EDI + + mov AX,MMSGLENGTH[EDI] + mov FRAME_LENGTH,AX + mov ECX,8 ; Max number of digi-peaters +; +; Display any Digi-Peaters ; +; + test MSGORIGIN+6[EDI],1 + jnz NO_MORE_DIGIS + + mov ESI,OFFSET VIA_MSG + call NORMSTR + jmp short skipspace + +NEXT_DIGI: + test MSGORIGIN+6[EDI],1 + jnz NO_MORE_DIGIS + + mov AL,' ' + call MONPUTCHAR +skipspace: + add EDI,7 + sub FRAME_LENGTH,7 ; Reduce length + + push EDI + push ECX + lea ESI,MSGORIGIN[EDI] + call CONVFROMAX25 ; Convert to call + + push EAX ; Last byte is in AH + + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EAX + + test AH,80H + jz NOT_REPEATED + + mov AL,'*' + call MONPUTCHAR + +NOT_REPEATED: + pop ECX + pop EDI + loop NEXT_DIGI + +NO_MORE_DIGIS: + +;----------------------------------------------------------------------------; +; Display ctl ; +;----------------------------------------------------------------------------; + + mov ESI,OFFSET CTL_MSG + call NORMSTR + +;-----------------------------------------------------------------------------; +; Start displaying the frame information ; +;-----------------------------------------------------------------------------; + + + mov INFO_FLAG,0 + + mov AL,FRAME_TYPE + + test AL,1 + jne NOT_I_FRAME + +;-----------------------------------------------------------------------------; +; Information frame ; +;-----------------------------------------------------------------------------; + + mov AL,'I' + call MONPUTCHAR + mov INFO_FLAG,1 + + mov ESI,OFFSET I_MSG + call NORMSTR + + lea ESI,MSGPID[EDI] + lodsb + + call BYTE_TO_HEX + + + jmp END_OF_TYPE + +NOT_I_FRAME: + +;-----------------------------------------------------------------------------; +; Un-numbered Information Frame ; +;-----------------------------------------------------------------------------; + + cmp AL,UI + jne NOT_UI_FRAME + + mov ESI,OFFSET UI_MSG + call NORMSTR + + lea ESI,MSGPID[EDI] + lodsb + + call BYTE_TO_HEX + + mov INFO_FLAG,1 + jmp END_OF_TYPE + +NOT_UI_FRAME: + test AL,10B + jne NOT_R_FRAME + +;-----------------------------------------------------------------------------; +; Process supervisory frames ; +;-----------------------------------------------------------------------------; + + + and AL,0FH ; Mask the interesting bits + cmp AL,RR + jne NOT_RR_FRAME + + mov ESI,OFFSET RR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RR_FRAME: + cmp AL,RNR + jne NOT_RNR_FRAME + + mov ESI,OFFSET RNR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RNR_FRAME: + cmp AL,REJ + jne NOT_REJ_FRAME + + mov ESI,OFFSET REJ_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_REJ_FRAME: + mov AL,'?' ; Print "?" + call MONPUTCHAR + jmp SHORT END_OF_TYPE + +; +; Process all other frame types ; +; + +NOT_R_FRAME: + cmp AL,UA + jne NOT_UA_FRAME + + mov ESI,OFFSET UA_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_UA_FRAME: + cmp AL,DM + jne NOT_DM_FRAME + + mov ESI,OFFSET DM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DM_FRAME: + cmp AL,SABM + jne NOT_SABM_FRAME + + mov ESI,OFFSET SABM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_SABM_FRAME: + cmp AL,DISC + jne NOT_DISC_FRAME + + mov ESI,OFFSET DISC_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DISC_FRAME: + cmp AL,FRMR + jne NOT_FRMR_FRAME + + mov ESI,OFFSET FRMR_MSG + call NORMSTR + MOV FRMRFLAG,1 + jmp SHORT END_OF_TYPE + +NOT_FRMR_FRAME: + mov AL,'?' + call MONPUTCHAR + +END_OF_TYPE: + + CMP FRMRFLAG,0 + JE NOTFRMR +; +; DISPLAY FRMR BYTES +; + lea ESI,MSGPID[EDI] + MOV CX,3 ; TESTING +FRMRLOOP: + lodsb + CALL BYTE_TO_HEX + + LOOP FRMRLOOP + + JMP NO_INFO + +NOTFRMR: + + MOVZX ECX,FRAME_LENGTH + + + cmp INFO_FLAG,1 ; Is it an information packet ? + jne NO_INFO + + + XOR AL,AL ; IN CASE EMPTY + + sub ECX,23 + CMP ecx,0 + je NO_INFO ; EMPTY I FRAME + +; +; PUT DATA IN MONBUFFER, LENGTH IN MONLENGTH +; + + pushad +} + TNC->MONFLAG = 1; + + _asm{ + + popad + + MOV MONHEADER,5 ; DATA FOLLOWS + + cmp ECX,257 + jb LENGTH_OK +; + mov ECX,256 +; +LENGTH_OK: +; + mov MonDataLen, ECX + + pushad + + } + + TNC->MONBUFFER[1] = MonDataLen & 0xff; + TNC->MONBUFFER[1]--; + + + TNC->MONLENGTH = MonDataLen+2; + + ptr1=&TNC->MONBUFFER[2]; + + _asm{ + + popad + + MOV EDI,ptr1 + +MONCOPY: + LODSB + CMP AL,7 ; REMOVE BELL + JNE MONC00 + + MOV AL,20H +MONC00: + STOSB + + LOOP MONCOPY + + POP EDI + RET + +NO_INFO: +; +; ADD CR UNLESS DATA ALREADY HAS ONE +; + CMP AL,CR + JE NOTANOTHER + + mov AL,CR + call MONPUTCHAR + +NOTANOTHER: +; + pop EDI + ret + +;----------------------------------------------------------------------------; +; Display ASCIIZ strings ; +;----------------------------------------------------------------------------; + +NORMSTR: + lodsb + cmp AL,0 ; End of String ? + je NORMSTR_RET ; Yes + call MONPUTCHAR + jmp SHORT NORMSTR + +NORMSTR_RET: + ret + +;-----------------------------------------------------------------------------; +; Display Callsign pointed to by SI ; +;-----------------------------------------------------------------------------; + +DISPADDR: + jcxz DISPADDR_RET + + lodsb + call MONPUTCHAR + + loop DISPADDR + +DISPADDR_RET: + ret + + +;-----------------------------------------------------------------------------; +; Convert byte in AL to nn format ; +;-----------------------------------------------------------------------------; + +DISPLAY_BYTE_2: + cmp AL,100 + jb TENS_2 + + sub AL,100 + jmp SHORT DISPLAY_BYTE_2 + +TENS_2: + mov AH,0 + +TENS_LOOP_2: + cmp AL,10 + jb TENS_LOOP_END_2 + + sub AL,10 + inc AH + jmp SHORT TENS_LOOP_2 + +TENS_LOOP_END_2: + push EAX + mov AL,AH + add AL,30H + call MONPUTCHAR + pop EAX + + add AL,30H + call MONPUTCHAR + + ret + +;-----------------------------------------------------------------------------; +; Convert byte in AL to Hex display ; +;-----------------------------------------------------------------------------; + +BYTE_TO_HEX: + push EAX + shr AL,1 + shr AL,1 + shr AL,1 + shr AL,1 + call NIBBLE_TO_HEX + pop EAX + call NIBBLE_TO_HEX + ret + +NIBBLE_TO_HEX: + and AL,0FH + cmp AL,10 + + jb LESS_THAN_10 + add AL,7 + +LESS_THAN_10: + add AL,30H + call MONPUTCHAR + ret + + + +CONVFROMAX25: +; +; CONVERT AX25 FORMAT CALL IN [SI] TO NORMAL FORMAT IN NORMCALL +; RETURNS LENGTH IN CX AND NZ IF LAST ADDRESS BIT IS SET +; + PUSH ESI ; SAVE + MOV EDI,OFFSET NORMCALL + MOV ECX,10 ; MAX ALPHANUMERICS + MOV AL,20H + REP STOSB ; CLEAR IN CASE SHORT CALL + MOV EDI,OFFSET NORMCALL + MOV CL,6 +CONVAX50: + LODSB + CMP AL,40H + JE CONVAX60 ; END IF CALL - DO SSID + + SHR AL,1 + STOSB + LOOP CONVAX50 +CONVAX60: + POP ESI + ADD ESI,6 ; TO SSID + LODSB + MOV AH,AL ; SAVE FOR LAST BIT TEST + SHR AL,1 + AND AL,0FH + JZ CONVAX90 ; NO SSID - FINISHED +; + MOV BYTE PTR [EDI],'-' + INC EDI + CMP AL,10 + JB CONVAX70 + SUB AL,10 + MOV BYTE PTR [EDI],'1' + INC EDI +CONVAX70: + ADD AL,30H ; CONVERT TO DIGIT + STOSB +CONVAX90: + MOV ECX,EDI + SUB ECX,OFFSET NORMCALL + MOV NORMLEN,ECX ; SIGNIFICANT LENGTH + + TEST AH,1 ; LAST BIT SET? + RET + + +PUTCHAR: + + pushad + push eax + push TNC + call PUTCHARx + pop eax + pop eax + popad + ret +} +} +*/ + + + +VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len) +{ + int Paclen = 0; + + if (Len == 0) + return; + + if (BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION) + Paclen = BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION->SESSPACLEN; + + if (Paclen == 0) + goto nochange; // paclen not set + +fragloop: + + if (Len <= Paclen) + goto nochange; // msglen <= paclen + +// need to fragment + + SendMsg(Channel->BPQStream, Msg, Paclen); + + Msg += Paclen; + + Len -= Paclen; + + if (Len) + goto fragloop; + + return; + +nochange: + + SendMsg(Channel->BPQStream, Msg, Len); + return; +} + +int DOCOMMAND(struct TNCDATA * conn) +{ + char Errbuff[500]; + int i; + +// PROCESS NORMAL MODE COMMAND + + sprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",conn->ComPort, conn->TONODEBUFFER); + OutputDebugString(Errbuff); + +// IF ECHO ENABLED, ECHO IT + + if (conn->ECHOFLAG) + { + UCHAR * ptr = conn->TONODEBUFFER; + UCHAR c; + + do + { + c = *(ptr++); + + if (c == 0x1b) c = ':'; + + PUTCHARx(conn, c); + + } while (c != 13); + } + + if (conn->TONODEBUFFER[0] != 0x1b) + goto NOTCOMMAND; // DATA IN NORMAL MODE - IGNORE + + switch (toupper(conn->TONODEBUFFER[1])) + { + case 'J': + + if (conn->TONODEBUFFER[6] == 0x0d) + conn->MODE = 0; + else + conn->MODE = conn->TONODEBUFFER[6] & 1; + + if (conn->MODE) + EnableAppl(conn); + else + DisableAppl(conn); + + if (conn->MODE) + { + // send host mode ack + +// PUTCHARx(conn, 0); +// PUTCHARx(conn, 0); + + conn->CURSOR = (UCHAR *)&conn->TONODEBUFFER; + return 0; + } + + break; + + case 'E': + + conn->ECHOFLAG = conn->TONODEBUFFER[2] & 1; + break; + + case 'I': + { + // Save call + + char * Call = &conn->TONODEBUFFER[2]; + + *(conn->CURSOR - 2) = 0; + + for (i = 0; i <= conn->HOSTSTREAMS; i++) + { + strcpy(conn->Channels[i]->MYCall, Call); + } + + break;; + } + case 'P': + +// PARAMS COMMAND - RETURN FIXED STRING + + PARAMPORT = conn->TONODEBUFFER[2]; + + for (i=0; i < LPARAMREPLY; i++) + { + PUTCHARx(conn, PARAMREPLY[i]); + } + + break; + + case 'S': + case 'D': + + // Return Channel Not Connected + + PUTSTRING(conn, "* CHANNEL NOT CONNECTED *\r"); + + default: + + break; + + } + +// PUTCHARx(conn, 'c'); +// PUTCHARx(conn, 'm'); +// PUTCHARx(conn, 'd'); +// PUTCHARx(conn, ':'); +// PUTCHARx(conn, 13); + +NOTCOMMAND: + + conn->CURSOR = (UCHAR *)&conn->TONODEBUFFER; + + return 0; + +} + +VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len) +{ + int n; + + if (Len == 0) + return; + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (n = 0; n < Len; n++) + { + PUTCHARx(TNC, Msg[n]); + } +} + +int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + // Status Poll + + int State, Change, i; + char WorkString[256]; + + if (TNC->MSGCHANNEL == 0) // Monitor Chan + return 0; + + LocalSessionState(Channel->BPQStream, &State, &Change, TRUE); + + if (Change == 0) + return 0; + + // PORT HAS CONNECTED OR DISCONNECTED - SEND STATUS CHANGE TO PC + + if (State == 0) + { + // DISCONNECTED + + i = sprintf(CONMSG, "\x3(%d) DISCONNECTED fm 0:SWITCH\r", TNC->MSGCHANNEL); + i++; + } + else + { + // GET CALLSIGN + + GetCallsign(Channel->BPQStream, WorkString); + strlop(WorkString, ' '); + i = sprintf(CONMSG, "\x3(%d) CONNECTED to %s\r", TNC->MSGCHANNEL, WorkString); + i++; + } + + SENDCMDREPLY(TNC, CONMSG, i); + return 1; +} + +int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + unsigned char NODEBUFFER[300]; // MESSAGE FROM NODE + int Len, Count, i; + time_t stamp; + char * ptr1; + + if (TNC->MSGCHANNEL == 0) + { + // POLL FOR MONITOR DATA + + if (TNC->MONFLAG == 0) + goto NOMONITOR; + + // HAVE ALREADY GOT DATA PART OF MON FRAME OT SEND + + TNC->MONFLAG = 0; + + ptr1 = (UCHAR *)&TNC->MONBUFFER; + + if (TNC->MONLENGTH) + { + SENDCMDREPLY(TNC, ptr1, TNC->MONLENGTH); + return TRUE; + } + + OutputDebugString("BPQHOST Mondata Flag Set with no data"); + +NOMONITOR: + + // SEE IF ANYTHING TO MONITOR + + stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + + if (Len) + { + // Use Normal Decode, then reformat to DED standard + + ULONG SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + unsigned char Decoded[1000]; + + SetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); + Len = IntDecodeFrame(&MONITORDATA, Decoded, stamp, TNC->MMASK, FALSE, FALSE); + SetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (Len) + { + return ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA); + } + } + return 0; + } + + // Look for session data + + GetMsg(Channel->BPQStream, NODEBUFFER, &Len, &Count); + + if (Len == 0) + return 0; + + if (Len > 256) + { + Debugprintf("BPQHOST Corrupt Length = %d", Len); + return 0; + } + + // SEND DATA + + // If a failure, set a close timer (for Airmail, etc) + + NODEBUFFER[Len] = 0; // For strstr + + if (strstr(NODEBUFFER, "} Downlink connect needs port number") || + strstr(NODEBUFFER, "} Error - TNC Not Ready") || + strstr(NODEBUFFER, "} Failure with ") || + strstr(NODEBUFFER, "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + else + Channel->CloseTimer = 0; // Cancel Timer + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 7); + PUTCHARx(TNC, Len - 1); + + for (i = 0; i < Len; i++) + { + PUTCHARx(TNC, NODEBUFFER[i]); + } + + return 1; // HAVE SEND SOMETHING +} + + + + + + + + + +// Kantronics Host Mode Stuff + +// Kantronics Host Mode Stuff + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + + +static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer); +VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int DoReceivedData(struct TNCDATA * conn, struct StreamInfo * channel); + + + +VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + size_t NewLen; + + if (!conn->MODE) + { + // In Terminal Mode - Pass to Term Mode Handler + + ProcessKPacket(conn, rxbuffer, Len); + return; + } + + // Split into KISS Packets. By far the most likely is a single KISS frame + // so treat as special case + + if (!(rxbuffer[0] == FEND)) + { + // Getting Non Host Data in Host Mode - Appl will have to sort the mess + // Discard any data + + conn->RXBPtr = 0; + return; + } + + conn->RXBPtr = 0; // Assume we will use all data in buffer - will reset if part packet received + + FendPtr = memchr(&rxbuffer[1], FEND, Len-1); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessKHOSTPacket(conn, &rxbuffer[1], Len - 2); + return; + } + + if (FendPtr == NULL) + { + // We have a partial Packet - Save it + + conn->RXBPtr = Len; + memcpy(&conn->TOUSERBUFFER[0], rxbuffer, Len); + return; + } + + // Process the first Packet in the buffer + + NewLen = FendPtr - rxbuffer -1; + ProcessKHOSTPacket(conn, &rxbuffer[1], (int)NewLen ); + + // Loop Back + + ProcessPacket(conn, FendPtr+1, Len - (int)NewLen -2); + return; + +} + +VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + UCHAR Char; + UCHAR * cmdStart; + int cmdlen = 0; + + // we will often get a whole connamd at once, but may not, so be prepared to receive char by char + // Could also get more than one command per packet + + cmdStart = rxbuffer; + + if (rxbuffer[0] == FEND && rxbuffer[Len-1] == FEND) + { + // Term thinks it is hosr mode + + // Unless it is FEND FF FEND (exit KISS) or FEND Q FEND (exit host) + + if (rxbuffer[2] == FEND) + { + if (rxbuffer[1] == 255 || rxbuffer[1] == 'q') + { + // If any more , process it. + + if (Len == 3) + return; + + Len -= 3; + rxbuffer+= 3; + ProcessKPacket(conn, rxbuffer, Len); + return; + } + } + conn->MODE = 1; + return; + } + + while (Len > 0) + { + Char = *(rxbuffer++); + Len--; + cmdlen++; + +// if (conn->TermPtr > 120) conn->TermPtr = 120; // Prevent overflow + + if (conn->ECHOFLAG) BPQSerialSendData(conn, &Char, 1); + + if (Char == 0x0d) + { + // We have a command line + + *(rxbuffer-1) = 0; + ProcessKNormCommand(conn, cmdStart); + conn->RXBPtr -= cmdlen; + cmdlen = 0; + cmdStart = rxbuffer; + } + } +} + +VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer) +{ +// UCHAR CmdReply[]="C00"; + UCHAR ResetReply[] = "\xC0\xC0S00\xC0"; + int Len; + + char seps[] = " \t\r"; + char * Command, * Arg1, * Arg2; + char * Context; + + if (conn->Channels[1]->Connected) + { + Len = (int)strlen(rxbuffer); + rxbuffer[Len] = 0x0d; + SendMsg(conn->Channels[1]->BPQStream, rxbuffer, Len+1); + return; + } + + Command = strtok_s(rxbuffer, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + Arg2 = strtok_s(NULL, seps, &Context); + + if (Command == NULL) + { + BPQSerialSendData(conn, "cmd:", 4); + return; + } + + if (_stricmp(Command, "RESET") == 0) + { + if (conn->nextMode) + BPQSerialSendData(conn, ResetReply, 6); + else + BPQSerialSendData(conn, "cmd:", 4); + + + conn->MODE = conn->nextMode; + + if (conn->MODE) + EnableAppl(conn); + else + DisableAppl(conn); + + return; + } + + if (_stricmp(Command, "K") == 0) + { + int SaveAuthProg = AuthorisedProgram; + + AuthorisedProgram = 1; + SessionControl(conn->Channels[1]->BPQStream, 1, 0); + AuthorisedProgram = SaveAuthProg; + + return; + } + + if (_memicmp(Command, "IN", 2) == 0) + { + if (Arg1) + { + if (_stricmp(Arg1, "HOST") == 0) + conn->nextMode = TRUE; + else + conn->nextMode = FALSE; + } + + BPQSerialSendData(conn, "INTFACE was TERMINAL\r", 21); + BPQSerialSendData(conn, "cmd:", 4); + return; + } + +//cmd: + +//INTFACE HOST +//INTFACE was TERMINAL +//cmd:RESET +//ÀÀS00À +//ÀC20XFLOW OFFÀ + + + //SendKISSData(conn, CmdReply, 3); + + BPQSerialSendData(conn, "cmd:", 4); + + + // Process Non-Hostmode Packet + + return; +} +int FreeBytes = 999; + +static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + struct StreamInfo * channel; + UCHAR Command[80]; + UCHAR Reply[400]; + UCHAR TXBuff[400]; + UCHAR CmdReply[]="C00"; + + UCHAR Chan, Stream; + int i, j, TXLen, StreamNo; + + char * Cmd, * Arg1, * Arg2, * Arg3; + char * Context; + char seps[] = " \t\r\xC0"; + int CmdLen; + + if ((Len == 1) && ((rxbuffer[0] == 'q') || (rxbuffer[0] == 'Q'))) + { + // Force Back to Command Mode + + Sleep(3000); + conn->MODE = FALSE; + BPQSerialSendData(conn, "\r\r\rcmd:", 7); + return; + } + + if (rxbuffer[0] == '?') + { + // what is this ??? + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + } + + Chan = rxbuffer[1]; + Stream = rxbuffer[2]; + + StreamNo = Stream == '0' ? 0 : Stream - '@'; + + if (StreamNo > conn->HOSTSTREAMS) + { + SendKISSData(conn, "C00Invalid Stream", 17); + return; + } + + switch (rxbuffer[0]) + { + case 'C': + + // Command Packet. Extract Command + + if (Len > 80) Len = 80; + + memcpy(Command, &rxbuffer[3], Len-3); + Command[Len-3] = 0; + + Cmd = strtok_s(Command, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + if (_stricmp(Cmd, "S") == 0) + { + // Status + + FreeBytes = 2000; + + // Ideally I should do flow control by channel, but Paclink (at least) doesn't have a mechanism + + for (i = 1; i < conn->HOSTSTREAMS; i++) + { + if (conn->Channels[i]->Connected) + if (TXCount(conn->Channels[1]->BPQStream) > 10) + FreeBytes = 0; + } + + // This format works with Paclink and RMS Packet, but it doesn't seem to conform to the spec + + // I think maybe the Channel status should be in the same Frame. + + TXLen = sprintf(Reply, "C00FREE BYTES %d\r", FreeBytes); + SendKISSData(conn, Reply, TXLen); + + for (j=1; j <= conn->HOSTSTREAMS; j++) + { + channel = conn->Channels[j]; + + if (channel->Connected) + { + TXLen = sprintf(Reply, "C00%c/V stream - CONNECTED to %s", j + '@', "SWITCH"); + SendKISSData(conn, Reply, TXLen); + } +// else +// TXLen = sprintf(Reply, "C00%c/V stream - DISCONNECTED", j + '@'); + + } + return; + } + + if (_memicmp(Cmd, "C", CmdLen) == 0) + { + int Port; + struct StreamInfo * channel; + int BPQStream; + UCHAR * MYCall; + + // Connect. If command has a via string and first call is numeric use it as a port number + + if (StreamNo == 0) + { + Stream = 'A'; + StreamNo = 1; + } + + if (Arg2 && Arg3) + { + if (_memicmp(Arg2, "via", (int)strlen(Arg2)) == 0) + { + // Have a via string as 2nd param + + Port = atoi(Arg3); + { + if (Port > 0) // First Call is numeric + { + if (strlen(Context) > 0) // More Digis + TXLen = sprintf(TXBuff, "c %s %s v %s\r", Arg3, Arg1, Context); + else + TXLen = sprintf(TXBuff, "c %s %s\r", Arg3, Arg1); + } + else + { + // First Call Isn't Numeric. This won't work, as Digis without a port is invalid + + SendKISSData(conn, "C00Invalid via String (First must be Port)", 42); + return; + + } + } + } + else + TXLen = sprintf(TXBuff, "%s %s %s %s %s\r", Cmd, Arg1, Arg2, Arg3, Context); + + } + else + { + TXLen = sprintf(TXBuff, "C %s\r", Arg1); + } + + Reply[0] = 'C'; + Reply[1] = Chan; + Reply[2] = Stream; + SendKISSData(conn, Reply, 3); + + channel = conn->Channels[StreamNo]; + BPQStream = channel->BPQStream; + MYCall = (UCHAR *)&channel->MYCall; + + Connect(BPQStream); + + if (MYCall[0] > 0) + { + ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); + } + + SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); + + return; + + } + + if (_stricmp(Cmd, "D") == 0) + { + // Disconnect + + if (StreamNo == 0) + { + Stream = 'A'; + StreamNo = 1; + } + + SessionControl(conn->Channels[StreamNo]->BPQStream, 2, 0); + return; + } + + if (_memicmp(Cmd, "INT", 3) == 0) + { + SendKISSData(conn, "C00INTFACE HOST", 15); + return; + } + + if (_stricmp(Cmd, "PACLEN") == 0) + { + SendKISSData(conn, "C00PACLEN 128/128", 17); + return; + } + + if (_memicmp(Cmd, "MYCALL", CmdLen > 1 ? CmdLen : 2) == 0) + { + if (strlen(Arg1) < 30) + strcpy(conn->Channels[StreamNo]->MYCall, Arg1); + } + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + + case 'D': + + // Data to send + + + if (StreamNo > conn->HOSTSTREAMS) return; // Protect + + TXLen = KissDecode(&rxbuffer[3], TXBuff, Len-3); + SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); + + conn->Channels[StreamNo]->CloseTimer = 0; // Cancel Timer + + return; + + default: + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + } +} + +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + for (i=0;iBPQStream, ConnectingCall); + strlop(ConnectingCall, ' '); + + if (conn->MODE) + { + Len = sprintf (Msg, "S1%c*** CONNECTED to %s ", Stream + '@', ConnectingCall); + SendKISSData(conn, Msg, Len); + } + else + { + Len = sprintf (Msg, "*** CONNECTED to %s\r", ConnectingCall); + BPQSerialSendData(conn, Msg, Len); + BPQSerialSetDCD(conn->hDevice); + } + + channel->Connected = TRUE; + + return 0; + +} + +int KANTDisconnected (struct TNCDATA * conn, struct StreamInfo * channel, int Stream) +{ + UCHAR Msg[50]; + int Len; + + if (conn->MODE) + { + Len = sprintf (Msg, "S1%c*** DISCONNECTED", Stream + '@'); + SendKISSData(conn, Msg, Len); + } + else + { + BPQSerialSendData(conn, "*** DISCONNECTED\r", 17); + BPQSerialClrDCD(conn->hDevice); + } + + channel->Connected = FALSE; + channel->CloseTimer = 0; + + return 0; +} + +// SCS Mode Stuff + +unsigned short int compute_crc(unsigned char *buf,int len); +VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len); +int APIENTRY ChangeSessionPaclen(int Stream, int Paclen); + +int EmUnstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len) +{ + int i, j=0; + + for (i=0; iOutgoingCall[0]) + strcpy(ConnectedCall, Channel->OutgoingCall); + else + GetCallsign(BPQStream, ConnectedCall); + + SCSReply[2] = HostStream; + SCSReply[3] = 3; + ReplyLen = sprintf(&SCSReply[4], "(%d) CONNECTED to %s", Channel->BPQStream, ConnectedCall); + ReplyLen += 5; + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + Channel->Connected = TRUE; + return TRUE; + } + // Disconnected + + SCSReply[2] = HostStream; + SCSReply[3] = 3; + ReplyLen = sprintf(&SCSReply[4], "(%d) DISCONNECTED fm G8BPQ", Channel->BPQStream); + ReplyLen += 5; // Include Null + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + Channel->Connected = FALSE; + return TRUE; + } + return FALSE; +} + +BOOL SCSCheckForData(struct TNCDATA * conn, struct StreamInfo * Channel, int HostStream, int BPQStream) +{ + int Length, Count; + + GetMsg(BPQStream, &SCSReply[5], &Length, &Count); + + if (Length == 0) + return FALSE; + + if (strstr(&SCSReply[5], "} Downlink connect needs port number") || + strstr(&SCSReply[5], "} Failure with ") || + strstr(&SCSReply[5], "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + else + Channel->CloseTimer = 0; // Cancel Timer + + SCSReply[2] = HostStream; + SCSReply[3] = 7; + SCSReply[4] = Length - 1; + + ReplyLen = Length + 5; + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + return TRUE; +} + +VOID ProcessSCSHostFrame(struct TNCDATA * conn, UCHAR * Buffer, int Length) +{ + int Channel = Buffer[0]; + int Command = Buffer[1] & 0x3f; + int Len = Buffer[2]; + struct StreamInfo * channel; + UCHAR TXBuff[400]; + int BPQStream; + char * MYCall; + UCHAR Stream; + int TXLen, i; + BPQVECSTRUC * HOST; + + // SCS Channel 31 is the Pactor channel, mapped to the first stream + + if (Channel == 0) + Stream = -1; + else + if (Channel == 31) + Stream = 0; + else + Stream = Channel; + + channel = conn->Channels[Stream]; + + if (conn->Toggle == (Buffer[1] & 0x80) && (Buffer[1] & 0x40) == 0) + { + // Repeat Condition + + //EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + //return; + } + + conn->Toggle = (Buffer[1] & 0x80); + conn->Toggle ^= 0x80; + +// if (Channel == 255 && Len == 0) + if (Channel == 255) + { + UCHAR * NextChan = &SCSReply[4]; + + // General Poll + + // See if any channels have anything avaiilable + + for (i = 1; i <= conn->HOSTSTREAMS; i++) + { + channel = conn->Channels[i]; + HOST = &BPQHOSTVECTOR[channel->BPQStream - 1]; // API counts from 1 + + if ((HOST->HOSTFLAGS & 3)) + { + *(NextChan++) = i + 1; // Something for this channel + continue; + } + + if (RXCount(channel->BPQStream)) + *(NextChan++) = i + 1; // Something for this channel + } + + *(NextChan++) = 0; + + SCSReply[2] = 255; + SCSReply[3] = 1; + + ReplyLen = (int)(NextChan - &SCSReply[0]); + + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + return; + } + + if (Channel == 254) // Status + { + // Extended Status Poll + + //TNC->Streams[0].PTCStatus0 = Status; + // TNC->Streams[0].PTCStatus1 = PactorLevel; // Pactor Level 1-4 + // TNC->Streams[0].PTCStatus2 = Msg[7]; // Speed Level + // Offset = Msg[8]; + + SCSReply[2] = 254; + SCSReply[3] = 7; // Status + SCSReply[4] = 3; // Len -1 + + if (conn->Channels[0]->Connected) + { + SCSReply[5] = 0x0AA; + SCSReply[6] = 3; + SCSReply[7] = 5; + SCSReply[8] = 128; + } + else + { + SCSReply[5] = 0; + SCSReply[6] = 0; + SCSReply[7] = 0; + SCSReply[8] = 0; + } + ReplyLen = 9; + EmCRCStuffAndSend(conn, SCSReply, 9); + return; + } + + + if (Command == 0) + { + // Data Frame + + SendMsg(channel->BPQStream, &Buffer[3], Buffer[2]+ 1); + + goto AckIt; + } + + switch (Buffer[3]) + { + case 'J': // JHOST + + conn->MODE = FALSE; + DisableAppl(conn); + + return; + + case 'G': // Specific Poll + + if (CheckStatusChange(conn, channel, Channel, channel->BPQStream)) + return; // It has sent reply + + if (SCSCheckForData(conn, channel, Channel, channel->BPQStream)) + return; // It has sent reply + + SCSReply[2] = Channel; + SCSReply[3] = 0; + ReplyLen = 4; + EmCRCStuffAndSend(conn, SCSReply, 4); + return; + + case 'C': // Connect + + // Could be real, or just C to request status + + if (Channel == 0) + goto AckIt; + + if (Length == 0) + { + // STATUS REQUEST - IF CONNECTED, GET CALL + + return; + } + Buffer[Length - 2] = 0; + + // Save call for connected report + + if (Buffer[5] = '%' ) // Pactor long path? + { + TXLen = sprintf(TXBuff, "C %s\r", &Buffer[6]); + strcpy(channel->OutgoingCall, &Buffer[6]); + } + else + { + TXLen = sprintf(TXBuff, "C %s\r", &Buffer[5]); + strcpy(channel->OutgoingCall, &Buffer[5]); + } + + BPQStream = channel->BPQStream; + MYCall = &channel->MYCall[0]; + + if (MYCall[0] == 0) + MYCall = (char *)&conn->MYCALL; + + Connect(BPQStream); + if (MYCall[0] > 0) + { + ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); + } + + ChangeSessionPaclen(BPQStream, 100); + + SendMsg(BPQStream, TXBuff, TXLen); + + AckIt: + + SCSReply[2] = Channel; + SCSReply[3] = 0; + ReplyLen = 4; + EmCRCStuffAndSend(conn, SCSReply, 4); + return; + + case 'D': + + // Disconnect + + Disconnect(channel->BPQStream); + goto AckIt; + + case '%': + + // %X commands + + switch (Buffer[4]) + { + case 'V': // Version + + SCSReply[2] = Channel; + SCSReply[3] = 1; + strcpy(&SCSReply[4], "4.8 1.32"); + ReplyLen = 13; + EmCRCStuffAndSend(conn, SCSReply, 13); + + return; + case 'M': + + default: + + SCSReply[2] = Channel; + SCSReply[3] = 1; + SCSReply[4] = 0; + + ReplyLen = 5; + EmCRCStuffAndSend(conn, SCSReply, 5); + + return; + } + case '@': + default: + + SCSReply[2] = Channel; + SCSReply[3] = 1; + SCSReply[4] = 0; + + ReplyLen = 5; + EmCRCStuffAndSend(conn, SCSReply, 5); + } +} + + +VOID ProcessSCSTextCommand(struct TNCDATA * conn, char * Command, int Len) +{ + // Command to SCS in non-Host mode. + + // We can probably just dump anything but JHOST 4 and MYCALL + + if (Len == 1) + goto SendPrompt; // Just a CR + + Debugprintf("%s", Command); + + if (_memicmp(Command, "JHOST4", 6) == 0) + { + conn->MODE = TRUE; + conn->Toggle = 0; + EnableAppl(conn); + + return; + } + + if (_memicmp(Command, "TERM 4", 6) == 0) + conn->Term4Mode = TRUE; + + else if (_memicmp(Command, "T 0", 3) == 0) + conn->Term4Mode = FALSE; + + else if (_memicmp(Command, "PAC 4", 5) == 0) + conn->PACMode = TRUE; + + if (_memicmp(Command, "MYC", 3) == 0) + { + char * ptr = strchr(Command, ' '); + char MYResp[80]; + + Command[Len-1] = 0; // Remove CR + + if (ptr && (strlen(ptr) > 2)) + { + strcpy(conn->MYCALL, ++ptr); + } + + sprintf(MYResp, "\rMycall: >%s<", conn->MYCALL); + PUTSTRING(conn, MYResp); + } + + else if (_memicmp(Command, "SYS SERN", 8) == 0) + { + char SerialNo[] = "\r\nSerial number: 0100000000000000"; + PUTSTRING(conn, SerialNo); + } + else + { + char SerialNo[] = "\rXXXX"; + PUTSTRING(conn, SerialNo); + } + +SendPrompt: + + if (conn->PACMode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'p'); + PUTCHARx(conn, 'a'); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + + return; + } + + if (conn->Term4Mode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 4); + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + PUTCHARx(conn, 1); + } + else + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + } + + +/* + + if (conn->Term4Mode) + PUTCHARx(conn, 4); + + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + +*/ + return; +} + + +VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length) +{ + unsigned short crc; + char UnstuffBuffer[500]; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + +Loop: + + if (rxbuffer[0] != 170) + { + UCHAR *ptr; + int cmdlen; + + // Char Mode Frame I think we need to see CR on end (and we could have more than one in buffer + + // If we think we are in host mode, then to could be noise - just discard. + + if (conn->MODE) + { + conn->FROMUSERLEN = 0; + return; + } + + rxbuffer[Length] = 0; + + if (rxbuffer[0] == 0) + { + // Just ignore + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + return; + } + if (rxbuffer[0] == 0x1b) + { + // Just ignore (I think!) + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + return; + } + + if (rxbuffer[0] == 0x1e) + { + // Status POLL + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + PUTCHARx(conn, 30); + PUTCHARx(conn, 0x87); + if (conn->Term4Mode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 4); + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + PUTCHARx(conn, 1); + } + else + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + } + + + return; + } + ptr = strchr(rxbuffer, 13); + + if (ptr == 0) + return; // Wait for rest of frame + + ptr++; + + cmdlen = (int)(ptr - rxbuffer); + + // Complete Char Mode Frame + + conn->FROMUSERLEN -= cmdlen; // Ready for next frame + + ProcessSCSTextCommand(conn, rxbuffer, cmdlen); + + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, ptr, conn->FROMUSERLEN + 1); + + if (conn->Mode) + { + // now in host mode, so pass rest up a level + + ProcessSCSPacket(conn, conn->FROMUSERBUFFER, conn->FROMUSERLEN); + return; + } + goto Loop; + } + return; + } + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (rxbuffer[2] == 170) + { + // Retransmit Request + + conn->RXBPtr = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = EmUnstuff(&rxbuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + conn->FROMUSERLEN = 0; + return; // Ignore for now + } + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + conn->FROMUSERLEN = 0; // Ready for next frame + ProcessSCSHostFrame(conn, &UnstuffBuffer[2], Length); + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + + +VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + Msg[0] = 170; + Msg[1] = 170; + + BPQSerialSendData(conn, Msg, Len); +} + + + \ No newline at end of file diff --git a/TelnetV6.c b/TelnetV6.c new file mode 100644 index 0000000..b03cbb0 --- /dev/null +++ b/TelnetV6.c @@ -0,0 +1,6897 @@ +/* +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 +*/ + +// +// Telnet Driver for BPQ Switch +// +// Uses BPQ EXTERNAL interface +// + +//#define WIN32_LEAN_AND_MEAN + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#include "kernelresource.h" + +#define IDM_DISCONNECT 2000 +#define IDM_LOGGING 2100 + +#define MAXBLOCK 4096 +#include "CHeaders.h" +#include "tncinfo.h" + +#ifdef WIN32 +#include +#include "WS2tcpip.h" +#else +//#define TIOCOUTQ 0x5411 +#define SIOCOUTQ TIOCOUTQ /* output queue size (not sent + not acked) */ +#endif + +#include "adif.h" +#include "telnetserver.h" + +#define MAX_PENDING_CONNECTS 4 + +extern UCHAR LogDirectory[]; + +extern char * PortConfig[]; + +static char ClassName[]="TELNETSERVER"; +static char WindowTitle[] = "Telnet Server"; +static int RigControlRow = 190; + +UCHAR * APIENTRY GetLogDirectory(); +static BOOL OpenSockets(struct TNCINFO * TNC); +static BOOL OpenSockets6(struct TNCINFO * TNC); +void ProcessHTTPMessage(void * conn); +static VOID SetupListenSet(struct TNCINFO * TNC); +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, unsigned long long Mask, BOOL APRS, BOOL MCTL); +DllExport int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly); +int WritetoConsoleLocal(char * buff); +BOOL TelSendPacket(int Stream, struct STREAMINFO * STREAM, PMSGWITHLEN buffptr, struct ADIF * ADIF); +int GetCMSHash(char * Challenge, char * Password); +int IsUTF8(unsigned char *cpt, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +VOID initUTF8(); +int TrytoGuessCode(unsigned char * Char, int Len); +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +extern BPQVECSTRUC * TELNETMONVECPTR; +BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived); +int DataSocket_ReadSync(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC); +int IntSetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly); +int DataSocket_ReadDRATS(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr); +void DRATSConnectionLost(struct ConnectionInfo * sockptr); +int BuildRigCtlPage(char * _REPLYBUFFER); + +#ifndef LINBPQ +extern HKEY REGTREE; +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HMENU hWndMenu; +extern HFONT hFont; +extern HBRUSH bgBrush; + +extern HWND ClientWnd, FrameWnd; + +extern HANDLE hInstance; +static RECT Rect; +#endif + +extern int REALTIMETICKS; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +#define MaxSockets 26 + +struct UserRec RelayUser; +struct UserRec SyncUser = {"","Sync"};; +struct UserRec CMSUser; +struct UserRec HostUser = {"","Host"}; +struct UserRec TriModeUser; + +static char AttemptsMsg[] = "Too many attempts - Disconnected\r\n"; +static char disMsg[] = "Disconnected by SYSOP\r\n"; + +static char BlankCall[]=" "; + +BOOL LogEnabled = FALSE; +BOOL CMSLogEnabled = TRUE; +extern BOOL IncludesMail; + +static HMENU hMenu, hPopMenu, hPopMenu2, hPopMenu3; // handle of menu + +static int ProcessLine(char * buf, int Port); +VOID __cdecl Debugprintf(const char * format, ...); +char * strlop(char * buf, char delim); + +#ifndef LINBPQ +LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +int DisplaySessions(struct TNCINFO * TNC); +int DoStateChange(int Stream); +int Connected(int Stream); +int Disconnected(int Stream); +//int DeleteConnection(con); +static int Socket_Accept(struct TNCINFO * TNC, SOCKET SocketId, int Port); +static int Socket_Data(struct TNCINFO * TNC, SOCKET SocketId,int error, int eventcode); +static int DataSocket_Read(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM); +int DataSocket_ReadFBB(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +int DataSocket_ReadRelay(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM); +int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +int DataSocket_Write(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET TCPSock); +int DataSocket_Disconnect(struct TNCINFO * TNC, struct ConnectionInfo * sockptr); +BOOL ProcessTelnetCommand(struct ConnectionInfo * sockptr, byte * Msg, int * Len); +int ShowConnections(struct TNCINFO * TNC); +int Terminate(); +int SendtoSocket(SOCKET TCPSock,char * Msg); +int WriteLog(char * msg); +VOID WriteCMSLog(char * msg); +byte * EncodeCall(byte * Call); +VOID SendtoNode(struct TNCINFO * TNC, int Stream, char * Msg, int MsgLen); +DllExport int APIENTRY GetNumberofPorts(); + +BOOL CheckCMS(struct TNCINFO * TNC); +int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, char * Host, int Port, BOOL FBB); +int CMSConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, int Stream); +int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Error); +BOOL ProcessConfig(); +VOID FreeConfig(); +VOID SaveCMSHostInfo(int port, struct TCPINFO * TCP, int CMSNo); +VOID GetCMSCachedInfo(struct TNCINFO * TNC); +BOOL CMSCheck(struct TNCINFO * TNC, struct TCPINFO * TCP); +VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst); +VOID ProcessTrimodeCommand(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, char * MsgPtr); +VOID ProcessTrimodeResponse(struct TNCINFO * TNC, struct STREAMINFO * STREAM, unsigned char * MsgPtr, int Msglen); +VOID ProcessTriModeDataMessage(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM); + + +static int LogAge = 13; + + + +#ifdef WIN32 + +int DeleteLogFile(char * Log); + +void DeleteTelnetLogFiles() +{ + DeleteLogFile("/logs/Telnet*.log"); + DeleteLogFile("/logs/CMSAccess_*.log"); + DeleteLogFile("/logs/ConnectLog_*.log"); +} + +int DeleteLogFile(char * Log) +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetLogDirectory()); + strcat(szDir, Log); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return dwError; + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (int)((now - ft.LowPart) / 86400); + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + Debugprintf("Deleting %s", File); + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return dwError; +} + +#else + +#include + +int TelFilter(const struct dirent * dir) +{ + return (memcmp(dir->d_name, "CMSAccess", 9) == 0 + || memcmp(dir->d_name, "Telnet", 6) == 0 + || memcmp(dir->d_name, "ConnectLog", 6) == 0) + && strstr(dir->d_name, ".log"); +} + +int DeleteTelnetLogFiles() +{ + struct dirent **namelist; + int n; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("logs", &namelist, TelFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + sprintf(FN, "logs/%s", namelist[n]->d_name); + if (stat(FN, &STAT) == 0) + { + Age = (now - STAT.st_mtime) / 86400; + + if (Age > LogAge) + { + Debugprintf("Deleting %s\n", FN); + unlink(FN); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + + + + +void BuffertoNode(struct ConnectionInfo * sockptr, char * MsgPtr, int InputLen) +{ + // Queue to Node. Data may arrive it large quatities, possibly exceeding node buffer capacity + + if (sockptr->FromHostBuffPutptr + InputLen > sockptr->FromHostBufferSize) + { + if (InputLen > 10000) + sockptr->FromHostBufferSize += InputLen; + else + sockptr->FromHostBufferSize += 10000; + + sockptr->FromHostBuffer = realloc(sockptr->FromHostBuffer, sockptr->FromHostBufferSize); + } + + memcpy(&sockptr->FromHostBuffer[sockptr->FromHostBuffPutptr], MsgPtr, InputLen); + + sockptr->FromHostBuffPutptr += InputLen; + sockptr->InputLen = 0; + + return; + } + +BOOL SendAndCheck(struct ConnectionInfo * sockptr, unsigned char * MsgPtr, int len, int flags) +{ + int err; + int sent = send(sockptr->socket, MsgPtr, len, flags); + + if (sent == len) + return TRUE; // OK + + err = WSAGetLastError(); + + Debugprintf("TCP Send Failed - Sent %d should be %d err %d - requeue data", sent, len, err); + + if (err == 10035 || err == 115 || err == 36) //EWOULDBLOCK + { + // Save unsent data + + if (sent == -1) // -1 means none sent + sent = 0; + + sockptr->ResendBuffer = malloc(len - sent); + sockptr->ResendLen = len - sent; + + memmove(sockptr->ResendBuffer, MsgPtr + sent, len - sent); + } + return FALSE; +} + +VOID SendPortsForMonitor(SOCKET sock, int Secure) +{ + UCHAR PortInfo[1500] = {0xff, 0xff}; + UCHAR * ptr = &PortInfo[2]; + char ID[31] = ""; + struct PORTCONTROL * PORT; + int i, n, p; + + // Sends the ID of each Port to TermTCP + + p = GetNumberofPorts(); + + if (IncludesMail && Secure) + p++; + + ptr += sprintf(ptr, "%d|", p); + + if (IncludesMail && Secure) + { + p--; + ptr += sprintf(ptr,"0 Mail Monitor|"); + } + + for (n=1; n <= p; n++) + { + PORT = GetPortTableEntryFromSlot(n); + + memcpy(ID, PORT->PORTDESCRIPTION, 30); + + for (i = 29; i; i--) + { + if (ID[i] != ' ') + break; + + ID[i] = 0; + } + + ptr += sprintf(ptr,"%d %s|", PORT->PORTNUMBER, ID); + } + + + send(sock, PortInfo, (int)(ptr - PortInfo), 0); +} + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + UCHAR * ptr1; + + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int len=510; + char errbuf[256]; + char * param; + char * value; + char *User, *Pwd, *UserCall, *Secure, * Appl; + int End = (int)strlen(buf) -1; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + strcpy(errbuf,buf); // save in case of error + + if (buf[End] == 10) + buf[End]=0; // remove newline + + if(buf[0] =='#') return (TRUE); // comment + + if(buf[0] ==';') return (TRUE); // comment + + ptr=strchr(buf,'='); + + if (!ptr) + ptr=strchr(buf,' '); + + if (!ptr) + return 0; + + if (TNCInfo[Port] == NULL) + { + TNC = TNCInfo[Port] = zalloc(sizeof(struct TNCINFO)); + TCP = TNC->TCPInfo = zalloc(sizeof (struct TCPINFO)); // Telnet Server Specific Data + + TCP->MaxSessions = 10; // Default Values + TNC->Hardware = H_TELNET; + TCP->IPV4 = TRUE; + strcpy(TCP->CMSServer, "cms.winlink.org"); + } + + TNC = TNCInfo[Port]; + TCP = TNC->TCPInfo; + + param=buf; + *(ptr)=0; + value=ptr+1; + + if (_stricmp(param, "IPV4") == 0) + TCP->IPV4 = atoi(value); + + else if (_stricmp(param, "IPV6") == 0) + TCP->IPV6 = atoi(value); + + else if (_stricmp(param, "CMS") == 0) + TCP->CMS = atoi(value); + + else if (_stricmp(param, "CMSPASS") == 0) + { + char Temp[80]; + + if (strlen(value) > 79) + value[79] = 0; + + strcpy(Temp, value); + strlop(Temp, 32); + strlop(Temp, 13); + strcpy(TCP->SecureCMSPassword, Temp); + + } + + else if (_stricmp(param, "CMSCALL") == 0) + { + if (strlen(value) > 9) + value[9] = 0; + strcpy(TCP->GatewayCall, value); + strlop(TCP->GatewayCall, 13); + _strupr(TCP->GatewayCall); + } + + else if (_stricmp(param, "CMSLOC") == 0) + { + if (strlen(value) > 9) + value[9] = 0; + strcpy(TCP->GatewayLoc, value); + strlop(TCP->GatewayLoc, 13); + _strupr(TCP->GatewayLoc); + } + + else if (_stricmp(param,"ReportHybrid") == 0) + TCP->ReportHybrid = atoi(value); + + else if (_stricmp(param, "HybridServiceCode") == 0) + { + TCP->HybridServiceCode = _strdup(value); + strlop(TCP->HybridServiceCode, 13); + strlop(TCP->HybridServiceCode, ';'); + _strupr(TCP->HybridServiceCode); + } + + else if (_stricmp(param, "HybridFrequencies") == 0) + { + TCP->HybridFrequencies = _strdup(value); + strlop(TCP->HybridFrequencies, 13); + strlop(TCP->HybridFrequencies, ' '); + _strupr(TCP->HybridFrequencies); + } + + else if (_stricmp(param, "HybridCoLocatedRMS") == 0) + { + TCP->HybridCoLocatedRMS = _strdup(value); + strlop(TCP->HybridCoLocatedRMS, 13); + strlop(TCP->HybridCoLocatedRMS, ' '); + _strupr(TCP->HybridCoLocatedRMS); + } + + else if (_stricmp(param,"LOGGING") == 0) + LogEnabled = atoi(value); + + else if (_stricmp(param,"CMSLOGGING") == 0) + CMSLogEnabled = atoi(value); + + else if (_stricmp(param,"DisconnectOnClose") == 0) + TCP->DisconnectOnClose = atoi(value); + + else if (_stricmp(param,"ReportRelayTraffic") == 0) + TCP->ReportRelayTraffic = atoi(value); + + else if (_stricmp(param,"SecureTelnet") == 0) + TCP->SecureTelnet = atoi(value); + + else if (_stricmp(param,"CloseOnDisconnect") == 0) + TCP->DisconnectOnClose = atoi(value); + + else if (_stricmp(param,"TCPPORT") == 0) + TCP->TCPPort = atoi(value); + + else if (_stricmp(param,"DRATSPORT") == 0) + TCP->DRATSPort = atoi(value); + + else if (_stricmp(param,"TRIMODEPORT") == 0) + TCP->TriModePort = atoi(value); + + else if (_stricmp(param,"HTTPPORT") == 0) + TCP->HTTPPort = atoi(value); + + else if (_stricmp(param,"SYNCPORT") == 0) + TCP->SyncPort = atoi(value); + + else if ((_stricmp(param,"CMDPORT") == 0) || (_stricmp(param,"LINUXPORT") == 0)) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", ", &context); + + while (ptr && n < 33) + { + TCP->CMDPort[n++] = atoi(ptr); + ptr = strtok_s(NULL, ", ", &context); + } + } + + else if (_stricmp(param,"CMSSERVER") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", \r", &context); + + strcpy(TCP->CMSServer, ptr); + } + + else if (_stricmp(param,"RELAYHOST") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", \r", &context); + + strcpy(TCP->RELAYHOST, ptr); + } + + + else if (_stricmp(param,"FALLBACKTORELAY") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", \r", &context); + + TCP->FallbacktoRelay = atoi(value); + } + + else if (_stricmp(param,"FBBPORT") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", ", &context); + + while (ptr && n < 99) + { + TCP->FBBPort[n++] = atoi(ptr); + ptr = strtok_s(NULL, ", ", &context); + } + } + + else if (_stricmp(param,"RELAYPORT") == 0) + TCP->RelayPort = atoi(value); + + else if (_stricmp(param,"RELAYAPPL") == 0) + { + if (TCP->RelayPort == 0) + TCP->RelayPort = 8772; + strcpy(TCP->RelayAPPL, value); + strcat(TCP->RelayAPPL, "\r"); + } + + else if (_stricmp(param,"SYNCAPPL") == 0) + { + if (TCP->SyncPort == 0) + TCP->SyncPort = 8780; + strcpy(TCP->SyncAPPL, value); + strcat(TCP->SyncAPPL, "\r"); + } + + // if (strcmp(param,"LOGINRESPONSE") == 0) cfgLOGINRESPONSE = value; + // if (strcmp(param,"PASSWORDRESPONSE") == 0) cfgPASSWORDRESPONSE = value; + + else if (_stricmp(param,"LOGINPROMPT") == 0) + { + ptr1 = strchr(value, 13); + if (ptr1) *ptr1 = 0; + strcpy(TCP->LoginMsg,value); + } + + else if (_stricmp(param,"PASSWORDPROMPT") == 0) + { + ptr1 = strchr(value, 13); + if (ptr1) *ptr1 = 0; + strcpy(TCP->PasswordMsg, value); + } + + else if (_stricmp(param,"HOSTPROMPT") == 0) + strcpy(TCP->cfgHOSTPROMPT, value); + + else if (_stricmp(param,"LOCALECHO") == 0) + strcpy(TCP->cfgLOCALECHO, value); + + else if (_stricmp(param,"MAXSESSIONS") == 0) + { + TCP->MaxSessions = atoi(value); + if (TCP->MaxSessions > MaxSockets ) TCP->MaxSessions = MaxSockets; + } + + else if (_stricmp(param,"CTEXT") == 0 ) + { + int len = (int)strlen (value); + + if (value[len -1] == ' ') + value[len -1] = 0; + + strcpy(TCP->cfgCTEXT, value); + + // Replace \n Signon string with cr lf + + ptr = &TCP->cfgCTEXT[0]; + +scanCTEXT: + + ptr = strstr(ptr, "\\n"); + + if (ptr) + { + *(ptr++)=13; // put in cr + *(ptr++)=10; // put in lf + + goto scanCTEXT; + } + } + + else if (_stricmp(param,"LOCALNET") == 0) + { + uint32_t Network, Mask; + uint32_t IPMask; + char * Netptr, * MaskPtr, *Context; + struct LOCALNET * LocalNet; + int Bits = 24; + + Netptr = strtok_s(value, " /;", &Context); + + if (Netptr) + { + Network = inet_addr(Netptr); + MaskPtr = strtok_s(NULL, " /;", &Context); + + if (MaskPtr) + { + Bits = atoi(MaskPtr); + + if (Bits > 32) + Bits = 32; + } + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + Mask = htonl(IPMask); // Needs to be Network order + + LocalNet = (struct LOCALNET *)zalloc(sizeof(struct LOCALNET)); + + LocalNet->Network = Network; + LocalNet->Mask = Mask; + LocalNet->Next = TNC->TCPInfo->LocalNets; + + TNC->TCPInfo->LocalNets = LocalNet; + + } + } + + + + + else if (_stricmp(param,"USER") == 0) + { + struct UserRec * USER; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + + // USER=user,password,call,appl,SYSOP + + memset(Param, 0, 2048); + strlop(value, 13); + strlop(value, ';'); + + ptr1 = value; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + } + + + User = &Param[0][0]; + + if (_stricmp(User, "ANON") == 0) + { + strcpy(&Param[2][0], "ANON"); + strcpy(&Param[4][0], ""); // Dont allow SYSOP if ANON + } + + Pwd = &Param[1][0]; + UserCall = &Param[2][0]; + Appl = &Param[3][0]; + Secure = &Param[4][0]; + + if (User[0] == 0 || Pwd[0] == 0 || UserCall[0] == 0) // invalid record + return 0; + + _strupr(UserCall); + + if (TCP->NumberofUsers == 0) + TCP->UserRecPtr = zalloc(sizeof(void *)); + else + TCP->UserRecPtr = realloc(TCP->UserRecPtr, (TCP->NumberofUsers+1) * sizeof(void *)); + + USER = zalloc(sizeof(struct UserRec)); + + TCP->UserRecPtr[TCP->NumberofUsers] = USER; + + USER->Callsign = _strdup(UserCall); + USER->Password = _strdup(Pwd); + USER->UserName = _strdup(User); + USER->Appl = zalloc(32); + USER->Secure = FALSE; + + if (_stricmp(Secure, "SYSOP") == 0) + USER->Secure = TRUE; + + if (Appl[0] && strcmp(Appl, "\"\"") != 0) + { + strcpy(USER->Appl, _strupr(Appl)); + strcat(USER->Appl, "\r\n"); + } + TCP->NumberofUsers += 1; + } + else if (_stricmp(param,"WebTermCSS") == 0) + { + TCP->WebTermCSS = _strdup(value); + } + else + return FALSE; + + return TRUE; +} + +static int MaxStreams = 26; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +void CheckRX(struct TNCINFO * TNC); +VOID TelnetPoll(int Port); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); + +VOID 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); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + + +static VOID WritetoTrace(int Stream, char * Msg, int Len, struct ADIF * ADIF, char Dirn) +{ + int index = 0; + UCHAR * ptr1 = Msg, * ptr2; + UCHAR Line[1000]; + int LineLen, i; + char logmsg[200]; + + Msg[Len] = 0; + +lineloop: + + if (Len > 0) + { + // copy text to file a line at a time + + ptr2 = memchr(ptr1, 13 , Len); + + if (ptr2) + { + ptr2++; + LineLen = (int)(ptr2 - ptr1); + Len -= LineLen; + + if (LineLen > 900) + goto Skip; + + memcpy(Line, ptr1, LineLen); + memcpy(&Line[LineLen - 1], "", 4); + LineLen += 3; + + if ((*ptr2) == 10) + { + memcpy(&Line[LineLen], "", 4); + LineLen += 4; + ptr2++; + Len --; + } + + Line[LineLen] = 0; + + // If line contains any data above 7f, assume binary and dont display + + for (i = 0; i < LineLen; i++) + { + if (Line[i] > 127 || Line[i] < 32) + goto Skip; + } + + if (strlen(Line) < 100) + { + sprintf(logmsg,"%d %s\r\n", Stream, Line); + WriteCMSLog(logmsg); + UpdateADIFRecord(ADIF, Line, Dirn); + } + +Skip: + ptr1 = ptr2; + goto lineloop; + } + + // No CR + + for (i = 0; i < Len; i++) + { + if (ptr1[i] > 127) + break; + } + + if (i == Len) // No binary data + { + if (strlen(ptr1) < 100) + { + sprintf(logmsg,"%d %s\r\n", Stream, ptr1); + WriteCMSLog(logmsg); + UpdateADIFRecord(ADIF, ptr1, Dirn); + } + } + } +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int txlen = 0, n; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct TCPINFO * TCP; + + int Stream; + struct ConnectionInfo * sockptr; + struct STREAMINFO * STREAM; + + if (TNC == NULL) + return 0; // Not configured + + switch (fn) + { + case 7: + + // 100 mS Timer. Now needed, as Poll can be called more ferquently in some circimstances + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + TCP = TNC->TCPInfo; + + if (TCP->CMS) + { + TCP->CheckCMSTimer++; + + if (TCP->CMSOK) + { + if (TCP->CheckCMSTimer > 600 * 15) + CheckCMS(TNC); + } + else + { + if (TCP->CheckCMSTimer > 600 * 2) + CheckCMS(TNC); + } + } + + // We now use persistent HTTP sessions, so need to close after a reasonable time + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode) + { + if (sockptr->WebSocks == 0) + { + if (sockptr->LastSendTime && (REALTIMETICKS - sockptr->LastSendTime) > 1500) // ~ 2.5 mins + { + closesocket(sockptr->socket); + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + } + } + } + else + { + // Time out Login + + if (sockptr->LoginState < 2 && (time(NULL) - sockptr->ConnectTime) > 30) + { + closesocket(sockptr->socket); + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + } + } + } + } + + + + return 0; + + case 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + TRANSPORTENTRY * SESS; + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + + if (sockptr && sockptr->UserPointer == &CMSUser) // Connected to CMS + { + SESS = TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]; + + if (SESS) + { + n = SESS->L4KILLTIMER; + if (n < (SESS->L4LIMIT - 120)) + { + SESS->L4KILLTIMER = SESS->L4LIMIT - 120; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = SESS->L4LIMIT - 120; + } + } + } + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + STREAM->ReportDISC = TRUE; + } + } + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + TelnetPoll(port); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + STREAM = &TNC->Streams[Stream]; + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + buffptr->Len = txlen; + memcpy(&buffptr->Data, &buff->L2DATA, txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesQueued > 40) + return (257); // Busy + + return 256; // OK + +#define SD_BOTH 0x02 + + case 4: // reinit + { + struct _EXTPORTDATA * PortRecord; + +#ifndef LINBPQ + HWND SavehDlg, SaveCMS, SaveMonitor; + HMENU SaveMenu1, SaveMenu2, SaveMenu3; +#endif + int n, i; + + if (!ProcessConfig()) + { + Consoleprintf("Failed to reread config file - leaving config unchanged"); + break; + } + + FreeConfig(); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + sockptr->SocketActive = FALSE; + closesocket(sockptr->socket); + } + + TCP = TNC->TCPInfo; + + shutdown(TCP->TCPSock, SD_BOTH); + shutdown(TCP->sock6, SD_BOTH); + + n = 0; + while (TCP->FBBsock[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock, SD_BOTH); + shutdown(TCP->HTTPsock, SD_BOTH); + shutdown(TCP->HTTPsock6, SD_BOTH); + + + n = 0; + while (TCP->FBBsock6[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock6, SD_BOTH); + Sleep(500); + + closesocket(TCP->TCPSock); + closesocket(TCP->sock6); + + n = 0; + while (TCP->FBBsock[n]) + closesocket(TCP->FBBsock[n++]); + + n = 0; + while (TCP->FBBsock6[n]) + closesocket(TCP->FBBsock6[n++]); + + closesocket(TCP->Relaysock); + closesocket(TCP->Relaysock6); + closesocket(TCP->HTTPsock); + closesocket(TCP->HTTPsock6); + + // Save info from old TNC record + + n = TNC->Port; + PortRecord = TNC->PortRecord; +#ifndef LINBPQ + SavehDlg = TNC->hDlg; + SaveCMS = TCP->hCMSWnd; + SaveMonitor = TNC->hMonitor; + SaveMenu1 = TCP->hActionMenu; + SaveMenu2 = TCP->hDisMenu; + SaveMenu3 = TCP->hLogMenu; +#endif + // Free old TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + free(TNC->Streams[i].ConnectionInfo); + } + + ReadConfigFile(TNC->Port, ProcessLine); + + TNC = TNCInfo[n]; + TNC->Port = n; + TNC->Hardware = H_TELNET; + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + + // Get Menu Handles + + TCP = TNC->TCPInfo; +#ifndef LINBPQ + TNC->hDlg = SavehDlg; + TCP->hCMSWnd = SaveCMS; + TNC->hMonitor = SaveMonitor; + TCP->hActionMenu = SaveMenu1; + TCP->hDisMenu = SaveMenu2; + TCP->hLogMenu = SaveMenu3; + + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TNC->TCPInfo->CMS<<3); + CheckMenuItem(TCP->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + CheckMenuItem(TCP->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); +#endif + // Malloc TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + TNC->Streams[i].ConnectionInfo = zalloc(sizeof(struct ConnectionInfo)); + TCP->CurrentSockets = i; //Record max used to save searching all entries +#ifndef LINBPQ + if (i > 0) + ModifyMenu(TCP->hDisMenu,i - 1 ,MF_BYPOSITION | MF_STRING,IDM_DISCONNECT + 1, "."); +#endif + } + + TNC->PortRecord = PortRecord; + + Sleep(500); + OpenSockets(TNC); + OpenSockets6(TNC); + SetupListenSet(TNC); + CheckCMS(TNC); + ShowConnections(TNC); + } + + break; + + case 5: // Close + + TCP = TNC->TCPInfo; + + for (n = 1; n <= TCP->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + closesocket(sockptr->socket); + } + + shutdown(TCP->TCPSock, SD_BOTH); + + n = 0; + while (TCP->FBBsock[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock, SD_BOTH); + shutdown(TCP->HTTPsock, SD_BOTH); + shutdown(TCP->HTTPsock6, SD_BOTH); + + shutdown(TCP->sock6, SD_BOTH); + + n = 0; + while (TCP->FBBsock6[n]) + shutdown(TCP->FBBsock6[n++], SD_BOTH); + + shutdown(TCP->Relaysock6, SD_BOTH); + + Sleep(500); + closesocket(TCP->TCPSock); + + n = 0; + while (TCP->FBBsock[n]) + closesocket(TCP->FBBsock[n++]); + + closesocket(TCP->Relaysock); + + closesocket(TCP->sock6); + + n = 0; + while (TCP->FBBsock6[n]) + closesocket(TCP->FBBsock6[n++]); + + closesocket(TCP->Relaysock6); + closesocket(TCP->HTTPsock); + closesocket(TCP->HTTPsock6); + + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len; + char msg[256]; + struct ConnectionInfo * sockptr; + int i,n; + + char CMSStatus[80] = ""; + + if (TNC->TCPInfo->CMS) + { + if (TNC->TCPInfo->CMSOK) + strcpy(CMSStatus, "CMS Ok"); + else + strcpy(CMSStatus, "No CMS"); + } + + Len = sprintf(Buff, "" + "Telnet StatusTelnet Status         %s", CMSStatus); + + Len += sprintf(&Buff[Len], ""); + + + Len += sprintf(&Buff[Len], ""); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr=TNC->Streams[n].ConnectionInfo; + + if (!sockptr->SocketActive) + { + strcpy(msg,""); + } + else + { + if (sockptr->UserPointer == 0) + { + if (sockptr->HTTPMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg,"", Addr); + } + else if (sockptr->DRATSMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg,"", Addr); + } + else + strcpy(msg,""); + } + else + { + i=sprintf(msg,"", + sockptr->UserPointer->UserName, sockptr->Callsign, + sockptr->FromHostBuffPutptr - sockptr->FromHostBuffGetptr); + } + } + Len += sprintf(&Buff[Len], "%s", msg); + } + + Len += sprintf(&Buff[Len], "
UserCallsignQueue
Idle  
HTTP<%s  
DRATS<%s  
Logging in  
%s%s%d
"); + return Len; +} + + + +void * TelnetExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + int port; + char * ptr; + int i; + HWND x=0; + +/* + UCHAR NC[257]; + WCHAR WC[1024]; + + int WLen, NLen; + + UINT UTF[256] = {0}; + UINT UTFL[256]; + + int n, u; + + for (n = 0; n < 256; n++) + NC[n] =n ; + + n = MultiByteToWideChar(437, 0, NC, 256, &WC[0], 1024); + + for (n = 0; n < 256; n++) + UTFL[n] = WideCharToMultiByte(CP_UTF8, 0, &WC[n], 1, &UTF[n], 1024 , NULL, NULL); + + // write UTF8 data as source + + { + HANDLE Handle; + int i, n, Len; + char Line [256]; + + + Handle = CreateFile("c:\\UTF8437Data.c", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + n = wsprintf (Line, "unsigned int CP437toUTF8Data[128] = {\r\n"); + + WriteFile(Handle, Line ,n , &n, NULL); + + if (Handle != INVALID_HANDLE_VALUE) + { + for (i = 128; i < 256; i += 4) + { + n = wsprintf (Line, " %d, %d, %d, %d, \r\n", + UTF[i], UTF[i+1], UTF[i+2], UTF[i+3]); + WriteFile(Handle, Line ,n , &n, NULL); + + } + + WriteFile(Handle, "};\r\n", 4, &n, NULL); + } + n = wsprintf (Line, "unsigned int CP437toUTF8DataLen[128] = {\r\n"); + + WriteFile(Handle, Line ,n , &n, NULL); + + if (Handle != INVALID_HANDLE_VALUE) + { + for (i = 128; i < 256;i += 4) + { + n = wsprintf (Line, " %d, %d, %d, %d, \r\n", + UTFL[i], UTFL[i+1], UTFL[i+2], UTFL[i+3]); + WriteFile(Handle, Line ,n , &n, NULL); + + } + + WriteFile(Handle, "};\r\n", 4, &n, NULL); + + SetEndOfFile(Handle); + + CloseHandle(Handle); + } + } +*/ + + DeleteTelnetLogFiles(); + + initUTF8(); + + sprintf(msg,"Telnet Server "); + WritetoConsoleLocal(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + WritetoConsoleLocal("\n"); + return ExtProc; + } + + TCP = TNC->TCPInfo; + + TNC->Port = port; + + TNC->Hardware = H_TELNET; + + PortEntry->MAXHOSTMODESESSIONS = TNC->TCPInfo->MaxSessions + 1; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] != 0) + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // No Scan Control + PortEntry->PERMITGATEWAY = TRUE; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TELNETMONVECPTR->HOSTAPPLFLAGS = 0x80; // Requext Monitoring + + if (TCP->LoginMsg[0] == 0) + strcpy(TCP->LoginMsg, "user:"); + if (TCP->PasswordMsg[0] == 0) + strcpy(TCP->PasswordMsg, "password:"); + if (TCP->cfgCTEXT[0] == 0) + { + char Call[10]; + memcpy(Call, MYNODECALL, 10); + strlop(Call, ' '); + + sprintf(TCP->cfgCTEXT, "Connected to %s's Telnet Server\r\n\r\n", Call); + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 260; + TNC->WebWinY = 325; + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, TelWndProc, 400, 300, NULL); + + TCP->hCMSWnd = CreateWindowEx(0, "STATIC", "CMS OK ", WS_CHILD | WS_VISIBLE, + 240,0,60,16, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TCP->hCMSWnd, WM_SETFONT, (WPARAM)hFont, 0); + + x = CreateWindowEx(0, "STATIC", " User Callsign Queue", WS_CHILD | WS_VISIBLE, + 0,0,240,16, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL, + 0,20,400,2800, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->hMonitor, WM_SETFONT, (WPARAM)hFont, 0); + + hPopMenu = CreatePopupMenu(); + hPopMenu2 = CreatePopupMenu(); + hPopMenu3 = CreatePopupMenu(); + + AppendMenu(hPopMenu, MF_STRING + MF_POPUP, (UINT)hPopMenu2,"Logging Options"); + AppendMenu(hPopMenu, MF_STRING + MF_POPUP, (UINT)hPopMenu3,"Disconnect User"); + AppendMenu(hPopMenu, MF_STRING, TELNET_RECONFIG, "ReRead Config"); + AppendMenu(hPopMenu, MF_STRING, CMSENABLED, "CMS Access Enabled"); + AppendMenu(hPopMenu, MF_STRING, USECACHEDCMS, "Using Cached CMS Addresses"); + + AppendMenu(hPopMenu2, MF_STRING, IDM_LOGGING, "Log incoming connections"); + AppendMenu(hPopMenu2, MF_STRING, IDM_CMS_LOGGING, "Log CMS Connections"); + + AppendMenu(hPopMenu3, MF_STRING, IDM_DISCONNECT, "1"); + + TCP->hActionMenu = hPopMenu; + + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); + + TCP->hLogMenu = hPopMenu2; + TCP->hDisMenu = hPopMenu3; + + CheckMenuItem(TCP->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + CheckMenuItem(TCP->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); + +// ModifyMenu(hMenu, 1, MF_BYPOSITION | MF_OWNERDRAW | MF_STRING, 10000, 0); + +#endif + + // Malloc TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + TNC->Streams[i].ConnectionInfo = zalloc(sizeof(struct ConnectionInfo)); + TNC->Streams[i].ConnectionInfo->Number = i; + TCP->CurrentSockets = i; //Record max used to save searching all entries + + sprintf(msg,"%d", i); + +#ifndef LINBPQ + if (i > 1) + AppendMenu(TCP->hDisMenu, MF_STRING, IDM_DISCONNECT ,msg); +#endif + } + + OpenSockets(TNC); + OpenSockets6(TNC); + SetupListenSet(TNC); + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + + if (TCP->CMS) + CheckCMS(TNC); + + WritetoConsoleLocal("\n"); + + ShowConnections(TNC); + + if (TCP->ReportHybrid) + SendWL2KRegisterHybrid(TNC); + + + return ExtProc; +} + +SOCKET OpenSocket4(struct TNCINFO * xTNC, int port) +{ + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + SOCKET sock = 0; + u_long param=1; + + char szBuff[80]; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + if (port) + { + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "socket() failed error %d\n", WSAGetLastError()); + WritetoConsoleLocal(szBuff); + return 0; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + + psin->sin_port = htons(port); // Convert to network ordering + + if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket( sock ); + return FALSE; + } + + if (listen( sock, MAX_PENDING_CONNECTS ) < 0) + { + sprintf(szBuff, "listen(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + return FALSE; + } + ioctl(sock, FIONBIO, ¶m); + } + + return sock; +} + +BOOL OpenSockets(struct TNCINFO * TNC) +{ + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + u_long param=1; + struct TCPINFO * TCP = TNC->TCPInfo; + int n; + + if (!TCP->IPV4) + return TRUE; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + if (TCP->TCPPort) + TCP->TCPSock = OpenSocket4(TNC, TCP->TCPPort); + + n = 0; + + while (TCP->FBBPort[n]) + { + TCP->FBBsock[n] = OpenSocket4(TNC, TCP->FBBPort[n]); + n++; + } + + if (TCP->RelayPort) + TCP->Relaysock = OpenSocket4(TNC, TCP->RelayPort); + + if (TCP->TriModePort) + { + TCP->TriModeSock = OpenSocket4(TNC, TCP->TriModePort); + TCP->TriModeDataSock = OpenSocket4(TNC, TCP->TriModePort + 1); + } + + if (TCP->HTTPPort) + TCP->HTTPsock = OpenSocket4(TNC, TCP->HTTPPort); + + if (TCP->SyncPort) + TCP->Syncsock = OpenSocket4(TNC, TCP->SyncPort); + + if (TCP->DRATSPort) + TCP->DRATSsock = OpenSocket4(TNC, TCP->DRATSPort); + + CMSUser.UserName = _strdup("CMS"); + + TriModeUser.Secure = TRUE; + TriModeUser.UserName = _strdup("TRIMODE"); + TriModeUser.Callsign = zalloc(10); + + return TRUE; +} + +SOCKET OpenSocket6(struct TNCINFO * TNC, int port) +{ + struct sockaddr_in6 local_sin; /* Local socket - internet style */ + struct sockaddr_in6 * psin; + SOCKET sock; + char szBuff[80]; + u_long param=1; + + memset(&local_sin, 0, sizeof(local_sin)); + + psin=&local_sin; + psin->sin6_family = AF_INET6; + psin->sin6_addr = in6addr_any; + psin->sin6_flowinfo = 0; + psin->sin6_scope_id = 0; + + sock = socket(AF_INET6, SOCK_STREAM, 0); + +#ifndef WIN32 + + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ¶m, sizeof(param)) < 0) + { + perror("setting option IPV6_V6ONLY"); + } + +#endif + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "IPV6 socket() failed error %d\n", WSAGetLastError()); + WritetoConsoleLocal(szBuff); + return FALSE; + } + + psin->sin6_port = htons(port); // Convert to network ordering + + if (bind(sock, (struct sockaddr FAR *)psin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "IPV6 bind(sock) failed Port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket( sock ); + + return FALSE; + } + + if (listen( sock, MAX_PENDING_CONNECTS ) < 0) + { + sprintf(szBuff, "IPV6 listen(sock) failed Error %d\n", WSAGetLastError()); + WritetoConsoleLocal(szBuff); + + return FALSE; + } + + ioctl(sock, FIONBIO, ¶m); + return sock; +} + + +BOOL OpenSockets6(struct TNCINFO * TNC) +{ + struct sockaddr_in6 local_sin; /* Local socket - internet style */ + + struct sockaddr_in6 * psin; + struct TCPINFO * TCP = TNC->TCPInfo; + int n; + u_long param=1; + + if (!TCP->IPV6) + return TRUE; + + memset(&local_sin, 0, sizeof(local_sin)); + + psin=&local_sin; + psin->sin6_family = AF_INET6; + psin->sin6_addr = in6addr_any; + psin->sin6_flowinfo = 0; + psin->sin6_scope_id = 0; + + + if (TCP->TCPPort) + TCP->sock6 = OpenSocket6(TNC, TCP->TCPPort); + + n = 0; + + while (TCP->FBBPort[n]) + { + TCP->FBBsock6[n] = OpenSocket6(TNC, TCP->FBBPort[n]); + n++; + } + + if (TCP->RelayPort) + TCP->Relaysock6 = OpenSocket6(TNC, TCP->RelayPort); + + + if (TCP->HTTPPort) + TCP->HTTPsock6 = OpenSocket6(TNC, TCP->HTTPPort); + + if (TCP->SyncPort) + TCP->Syncsock6 = OpenSocket6(TNC, TCP->SyncPort); + + if (TCP->DRATSPort) + TCP->DRATSsock6 = OpenSocket6(TNC, TCP->DRATSPort); + + + CMSUser.UserName = _strdup("CMS"); + + return TRUE; +} + +static VOID SetupListenSet(struct TNCINFO * TNC) +{ + // Set up master set of fd's for checking for incoming calls + + struct TCPINFO * TCP = TNC->TCPInfo; + SOCKET maxsock = 0; + int n; + fd_set * readfd = &TCP->ListenSet; + SOCKET sock; + + FD_ZERO(readfd); + + n = 0; + while (n < 100) + { + sock = TCP->FBBsock[n++]; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + } + + sock = TCP->TCPSock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->Relaysock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->HTTPsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->Syncsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->DRATSsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->TriModeSock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->TriModeDataSock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + n = 0; + while (n < 100) + { + sock = TCP->FBBsock6[n++]; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + } + + sock = TCP->sock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->Relaysock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->HTTPsock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->DRATSsock6; + + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + TCP->maxsock = maxsock; +} + + +VOID TelnetPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + struct TCPINFO * TCP = TNC->TCPInfo; + struct STREAMINFO * STREAM; + int Msglen; + int Stream; + + // we now poll for incoming connections + + struct timeval timeout; + int retval; + int n; + struct ConnectionInfo * sockptr; + SOCKET sock; + int Active = 0; + SOCKET maxsock; + + fd_set readfd, writefd, exceptfd; + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (TCP->maxsock == 0) + goto nosocks; + + memcpy(&readfd, &TCP->ListenSet, sizeof(fd_set)); + + retval = select((int)(TCP->maxsock) + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + retval = WSAGetLastError(); + perror("listen select"); + } + + if (retval) + { + n = 0; + while (TCP->FBBsock[n]) + { + sock = TCP->FBBsock[n]; + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->FBBPort[n]); + + n++; + } + + sock = TCP->TCPSock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TCPPort); + } + + sock = TCP->TriModeSock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TriModePort); + } + sock = TCP->TriModeDataSock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TriModePort + 1); + } + + sock = TCP->Relaysock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->RelayPort); + } + + sock = TCP->HTTPsock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->HTTPPort); + } + + sock = TCP->DRATSsock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->DRATSPort); + } + + sock = TCP->Syncsock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->SyncPort); + } + + + + n = 0; + + while (TCP->FBBsock6[n]) + { + sock = TCP->FBBsock6[n]; + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->FBBPort[n]); + n++; + } + + sock = TCP->sock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TCPPort); + } + + sock = TCP->Relaysock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->RelayPort); + } + + sock = TCP->HTTPsock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->HTTPPort); + } + + sock = TCP->DRATSsock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->DRATSPort); + } + } + + // look for data on any active sockets + + maxsock = 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + // Should we use write event after a blocked write ???? + + if (sockptr->SocketActive) + { +// if (sockptr->socket == 0) +// { +// Debugprintf("Active Session but zero socket"); +// DataSocket_Disconnect(TNC, sockptr); +// return; +// } + + if (TNC->Streams[n].Connecting) + { + // look for complete or failed + + FD_SET(sockptr->socket, &writefd); + FD_SET(sockptr->socket, &exceptfd); + } + else + { + FD_SET(sockptr->socket, &readfd); + FD_SET(sockptr->socket, &exceptfd); + } + Active++; + if (sockptr->socket > maxsock) + maxsock = sockptr->socket; + + if (sockptr->TriModeDataSock) + { + FD_SET(sockptr->TriModeDataSock, &readfd); + FD_SET(sockptr->TriModeDataSock, &exceptfd); + + if (sockptr->TriModeDataSock > maxsock) + maxsock = sockptr->TriModeDataSock; + } + } + } + + if (Active) + { + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("data select"); + Debugprintf("Telnet Select Error %d Active %d", WSAGetLastError(), Active); + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + if (sockptr->SocketActive) + Debugprintf("Active Session %d socket %d", n, sockptr->socket); + } + } + else + { + if (retval) + { + // see who has data + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + sock = sockptr->socket; + + if (sockptr->TriModeDataSock) + { + if (FD_ISSET(sockptr->TriModeDataSock, &readfd)) + { + ProcessTriModeDataMessage(TNC, sockptr, sockptr->TriModeDataSock, &TNC->Streams[n]); + } + } + + if (FD_ISSET(sock, &readfd)) + { + if (sockptr->RelayMode) + DataSocket_ReadRelay(TNC, sockptr, sock, &TNC->Streams[n]); + else if (sockptr->HTTPMode) + DataSocket_ReadHTTP(TNC, sockptr, sock, n); + else if (sockptr->SyncMode) + DataSocket_ReadSync(TNC, sockptr, sock, n); + else if (sockptr->FBBMode) + DataSocket_ReadFBB(TNC, sockptr, sock, n); + else if (sockptr->DRATSMode) + DataSocket_ReadDRATS(TNC, sockptr, sock, n); + else + DataSocket_Read(TNC, sockptr, sock, &TNC->Streams[n]); + } + + if (FD_ISSET(sock, &exceptfd)) + { + Debugprintf("exceptfd set"); + Telnet_Connected(TNC, sockptr, sock, 1); + } + + if (FD_ISSET(sock, &writefd)) + Telnet_Connected(TNC, sockptr, sock, 0); + + } + } + } + } + } + + +nosocks: + + while (TELNETMONVECPTR->HOSTTRACEQ) + { + int len; + time_t stamp; + BOOL MonitorNODES = FALSE; + MESSAGE * monbuff; + UCHAR * monchars; + + unsigned char buffer[1024] = "\xff\x1b\xb"; + + monbuff = Q_REM((void **)&TELNETMONVECPTR->HOSTTRACEQ); + monchars = (UCHAR *)monbuff; + + stamp = monbuff->Timestamp; + + if (monbuff->PORT & 0x80) // TX + buffer[2] = 91; + else + buffer[2] = 17; + + for (Stream = 0; Stream <= TCP->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) + { + struct ConnectionInfo * sockptr = STREAM->ConnectionInfo; + + if (sockptr->BPQTermMode) + { + if (sizeof(void *) > 4) + monchars += 4; + + if (!sockptr->MonitorNODES && monchars[21] == 3 && monchars[22] == 0xcf && monchars[23] == 0xff) + { + len = 0; + } + else + { + unsigned long long SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + + IntSetTraceOptionsEx(sockptr->MMASK, sockptr->MTX, sockptr->MCOM, sockptr->MUIOnly); + len = IntDecodeFrame((MESSAGE *)monbuff, &buffer[3], stamp, sockptr->MMASK, FALSE, FALSE); + SetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (len) + { + len += 3; + buffer[len++] = 0xfe; + send(STREAM->ConnectionInfo->socket, buffer, len, 0); + } + } + } + } + } + + ReleaseBuffer(monbuff); + } + + for (Stream = 0; Stream <= TCP->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + STREAM->Attached = TRUE; + STREAM->FramesQueued= 0; + STREAM->NoCMSFallback = 0; + + continue; + } + + if (STREAM->Attached) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0 && STREAM->Attached) + { + // Node has disconnected - clear any connection + + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + SOCKET sock = sockptr->socket; + char Msg[80]; + PMSGWITHLEN buffptr; + + STREAM->Attached = FALSE; + STREAM->Connected = FALSE; + STREAM->NoCMSFallback = FALSE; + + sockptr->FromHostBuffPutptr = sockptr->FromHostBuffGetptr = 0; // clear any queued data + + while(TNC->Streams[Stream].BPQtoPACTOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + if (TelSendPacket(Stream, &TNC->Streams[Stream], buffptr, sockptr->ADIF) == FALSE) + { + // Send failed, and has saved packet + // free saved and discard any more on queue + + free(sockptr->ResendBuffer); + sockptr->ResendBuffer = NULL; + sockptr->ResendLen = 0; + + while(TNC->Streams[Stream].BPQtoPACTOR_Q) + { + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + } + break; + } + } + + while(TNC->Streams[Stream].PACTORtoBPQ_Q) + { + buffptr=Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + ReleaseBuffer(buffptr); + } + + if (LogEnabled) + { + char logmsg[120]; + sprintf(logmsg,"%d Disconnected. Bytes Sent = %d Bytes Received %d\n", + sockptr->Number, STREAM->BytesTXed, STREAM->BytesRXed); + + WriteLog (logmsg); + } + + if (!sockptr->FBBMode) + { + sprintf(Msg,"*** Disconnected from Stream %d\r\n",Stream); + send(sock, Msg, (int)strlen(Msg),0); + } + + if (sockptr->UserPointer == &TriModeUser) + { + // Always Disconnect + + send(sockptr->socket, "DISCONNECTED\r\n", 14, 0); + return; + } + + if (sockptr->UserPointer == &CMSUser) + { + if (CMSLogEnabled) + { + char logmsg[120]; + sprintf(logmsg,"%d Disconnected. Bytes Sent = %d Bytes Received %d Time %d Seconds\r\n", + sockptr->Number, STREAM->BytesTXed, STREAM->BytesRXed, (int)(time(NULL) - sockptr->ConnectTime)); + + WriteCMSLog (logmsg); + } + + // Don't report if Internet down unless ReportRelayTraffic set) + + if (sockptr->RelaySession == FALSE || TCP->ReportRelayTraffic) + SendWL2KSessionRecord(sockptr->ADIF, STREAM->BytesTXed, STREAM->BytesRXed); + + WriteADIFRecord(sockptr->ADIF); + + if (sockptr->ADIF) + free(sockptr->ADIF); + + sockptr->ADIF = NULL; + + // Always Disconnect CMS Socket + + DataSocket_Disconnect(TNC, sockptr); + return; + } + + if (sockptr->RelayMode) + { + // Always Disconnect Relay Socket + + Sleep(100); + DataSocket_Disconnect(TNC, sockptr); + return; + } + + if (sockptr->Signon[0] || sockptr->ClientSession) // Outward Connect + { + Sleep(1000); + DataSocket_Disconnect(TNC, sockptr); + return; + } + + + if (TCP->DisconnectOnClose) + { + Sleep(1000); + DataSocket_Disconnect(TNC, sockptr); + } + else + { + char DisfromNodeMsg[] = "Disconnected from Node - Telnet Session kept\r\n"; + send(sockptr->socket, DisfromNodeMsg, (int)strlen(DisfromNodeMsg),0); + } + } + } + } + + for (Stream = 0; Stream <= TCP->MaxSessions; Stream++) + { + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + STREAM = &TNC->Streams[Stream]; + + if (sockptr->SocketActive && sockptr->Keepalive && L4LIMIT) + { +#ifdef WIN32 + if ((REALTIMETICKS - sockptr->LastSendTime) > (L4LIMIT - 60) * 9) // PC Ticks are about 10% slow +#else + if ((REALTIMETICKS - sockptr->LastSendTime) > (L4LIMIT - 60) * 10) +#endif + { + // Send Keepalive + + sockptr->LastSendTime = REALTIMETICKS; + BuffertoNode(sockptr, "Keepalive\r", 10); + } + } + + if (sockptr->ResendBuffer) + { + // Data saved after EWOULDBLOCK returned to send + + UCHAR * ptr = sockptr->ResendBuffer; + sockptr->ResendBuffer = NULL; + + SendAndCheck(sockptr, ptr, sockptr->ResendLen, 0); + free(ptr); + + continue; + } + + while (STREAM->BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + UCHAR * MsgPtr; + + // Make sure there is space. Linux TCP buffer is quite small + // Windows doesn't support SIOCOUTQ + +#ifndef WIN32 + int value = 0, error; + + error = ioctl(sockptr->socket, SIOCOUTQ, &value); + + if (value > 1500) + break; +#endif + buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + STREAM->FramesQueued--; + datalen = (int)(buffptr->Len); + MsgPtr = &buffptr->Data[0]; + + if (STREAM->ConnectionInfo->TriMode) + { + ProcessTrimodeResponse(TNC, STREAM, MsgPtr, datalen); + ReleaseBuffer(buffptr); + return; + } + + + if (TNC->Streams[Stream].Connected) + { + if (sockptr->SyncMode) + { + // Suppress Conected and SID - Relay doesn't understand them + + if (strstr(buffptr->Data, "Connected to") || memcmp(buffptr->Data, "[BPQ-", 5) == 0) + { + ReleaseBuffer(buffptr); + return; + } + } + + if (TelSendPacket(Stream, STREAM, buffptr, sockptr->ADIF) == FALSE) + { + // Send failed, and has requeued packet + // Dont send any more + + break; + } + } + else // Not Connected + { + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + + if (_stricmp(MsgPtr, "NoFallback") == 0) + { + TNC->Streams[Stream].NoCMSFallback = TRUE; + buffptr->Len = sprintf(&buffptr->Data[0], "Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return; + } + + if (_memicmp(MsgPtr, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if ((_memicmp(MsgPtr, "C", 1) == 0) && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + char Host[100] = ""; + char P2[100] = ""; + char P3[100] = ""; + char P4[100] = ""; + char P5[100] = ""; + char P6[100] = ""; + char P7[100] = ""; + unsigned int Port = 0; + int n; + + n = sscanf(&MsgPtr[2], "%s %s %s %s %s %s %s", + &Host[0], &P2[0], &P3[0], &P4[0], &P5[0], &P6[0], &P7[0]); + + sockptr->Signon[0] = 0; // Not outgoing; + sockptr->Keepalive = FALSE; // No Keepalives + sockptr->NoCallsign = FALSE; + sockptr->UTF8 = 0; // Not UTF8 + + if (_stricmp(Host, "HOST") == 0) + { + Port = atoi(P2); + + if (Port > 33 || TCP->CMDPort[Port] == 0) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Error - Invalid HOST Port\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + return; + } + + STREAM->Connecting = TRUE; + sockptr->CMSSession = FALSE; + sockptr->FBBMode = FALSE; + + if (P3[0] == 'K' || P4[0] == 'K' || P5[0] == 'K' || P6[0] == 'K') + { + sockptr->Keepalive = TRUE; + sockptr->LastSendTime = REALTIMETICKS; + } + + if (P3[0] == 'S' || P4[0] == 'S' || P5[0] == 'S' || P6[0] == 'S') + { + // Set Say flag on partner session + + struct _TRANSPORTENTRY * Sess = TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->L4CROSSLINK; + if (Sess) + Sess->STAYFLAG = 1; + } + + if (_stricmp(P3, "NOCALL") == 0 || _stricmp(P4, "NOCALL") == 0 || _stricmp(P5, "NOCALL") == 0 || _stricmp(P6, "NOCALL") == 0) + sockptr->NoCallsign = TRUE; + + if (_stricmp(P3, "TRANS") == 0 || _stricmp(P4, "TRANS") == 0 || _stricmp(P5, "TRANS") == 0 || _stricmp(P6, "TRANS") == 0) + { + sockptr->FBBMode = TRUE; + sockptr->NeedLF = TRUE; + } + + TCPConnect(TNC, TCP, STREAM, "127.0.0.1", TCP->CMDPort[Port], sockptr->FBBMode); + ReleaseBuffer(buffptr); + return; + } + + if (_stricmp(Host, "RELAY") == 0) + { + if (P2[0] == 0) + { + strcpy(P2, TCP->RELAYHOST); + strcpy(P3, "8772"); + } + + if (P2[0]) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = TRUE; + TCPConnect(TNC, TCP, STREAM, P2, atoi(P3), TRUE); + ReleaseBuffer(buffptr); + return; + } + } + + if (_stricmp(Host, "SYNC") == 0) + { + if (P2[0] == 0) + { + strcpy(P2, TCP->RELAYHOST); + strcpy(P3, "8780"); + } + + if (P2[0]) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->SyncMode = TRUE; + TCPConnect(TNC, TCP, STREAM, P2, atoi(P3), TRUE); + ReleaseBuffer(buffptr); + return; + } + } + + + if (_stricmp(Host, "CMS") == 0) + { + if (TCP->CMS == 0 || !TCP->CMSOK) + { + if (TCP->RELAYHOST[0] && TCP->FallbacktoRelay && STREAM->NoCMSFallback == 0) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = TRUE; + TCPConnect(TNC, TCP, STREAM, TCP->RELAYHOST, 8772, TRUE); + ReleaseBuffer(buffptr); + return; + } + + buffptr->Len = sprintf(&buffptr->Data[0], "Error - CMS Not Available\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + CheckCMS(TNC); + + return; + } + + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = FALSE; + CMSConnect(TNC, TCP, STREAM, Stream); + ReleaseBuffer(buffptr); + + return; + } + + // Outward Connect. + + // Only Allow user specified host if Secure Session + + if (TCP->SecureTelnet) + { + struct _TRANSPORTENTRY * Sess = TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]; + +// if (Sess && Sess->L4CROSSLINK) +// Sess = Sess->L4CROSSLINK; + + if (Sess && !Sess->Secure_Session) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Error - Telnet Outward Connect needs SYSOP Status\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + return; + } + } + + Port = atoi(P2); + + if (Port) + { + int useFBBMode = TRUE; + + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = FALSE; + STREAM->ConnectionInfo->RelaySession = FALSE; + + if (_stricmp(P3, "TELNET") == 0) + { +// // FBB with CRLF + + STREAM->ConnectionInfo->NeedLF = TRUE; + } + else if (_stricmp(P3, "REALTELNET") == 0) + { +// // Telnet Mode with CRLF + + useFBBMode = FALSE; + STREAM->ConnectionInfo->NeedLF = TRUE; + } + else + STREAM->ConnectionInfo->NeedLF = FALSE; + + + STREAM->ConnectionInfo->FBBMode = TRUE; + + if (_stricmp(P3, "NOCALL") == 0 || _stricmp(P4, "NOCALL") == 0 || _stricmp(P5, "NOCALL") == 0 || _stricmp(P6, "NOCALL") == 0) + { + STREAM->ConnectionInfo->NoCallsign = TRUE; + } + else + { + if (_stricmp(P3, "NEEDLF") == 0 || STREAM->ConnectionInfo->NeedLF) + { + // Send LF after each param + + if (P6[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r\n%s\r\n%s\r\n", P4, P5, P6); + else + if (P5[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r\n%s\r\n", P4, P5); + else + if (P4[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r\n", P4); + } + else + { + if (P5[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r%s\r%s\r", P3, P4, P5); + else + if (P4[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r%s\r", P3, P4); + else + if (P3[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r", P3); + } + } + + TCPConnect(TNC, TCP, STREAM, Host, Port, useFBBMode); + ReleaseBuffer(buffptr); + return; + } + } + + buffptr->Len = sprintf(&buffptr->Data[0], "Error - Invalid Command\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + return; + } + } + + Msglen = sockptr->FromHostBuffPutptr - sockptr->FromHostBuffGetptr; + + if (Msglen) + { + int Paclen = 0; + int Queued = 0; + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + + if (Sess1) + Sess2 = Sess1->L4CROSSLINK; + + // Can't use TXCount - it is Semaphored= + + Queued = C_Q_COUNT(&TNC->Streams[Stream].PACTORtoBPQ_Q); + Queued += C_Q_COUNT((UINT *)&TNC->PortRecord->PORTCONTROL.PORTRX_Q); + + if (Sess2) + Queued += CountFramesQueuedOnSession(Sess2); + + if (Sess1) + Queued += CountFramesQueuedOnSession(Sess1); + + if (Queued > 15) + continue; + + if (Sess1) + Paclen = Sess1->SESSPACLEN; + + if (Paclen == 0) + Paclen = 256; + + ShowConnections(TNC); + + if (Msglen > Paclen) + Msglen = Paclen; + + if (Sess1) Sess1->L4KILLTIMER = 0; + if (Sess2) Sess2->L4KILLTIMER = 0; + + SendtoNode(TNC, Stream, &sockptr->FromHostBuffer[sockptr->FromHostBuffGetptr], Msglen); + sockptr->FromHostBuffGetptr += Msglen; + sockptr->LastSendTime = REALTIMETICKS; + } + } +} + +#ifndef LINBPQ + +LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + struct ConnectionInfo * sockptr; + SOCKET sock; + int i, n; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + struct _EXTPORTDATA * PortRecord; + HWND SavehDlg, SaveCMS, SaveMonitor; + HMENU SaveMenu1, SaveMenu2, SaveMenu3; + MINMAXINFO * mmi; + + LPMEASUREITEMSTRUCT lpmis; // pointer to item of data + LPDRAWITEMSTRUCT lpdis; // pointer to item drawing data + +// struct ConnectionInfo * ConnectionInfo; + + for (i=1; i<33; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) + { +// case WM_SIZING: + case WM_SIZE: + { + RECT rcClient; + int ClientHeight, ClientWidth; + + GetClientRect(TNC->hDlg, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(TNC->hMonitor, 0,20 ,ClientWidth-4, ClientHeight-25, TRUE); + break; + } + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 400; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 400; + mmi->ptMaxTrackSize.y = 500; + break; + + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hPopMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + TNC->Minimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + + if (wmId > IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) + { + // disconnect user + + sockptr = TNC->Streams[wmId-IDM_DISCONNECT].ConnectionInfo; + + if (sockptr->SocketActive) + { + sock=sockptr->socket; + + send(sock,disMsg, (int)strlen(disMsg),0); + + Sleep (1000); + + shutdown(sock,2); + + DataSocket_Disconnect(TNC, sockptr); + + TNC->Streams[wmId-IDM_DISCONNECT].ReportDISC = TRUE; //Tell Node + + return 0; + } + } + + switch (wmId) + { + case CMSENABLED: + + // Toggle CMS Enabled Flag + + TCP = TNC->TCPInfo; + + TCP->CMS = !TCP->CMS; + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); + + if (TCP->CMS) + CheckCMS(TNC); + else + { + TCP->CMSOK = FALSE; + SetWindowText(TCP->hCMSWnd, "CMS Off"); + } + break; + + case IDM_LOGGING: + + // Toggle Logging Flag + + LogEnabled = !LogEnabled; + CheckMenuItem(TNC->TCPInfo->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + + break; + + case IDM_CMS_LOGGING: + + // Toggle Logging Flag + + LogEnabled = !LogEnabled; + CheckMenuItem(TNC->TCPInfo->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); + + break; + + case TELNET_RECONFIG: + + if (!ProcessConfig()) + { + Consoleprintf("Failed to reread config file - leaving config unchanged"); + break; + } + + FreeConfig(); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + sockptr->SocketActive = FALSE; + closesocket(sockptr->socket); + } + + TCP = TNC->TCPInfo; + + shutdown(TCP->TCPSock, SD_BOTH); + shutdown(TCP->sock6, SD_BOTH); + + n = 0; + while (TCP->FBBsock[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock, SD_BOTH); + shutdown(TCP->HTTPsock, SD_BOTH); + shutdown(TCP->HTTPsock6, SD_BOTH); + + + n = 0; + while (TCP->FBBsock6[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock6, SD_BOTH); + + Sleep(500); + + closesocket(TCP->TCPSock); + closesocket(TCP->sock6); + + n = 0; + while (TCP->FBBsock[n]) + closesocket(TCP->FBBsock[n++]); + + n = 0; + while (TCP->FBBsock6[n]) + closesocket(TCP->FBBsock6[n++]); + + closesocket(TCP->Relaysock); + closesocket(TCP->Relaysock6); + closesocket(TCP->HTTPsock); + closesocket(TCP->HTTPsock6); + + // Save info from old TNC record + + n = TNC->Port; + PortRecord = TNC->PortRecord; + SavehDlg = TNC->hDlg; + SaveCMS = TCP->hCMSWnd; + SaveMonitor = TNC->hMonitor; + SaveMenu1 = TCP->hActionMenu; + SaveMenu2 = TCP->hDisMenu; + SaveMenu3 = TCP->hLogMenu; + + // Free old TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + free(TNC->Streams[i].ConnectionInfo); + } + + ReadConfigFile(TNC->Port, ProcessLine); + + TNC = TNCInfo[n]; + TNC->Port = n; + TNC->Hardware = H_TELNET; + TNC->hDlg = SavehDlg; + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + + // Get Menu Handles + + TCP = TNC->TCPInfo; + + TCP->hCMSWnd = SaveCMS; + TNC->hMonitor = SaveMonitor; + TCP->hActionMenu = SaveMenu1; + TCP->hDisMenu = SaveMenu2; + TCP->hLogMenu = SaveMenu3; + + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TNC->TCPInfo->CMS<<3); + CheckMenuItem(TCP->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + CheckMenuItem(TCP->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); + + // Malloc TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + TNC->Streams[i].ConnectionInfo = zalloc(sizeof(struct ConnectionInfo)); + TNC->Streams[i].ConnectionInfo->Number = i; + + TCP->CurrentSockets = i; //Record max used to save searching all entries + + if (i > 0) + ModifyMenu(TCP->hDisMenu,i - 1 ,MF_BYPOSITION | MF_STRING,IDM_DISCONNECT + 1, "."); + } + + TNC->PortRecord = PortRecord; + + Sleep(500); + OpenSockets(TNC); + OpenSockets6(TNC); + SetupListenSet(TNC); + CheckCMS(TNC); + ShowConnections(TNC); + + break; + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + break; + + // case WM_SIZE: + +// if (wParam == SIZE_MINIMIZED) +// return (0); + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + + if (TNC->TCPInfo->hCMSWnd == (HWND)lParam) + { + if (TNC->TCPInfo->CMSOK) + SetTextColor(hdcStatic, RGB(0, 128, 0)); + else + SetTextColor(hdcStatic, RGB(255, 0, 0)); + } + + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + case WM_MEASUREITEM: + + // Retrieve pointers to the menu item's + // MEASUREITEMSTRUCT structure and MYITEM structure. + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + lpmis->itemWidth = 300; + + return TRUE; + + case WM_DRAWITEM: + + // Get pointers to the menu item's DRAWITEMSTRUCT + // structure and MYITEM structure. + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If the user has selected the item, use the selected + // text and background colors to display the item. + + SetTextColor(lpdis->hDC, RGB(0, 128, 0)); + + if (TNC->TCPInfo->CMS) + { + if (TNC->TCPInfo->CMSOK) + TextOut(lpdis->hDC, 340, lpdis->rcItem.top + 2, "CMS OK", 6); + else + { + SetTextColor(lpdis->hDC, RGB(255, 0, 0)); + TextOut(lpdis->hDC, 340, lpdis->rcItem.top + 2, "NO CMS", 6); + } + } + else + TextOut(lpdis->hDC, 340, lpdis->rcItem.top + 2, " ", 13); + + return TRUE; + + case WM_DESTROY: + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + +} + +#endif + +int Socket_Accept(struct TNCINFO * TNC, SOCKET SocketId, int Port) +{ + int n, addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 sin6; + + struct ConnectionInfo * sockptr; + SOCKET sock; + char Negotiate[6]={IAC,WILL,suppressgoahead,IAC,WILL,echo}; +// char Negotiate[3]={IAC,WILL,echo}; + struct TCPINFO * TCP = TNC->TCPInfo; + HMENU hDisMenu = TCP->hDisMenu; + u_long param=1; + + // if for TriModeData Session, use the TriMode Control connection entry + + if (SocketId == TCP->TriModeDataSock) + { + sockptr = TNC->TCPInfo->TriModeControlSession; + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + sockptr->TriModeDataSock = sock; + ioctl(sock, FIONBIO, ¶m); + + return 0; + } + +// Find a free Session + + for (n = 1; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive == FALSE) + { + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + Debugprintf("BPQ32 Telnet accept() failed Error %d", WSAGetLastError()); + return FALSE; + } + + // Log, including port + + if (LogEnabled) + { + char Addr[256]; + char logmsg[512]; + + Tel_Format_Addr(sockptr, Addr); + + sprintf(logmsg,"%d %s Incoming Connect on Port %d\n", sockptr->Number, Addr, Port); + WriteLog (logmsg); + } + + + +// Debugprintf("BPQ32 Telnet accept() Sock %d", sock); + + ioctl(sock, FIONBIO, ¶m); + + sockptr->socket = sock; + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; + sockptr->Number = n; + sockptr->LoginState = 0; + sockptr->UserPointer = 0; + sockptr->DoEcho = FALSE; + sockptr->BPQTermMode = FALSE; + sockptr->ConnectTime = time(NULL); + sockptr->Keepalive = FALSE; + sockptr->UTF8 = 0; + + TNC->Streams[n].BytesRXed = TNC->Streams[n].BytesTXed = 0; + TNC->Streams[n].FramesQueued = 0; + + sockptr->HTTPMode = FALSE; + sockptr->DRATSMode = FALSE; + sockptr->FBBMode = FALSE; + sockptr->RelayMode = FALSE; + sockptr->ClientSession = FALSE; + sockptr->NeedLF = FALSE; + sockptr->TNC = TNC; + sockptr->WebSocks = 0; + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + if (SocketId == TCP->HTTPsock || SocketId == TCP->HTTPsock6) + sockptr->HTTPMode = TRUE; + else if (SocketId == TCP->Syncsock || SocketId == TCP->Syncsock6) + sockptr->SyncMode = TRUE; + else if (SocketId == TCP->DRATSsock || SocketId == TCP->DRATSsock6) + sockptr->DRATSMode = TRUE; + + else if (SocketId == TCP->Relaysock || SocketId == TCP->Relaysock6) + { + sockptr->RelayMode = TRUE; + sockptr->FBBMode = TRUE; + } + else if (SocketId == TCP->TriModeSock) + { + sockptr->TriMode = TRUE; + sockptr->FBBMode = TRUE; + TNC->TCPInfo->TriModeControlSession = sockptr; + sockptr->TriModeConnected = FALSE; + sockptr->TriModeDataSock = 0; + } + else if (SocketId != TCP->TCPSock && SocketId != TCP->sock6) // We can have several listening FBB mode sockets + sockptr->FBBMode = TRUE; +#ifndef LINBPQ + ModifyMenu(hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, "."); + DrawMenuBar(TNC->hDlg); +#endif + ShowConnections(TNC); + + if (sockptr->HTTPMode) + return 0; + + if (sockptr->DRATSMode) + { + send(sock, "100 Authentication not required\n", 33, 0); + sockptr->LoginState = 2; + return 0; + } + + if (sockptr->SyncMode) + { + char MyCall[16] = ""; + char Hello[32]; + int Len; + memcpy(MyCall, MYNODECALL, 10); + strlop(MyCall, ' '); + strlop(MyCall, '-'); + + Len = sprintf(Hello, "POSYNCHELLO %s\r", MyCall); + + send(sock, Hello, Len, 0); + return 0; + } + else + if (sockptr->RelayMode) + { + send(sock,"\r\rCallsign :\r", 13,0); + } + else + if (sockptr->TriMode) + { + // Trimode emulator Control Connection. + + sockptr->UserPointer = &TriModeUser; + + + send(sock,"CMD\r\n", 5,0); + sockptr->LoginState = 5; + } + else + if (sockptr->FBBMode == FALSE) + { + send(sock, Negotiate, 6, 0); + send(sock, TCP->LoginMsg, (int)strlen(TCP->LoginMsg), 0); + } + + if (sockptr->FromHostBuffer == 0) + { + sockptr->FromHostBuffer = malloc(10000); + sockptr->FromHostBufferSize = 10000; + } + + sockptr->FromHostBuffPutptr = sockptr->FromHostBuffGetptr = 0; + + return 0; + } + } + + // No free sessions. Must accept() then close + + sock = accept(SocketId, (struct sockaddr *)&sin6, &addrlen); + + send(sock,"No Free Sessions\r\n", 18,0); + Debugprintf("No Free Telnet Sessions"); + + Sleep (1000); + closesocket(sock); + + return 0; +} + +/* +int Socket_Data(struct TNCINFO * TNC, int sock, int error, int eventcode) +{ + int n; + struct ConnectionInfo * sockptr; + struct TCPINFO * TCP = TNC->TCPInfo; + HMENU hDisMenu = TCP->hDisMenu; + + // Find Connection Record + + for (n = 0; n <= TCP->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->socket == sock && sockptr->SocketActive) + { +#ifndef LINBPQ + switch (eventcode) + { + case FD_READ: + + if (sockptr->RelayMode) + return DataSocket_ReadRelay(TNC, sockptr, sock, &TNC->Streams[n]); + + if (sockptr->HTTPMode) + return DataSocket_ReadHTTP(TNC, sockptr, sock, n); + + if (sockptr->FBBMode) + return DataSocket_ReadFBB(TNC, sockptr, sock, n); + else + return DataSocket_Read(TNC, sockptr, sock, &TNC->Streams[n]); + + case FD_WRITE: + + return 0; + + case FD_OOB: + + return 0; + + case FD_ACCEPT: + + return 0; + + case FD_CONNECT: + + return 0; + + case FD_CLOSE: + + TNC->Streams[n].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + return 0; +#endif + } + + } + + return 0; +} + +*/ +#define PACLEN 100 + +VOID SendtoNode(struct TNCINFO * TNC, int Stream, char * Msg, int MsgLen) +{ + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == NULL) + return; // No buffers, so ignore + + if (TNC->Streams[Stream].Connected == 0) + { + // Connection Closed - Get Another + + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == FALSE) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return; + } + + if (sockptr->UserPointer) + TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->Secure_Session = sockptr->UserPointer->Secure; + } + + buffptr->Len= MsgLen; // Length + + memcpy(&buffptr->Data, Msg, MsgLen); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); +} + +int InnerProcessData(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM, int len); + + +int DataSocket_Read(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM) +{ + int len=0, maxlen; + char NLMsg[3]={13,10,0}; + byte * IACptr; + BOOL wait; + + struct TCPINFO * TCP = TNC->TCPInfo; + int SendIndex = 0; + byte * TelCommand; + int beforeIAC = 0; + int rest = 0; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len=maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len ==0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + // If message contains Telnet Commands we should process the data in the buffer first + // and not echo the commands! + + + IACptr = memchr(sockptr->InputBuffer, IAC, sockptr->InputLen + len); + + if (IACptr) + { + beforeIAC = IACptr - sockptr->InputBuffer; + rest = (sockptr->InputLen + len) - beforeIAC; + + if (beforeIAC) + { + TelCommand = malloc(rest); + memcpy(TelCommand, IACptr, rest); + InnerProcessData(TNC, sockptr, sock, STREAM, beforeIAC); + + // There may still be data in buffer, but it may be less than before + // Put IAC and following into buffer + + memcpy(&sockptr->InputBuffer[sockptr->InputLen], TelCommand, rest); + len -= sockptr->InputLen; + free(TelCommand); + } + } + +IACLoop: + + IACptr = memchr(sockptr->InputBuffer, IAC, sockptr->InputLen + len); + + if (IACptr) + { + // There still may be data in the buffer. + + wait = ProcessTelnetCommand(sockptr, IACptr, &rest); + + if (wait) + { + // Need more. + + sockptr->InputLen += len; + return 0; // wait for more chars + } + + // If ProcessTelnet Command returns FALSE, then it has removed the IAC and its + // params from the buffer. There may still be more to process. + + if (rest == 0) + return 0; // Nothing Left + + memmove(&sockptr->InputBuffer[sockptr->InputLen], IACptr + len - rest, rest); + len = rest; + + goto IACLoop; // There may be more + } + + return InnerProcessData(TNC, sockptr, sock, STREAM, len); +} + + +int InnerProcessData(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM, int len) +{ + int InputLen, MsgLen, i, n,charsAfter; + char NLMsg[3]={13,10,0}; + byte * CRPtr; + byte * LFPtr; + byte * BSptr; + byte * MsgPtr; + char logmsg[1000]; + struct UserRec * USER; + struct TCPINFO * TCP = TNC->TCPInfo; + int SendIndex = 0; + + // echo data just read + + if (sockptr->DoEcho && sockptr->LoginState != 1) // Password + send(sockptr->socket,&sockptr->InputBuffer[sockptr->InputLen], len, 0); + + sockptr->InputLen += len; + + // look for backspaces in data just read + + BSptr = memchr(&sockptr->InputBuffer[0], 8, sockptr->InputLen); + + if (BSptr == NULL) + BSptr = memchr(&sockptr->InputBuffer[0], 127, sockptr->InputLen); + + if (BSptr != 0) + { + // single char or BS as last is most likely, and worth treating as a special case + + int n; + + charsAfter = sockptr->InputLen - (int)((BSptr-&sockptr->InputBuffer[0])) - 1; + + if (charsAfter == 0) + { + sockptr->InputLen--; + if (sockptr->InputLen > 0) + sockptr->InputLen--; //Remove last char + + goto noBS; + } + // more than single char. Copy stuff after bs over char before + + memmove(BSptr-1, BSptr+1, charsAfter); + + n = sockptr->InputLen; + + sockptr->InputLen -= 2; // drop bs and char before + + // see if more bs chars +BSCheck: + BSptr = memchr(&sockptr->InputBuffer[0], 8, sockptr->InputLen); + + if (BSptr == NULL) + BSptr = memchr(&sockptr->InputBuffer[0], 127, sockptr->InputLen); + + if (BSptr == NULL) + goto noBS; + + charsAfter = sockptr->InputLen - (int)((BSptr-&sockptr->InputBuffer[0])) - 1; + + if (charsAfter == 0) + { + sockptr->InputLen--; // Remove BS + if (sockptr->InputLen > 0) + sockptr->InputLen--; //Remove last char if not at start + goto noBS; + } + + memmove(BSptr-1, BSptr+1, charsAfter); + sockptr->InputLen--; // Remove BS + if (sockptr->InputLen > 0) sockptr->InputLen--; //Remove last char if not at start + + goto BSCheck; // may be more bs chars + } + +noBS: + + // Extract lines from input stream + + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + +MsgLoop: + + // if in Client Mode, accept CR, CR Null, CR LF or LF, and replace with CR + // Also send immediately to client - dont wait for complete lines + + if (sockptr->ClientSession) + { + int n = InputLen; + char * ptr = MsgPtr; + char * Start = MsgPtr; + char c; + int len = 0; + char NodeLine[300]; + char * optr = NodeLine; + + while (n--) + { + c = *(ptr++); + + if (c == 0) + // Ignore Nulls + continue; + + len++; + *(optr++) = c; + + if (c == 13) + { + // See if next is lf or null + + if (n) + { + // Some Left + + if ((*ptr) == 0 || *(ptr) == 10) + { + // skip next + + n--; + ptr++; + } + } + } + else if (c == 10) + { + *(optr - 1) = 13; + } + else + { + // Normal Char + + if (len >= PACLEN) + { + BuffertoNode(sockptr, NodeLine, len); + optr = NodeLine; + len = 0; + } + } + } + + // All scanned - send anything outstanding + + if (len) + BuffertoNode(sockptr, NodeLine, len); + + sockptr->InputLen = 0; + ShowConnections(TNC);; + + return 0; + } + + + // Server Mode + + CRPtr=memchr(MsgPtr, 13, InputLen); + + if (CRPtr) + { + // Convert CR Null to CR LF + + LFPtr=memchr(MsgPtr, 0, InputLen); + + if (LFPtr && *(LFPtr - 1) == 13) // Convert CR NULL to CR LF + { + *LFPtr = 10; // Replace NULL with LF + send(sockptr->socket, LFPtr, 1, 0); // And echo it + } + } + + // could just have LF?? + + LFPtr=memchr(MsgPtr, 10, InputLen); + + if (LFPtr == 0) + if (CRPtr) + { + LFPtr = ++CRPtr; + InputLen++; + } + if (LFPtr == 0) + { + // Check Paclen + + if (InputLen > PACLEN) + { + if (sockptr->LoginState != 2) // Normal Data + { + // Long message received when waiting for user or password - just ignore + + sockptr->InputLen=0; + + return 0; + } + + // Send to Node + + // Line could be up to 500 chars if coming from a program rather than an interative user + // Limit send to node to 255 + + while (InputLen > 255) + { + SendtoNode(TNC, sockptr->Number, MsgPtr, 255); + sockptr->InputLen -= 255; + InputLen -= 255; + + memmove(MsgPtr,MsgPtr+255,InputLen); + } + + SendtoNode(TNC, sockptr->Number, MsgPtr, InputLen); + + sockptr->InputLen = 0; + + } // PACLEN + + return 0; // No CR + } + + // Got a LF + + // Process data up to the cr + + MsgLen = (int)(LFPtr-MsgPtr); // Include the CR but not LF + + switch (sockptr->LoginState) + { + + case 2: + + // Normal Data State + + STREAM->BytesRXed += MsgLen; + SendIndex = 0; + + // Line could be up to 500 chars if coming from a program rather than an interative user + // Limit send to node to 255. Should really use PACLEN instead of 255.... + + while (MsgLen > 255) + { + SendtoNode(TNC, sockptr->Number, MsgPtr + SendIndex, 255); + SendIndex += 255; + MsgLen -= 255; + } + + SendtoNode(TNC, sockptr->Number, MsgPtr + SendIndex, MsgLen); + + MsgLen += SendIndex; + + // If anything left, copy down buffer, and go back + + InputLen=InputLen-MsgLen-1; + + sockptr->InputLen=InputLen; + + if (InputLen > 0) + { + memmove(MsgPtr,LFPtr+1,InputLen); + + goto MsgLoop; + } + + return 0; + + case 0: + + // Check Username + // + + *(LFPtr-1)=0; // remove cr + + // send(sock, NLMsg, 2, 0); + + if (LogEnabled) + { + char Addr[256]; + + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + Debugprintf("Telnet Bad User Name %s", MsgPtr); + MsgPtr[64] = 0; + } + + sprintf(logmsg,"%d %s User=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (USER == NULL) + continue; + + if (_stricmp(USER->UserName, "ANON") == 0) + { + // Anon Login - Callsign is supplied as user + + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, _strupr(MsgPtr)); //' for *** linked + } + else if (strcmp(MsgPtr,USER->UserName) == 0) + { + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, USER->Callsign); //' for *** linked + } + else + continue; + + send(sock, TCP->PasswordMsg, (int)strlen(TCP->PasswordMsg),0); + + sockptr->Retries = 0; + + sockptr->LoginState = 1; + sockptr->InputLen = 0; + + n=sockptr->Number; +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, MsgPtr); +#endif + ShowConnections(TNC);; + return 0; + } + + // Not found + + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg,sizeof(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->LoginMsg, (int)strlen(TCP->LoginMsg), 0); + sockptr->InputLen=0; + } + + return 0; + + case 1: + + *(LFPtr-1)=0; // remove cr + + send(sock, NLMsg, 2, 0); // Need to echo NL, as password is not echoed + + if (LogEnabled) + { + char Addr[256]; + + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + Debugprintf("Telnet Bad Password %s", MsgPtr); + MsgPtr[64] = 0; + } + + + sprintf(logmsg,"%d %s Password=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + + if (strcmp(MsgPtr, sockptr->UserPointer->Password) == 0) { + char * ct = TCP->cfgCTEXT; + char * Appl; + int ctlen = (int)strlen(ct); + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == FALSE) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->Secure_Session = sockptr->UserPointer->Secure; + + sockptr->LoginState = 2; + sockptr->InputLen = 0; + + if (ctlen > 0) send(sock, ct, ctlen, 0); + + STREAM->BytesTXed = ctlen; + + if (LogEnabled) + { + char Addr[100]; + + Tel_Format_Addr(sockptr, Addr); + sprintf(logmsg,"%d %s Call Accepted Callsign=%s\n", sockptr->Number, Addr, sockptr->Callsign); + WriteLog (logmsg); + } + + Appl = sockptr->UserPointer->Appl; + + if (Appl[0]) + SendtoNode(TNC, sockptr->Number, Appl, (int)strlen(Appl)); + + ShowConnections(TNC); + + return 0; + } + + // Bad Password + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg, (int)strlen(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect (TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->PasswordMsg, (int)strlen(TCP->PasswordMsg), 0); + sockptr->InputLen=0; + } + + return 0; + + default: + + return 0; + + } + + return 0; +} + +int DataSocket_ReadRelay(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM) +{ + int len=0, maxlen, InputLen, MsgLen, n; + char NLMsg[3]={13,10,0}; + byte * LFPtr; + byte * MsgPtr; + char logmsg[256]; + char RelayMsg[] = "No CMS connection available - using local BPQMail\r"; + struct TCPINFO * TCP = TNC->TCPInfo; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len=maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + + if (len == SOCKET_ERROR || len ==0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + sockptr->InputLen+=len; + + // Extract lines from input stream + + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + + STREAM->BytesRXed += InputLen; + + if (sockptr->LoginState == 2) + { + // Data. FBB is binary + + // Send to Node + + // Queue to Node. Data may arrive it large quatities, possibly exceeding node buffer capacity + + STREAM->BytesRXed += InputLen; + + if (sockptr->FromHostBuffPutptr + InputLen > sockptr->FromHostBufferSize) + { + if (InputLen > 10000) + sockptr->FromHostBufferSize += InputLen; + else + sockptr->FromHostBufferSize += 10000; + + sockptr->FromHostBuffer = realloc(sockptr->FromHostBuffer, sockptr->FromHostBufferSize); + } + + memcpy(&sockptr->FromHostBuffer[sockptr->FromHostBuffPutptr], MsgPtr, InputLen); + + sockptr->FromHostBuffPutptr += InputLen; + sockptr->InputLen = 0; + + return 0; + } +/* + if (InputLen > 256) + { + SendtoNode(TNC, sockptr->Number, MsgPtr, 256); + sockptr->InputLen -= 256; + + InputLen -= 256; + + memmove(MsgPtr,MsgPtr+256,InputLen); + + goto MsgLoop; + } + + SendtoNode(TNC, sockptr->Number, MsgPtr, InputLen); + sockptr->InputLen = 0; + + return 0; + } +*/ + if (InputLen > 256) + { + // Long message received when waiting for user or password - just ignore + + sockptr->InputLen=0; + + return 0; + } + + LFPtr=memchr(MsgPtr, 13, InputLen); + + if (LFPtr == 0) + return 0; // Waitr for more + + // Got a CR + + // Process data up to the cr + + MsgLen = (int)(LFPtr-MsgPtr); + + switch (sockptr->LoginState) + { + + case 0: + + // Check Username + // + + *(LFPtr)=0; // remove cr + + if (*MsgPtr == '.') + MsgPtr++; + + if (strlen(MsgPtr) == 0) + { + DataSocket_Disconnect(TNC, sockptr); // Silently disconnect - should only be used for automatic systems + return 0; + } + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d User=%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + MsgPtr); + + WriteLog (logmsg); + } + + strcpy(sockptr->Callsign, _strupr(MsgPtr)); + + // Save callsign for *** linked + + send(sock, "Password :\r", 11,0); + + sockptr->Retries = 0; + sockptr->LoginState = 1; + sockptr->InputLen = 0; + + n=sockptr->Number; +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, MsgPtr); +#endif + ShowConnections(TNC);; + + return 0; + + + case 1: + + *(LFPtr)=0; // remove cr + + if (strlen(MsgPtr) == 0) + { + DataSocket_Disconnect(TNC, sockptr); // Silently disconnect - should only be used for automatic systems + return 0; + } + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d Password=%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + MsgPtr); + + WriteLog (logmsg); + } + + if (strchr(MsgPtr, '$')) + { + // Special format Password for PAT Gateway Mode + + char * Port = strlop(MsgPtr, '$'); + char * Call; + int PortNo; + char ConMsg[80]; + + if (Port) + { + Call = strlop(Port, '$'); + + if (Call) + { + struct PORTCONTROL * PORT; + + PortNo = atoi(Port); + PORT = GetPortTableEntryFromPortNum(PortNo); + + if (PORT == NULL || PORT->PROTOCOL < 10) + sprintf(ConMsg, "C %s %s", Port, Call); + else + sprintf(ConMsg, "ATT %s %s", Port, Call); + + } + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == 0) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + sockptr->LoginState = 2; + + sockptr->InputLen = 0; + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d Gateway Connect Call=%s Command=%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + sockptr->Callsign,ConMsg); + + WriteLog (logmsg); + } + + // Send Command to Node + + strcat(ConMsg, "\r"); + SendtoNode(TNC, sockptr->Number, ConMsg, (int)strlen(ConMsg)); + } + + return 0; + } + + sockptr->UserPointer = &RelayUser; + + if (ProcessIncommingConnectEx(TNC, sockptr->Callsign, sockptr->Number, FALSE, TRUE) == 0) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + if (TCP->FallbacktoRelay) + send(sock, RelayMsg, (int)strlen(RelayMsg), 0); + + sockptr->LoginState = 2; + + sockptr->InputLen = 0; + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d Call Accepted Callsign =%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + sockptr->Callsign); + + WriteLog (logmsg); + } + + ShowConnections(TNC); + + sockptr->InputLen = 0; + + // Connect to the BBS + + SendtoNode(TNC, sockptr->Number, TCP->RelayAPPL, (int)strlen(TCP->RelayAPPL)); + + ShowConnections(TNC);; + + return 0; + + default: + + return 0; + + } + + return 0; +} +#define ZEXPORT WINAPI + +#include "zlib.h" + +int DataSocket_ReadSync(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int len=0, maxlen, InputLen; + byte * MsgPtr; + struct TCPINFO * TCP = TNC->TCPInfo; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + sockptr->InputLen+=len; + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + MsgPtr[InputLen] = 0; + + STREAM->BytesRXed += InputLen; + + if (sockptr->LoginState == 0) // Initial connection + { + // if (TNC->Streams[Stream].Connected == 0) + + strcpy(sockptr->Callsign, "SYNC"); + + sockptr->UserPointer = &SyncUser; + + SendtoNode(TNC, sockptr->Number, TCP->SyncAPPL, (int)strlen(TCP->SyncAPPL)); + BuffertoNode(sockptr, MsgPtr, InputLen); + STREAM->RelaySyncStream = 1; + sockptr->LoginState = 1; + + ShowConnections(TNC); + return 0; + } + + // Queue to Node. Data may arrive in large quantities, possibly exceeding node buffer capacity + + BuffertoNode(sockptr, MsgPtr, InputLen); + sockptr->InputLen = 0; + + return 0; +} + + + +int DataSocket_ReadFBB(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int len=0, maxlen, InputLen, MsgLen, i, n; + char NLMsg[3]={13,10,0}; + byte * CRPtr; + byte * MsgPtr; + char logmsg[1000]; + struct UserRec * USER; + struct TCPINFO * TCP = TNC->TCPInfo; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + sockptr->InputLen+=len; + + // Extract lines from input stream + + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + MsgPtr[InputLen] = 0; + + if (sockptr->LoginState == 0) + { + // Look for FLMSG Header + + if (InputLen > 10 && memcmp(MsgPtr, "... start\n", 10) == 0) + { + MsgPtr[9] = 13; // Convert to CR + sockptr->LoginState = 2; // Set Logged in + + SendtoNode(TNC, Stream, "..FLMSG\r", 8); // Dummy command to command handler + + } + } + +MsgLoop: + + if (sockptr->LoginState == 2) + { + // Data. FBB is binary + + int Paclen = 0; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) + Paclen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + +// if (Paclen == 0) + Paclen = 256; + + if (sockptr->BPQTermMode) + { + if (memcmp(MsgPtr, "\\\\\\\\", 4) == 0) + { + // Monitor Control + + int P8 = 0; + + int n = sscanf(&MsgPtr[4], "%llx %x %x %x %x %x %x %x", + &sockptr->MMASK, &sockptr->MTX, &sockptr->MCOM, &sockptr->MonitorNODES, + &sockptr->MonitorColour, &sockptr->MUIOnly, &sockptr->UTF8, &P8); + + if (n == 5) + sockptr->MUIOnly = sockptr->UTF8 = 0; + + if (n == 6) + sockptr->UTF8 = 0; + + if (P8 == 1) + SendPortsForMonitor(sock, sockptr->UserPointer->Secure); + sockptr->InputLen = 0; + return 0; + } + } + + if (sockptr->UserPointer == &CMSUser) + { + WritetoTrace(Stream, MsgPtr, InputLen, sockptr->ADIF, 'R'); + } + + if (InputLen == 8 && memcmp(MsgPtr, ";;;;;;\r\n", 8) == 0) + { + // CMS Keepalive + + sockptr->InputLen = 0; + return 0; + } + + // Queue to Node. Data may arrive it large quantities, possibly exceeding node buffer capacity + + STREAM->BytesRXed += InputLen; + BuffertoNode(sockptr, MsgPtr, InputLen); + sockptr->InputLen = 0; + + return 0; + } + + if (InputLen > 256) + { + // Long message received when waiting for user or password - just ignore + + sockptr->InputLen=0; + + return 0; + } + + if (MsgPtr[0] == 10) // LF + { + // Remove the LF + + InputLen--; + sockptr->InputLen--; + + memmove(MsgPtr, MsgPtr+1, InputLen); + } + + CRPtr = memchr(MsgPtr, 13, InputLen); + + if (CRPtr == 0) + return 0; // Waitr for more + + // Got a CR + + // Process data up to the cr + + MsgLen = (int)(CRPtr - MsgPtr); + + if (MsgLen == 0) // Just CR + { + MsgPtr++; // Skip it + InputLen--; + sockptr->InputLen--; + goto MsgLoop; + } + + + switch (sockptr->LoginState) + { + case 5: + + // Trimode Emulator Command + + *CRPtr = 0; + + ProcessTrimodeCommand(TNC, sockptr, MsgPtr); + + MsgLen++; + + InputLen -= MsgLen; + + memmove(MsgPtr, MsgPtr+MsgLen, InputLen); + sockptr->InputLen = InputLen ; + MsgPtr[InputLen] = 0; + + + goto MsgLoop; + + case 3: + + // CMS Signon + + strlop(MsgPtr, 13); + + sprintf(logmsg,"%d %s\r\n", Stream, MsgPtr); + WriteCMSLog (logmsg); + + if (strstr(MsgPtr, "Callsign :")) + { + char Msg[80]; + int Len; + + if (sockptr->LogonSent) + { + sockptr->InputLen=0; + return TRUE; + } + + sockptr->LogonSent = TRUE; + + if (TCP->SecureCMSPassword[0] && sockptr->RelaySession == 0) + Len = sprintf(Msg, "%s %s\r", TNC->Streams[sockptr->Number].MyCall, TCP->GatewayCall); + else + Len = sprintf(Msg, "%s\r", TNC->Streams[sockptr->Number].MyCall); + + if (sockptr->ADIF == NULL) + { + sockptr->ADIF = malloc(sizeof(struct ADIF)); + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + } + + strcpy(sockptr->ADIF->CMSCall, TCP->GatewayCall); + + send(sock, Msg, Len, 0); + sprintf(logmsg,"%d %s\n", Stream, Msg); + WriteCMSLog (logmsg); + + sockptr->InputLen=0; + + return TRUE; + } + if (memcmp(MsgPtr, ";SQ: ", 5) == 0) + { + // Secure CMS challenge + + char Msg[80]; + int Len; + int Response = GetCMSHash(&MsgPtr[5], TCP->SecureCMSPassword); + char RespString[12]; + long long Freq = 0; + int Mode = 0; + ADIF * ADIF = sockptr->ADIF; + + strcat(MsgPtr,""); + UpdateADIFRecord(ADIF, MsgPtr, 'R'); + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // if Session has report info, use it + + if (Sess2->Mode) + { + ADIF->Freq = Freq = Sess2->Frequency; + ADIF->Mode = Mode = Sess2->Mode; + } + else + { + // See if L2 session - if so, get info from WL2K report line + + if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + ADIF->Freq = Freq = PORT->WL2KInfo.Freq; + ADIF->Mode = Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + ADIF->Freq = Freq = Sess2->Frequency; + ADIF->Mode = Mode = Sess2->Mode; + } + } + } + } + } + + sprintf(RespString, "%010d", Response); + + Len = sprintf(Msg, ";SR: %s %lld %d\r", &RespString[2], Freq, Mode); + + send(sock, Msg, Len,0); + sprintf(logmsg,"%d %s\n", Stream, Msg); + WriteCMSLog (logmsg); + + strcat(Msg,""); + UpdateADIFRecord(ADIF, Msg, 'S'); + + sockptr->InputLen=0; + sockptr->LoginState = 2; // Data + sockptr->LogonSent = FALSE; + + return TRUE; + } + + if (strstr(MsgPtr, "Password :")) + { + // Send “CMSTelnet” + gateway callsign + frequency + emission type if info is available + + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + char Passline[80] = "CMSTELNET\r"; + int len = 10; + ADIF * ADIF = sockptr->ADIF; + + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // if Session has report info, use it + + if (Sess2->Mode) + { + ADIF->Freq = Sess2->Frequency; + ADIF->Mode = Sess2->Mode; + } + else + { + // See if L2 session - if so, get info from WL2K report line + + if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + if (PORT->WL2KInfo.Freq) + { + len = sprintf(Passline, "CMSTELNET %s %lld %d\r", PORT->WL2KInfo.RMSCall, PORT->WL2KInfo.Freq, PORT->WL2KInfo.mode); + ADIF->Freq = PORT->WL2KInfo.Freq; + ADIF->Mode = PORT->WL2KInfo.mode; + } + } + else + { + if (Sess2->RMSCall[0]) + { + len = sprintf(Passline, "CMSTELNET %s %lld %d\r", Sess2->RMSCall, Sess2->Frequency, Sess2->Mode); + ADIF->Mode = Sess2->Frequency; + ADIF->Mode = Sess2->Mode; + } + } + } + } + } + send(sock, Passline, len, 0); + sockptr->LoginState = 2; // Data + sockptr->InputLen=0; + sockptr->LogonSent = FALSE; + + if (CMSLogEnabled) + { + char logmsg[120]; + sprintf(logmsg,"%d %s\r\n", sockptr->Number, Passline); + WriteCMSLog (logmsg); + } + + return TRUE; + } + + return TRUE; + + case 0: + + // Check Username + // + + *(CRPtr)=0; // remove cr + + if (LogEnabled) + { + char Addr[256]; + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + Debugprintf("Telnet Bad User Name %s", MsgPtr); + MsgPtr[64] = 0; + } + + sprintf(logmsg,"%d %s User=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (USER == NULL) + continue; + + if (_stricmp(USER->UserName, "ANON") == 0) + { + // Anon Login - Callsign is supplied as user + + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, _strupr(MsgPtr)); //' for *** linked + } + else if (strcmp(MsgPtr,USER->UserName) == 0) + { + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, USER->Callsign); //' for *** linked + + } + else + continue; + + sockptr->Retries = 0; + + sockptr->LoginState = 1; + sockptr->InputLen = 0; + + n=sockptr->Number; + +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, MsgPtr); +#endif + + ShowConnections(TNC);; + + InputLen=InputLen-(MsgLen+1); + + sockptr->InputLen=InputLen; + + if (InputLen > 0) + { + memmove(MsgPtr, CRPtr+1, InputLen); + goto MsgLoop; + } + + return 0; + } + + // User Not found + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg,sizeof(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->LoginMsg, (int)strlen(TCP->LoginMsg), 0); + sockptr->InputLen=0; + + } + + return 0; + + case 1: + + *(CRPtr)=0; // remove cr + + if (LogEnabled) + { + char Addr[256]; + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + Debugprintf("Telnet Bad Password %s", MsgPtr); + MsgPtr[64] = 0; + } + + sprintf(logmsg,"%d %s Password=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + if (strcmp(MsgPtr, sockptr->UserPointer->Password) == 0) + { + char * Appl; + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == FALSE) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->Secure_Session = sockptr->UserPointer->Secure; + + sockptr->LoginState = 2; + + sockptr->InputLen = 0; + + if (LogEnabled) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(logmsg,"%d %s Call Accepted. Callsign=%s\n", + sockptr->Number, Addr,sockptr->Callsign); + + WriteLog (logmsg); + } + + ShowConnections(TNC);; + InputLen=InputLen-(MsgLen+1); + + sockptr->InputLen=InputLen; + + // What is left is the Command to connect to the BBS + + if (InputLen > 1) + { + if (*(CRPtr+1) == 10) + { + CRPtr++; + InputLen--; + } + + memmove(MsgPtr, CRPtr+1, InputLen); + + if (_memicmp(MsgPtr, "BPQTermTCP", 10) == 0) + { + send(sock, "Connected to TelnetServer\r", 26, 0); + sockptr->BPQTermMode = TRUE; + sockptr->MMASK = 0; // Make sure defaults to off + sockptr->InputLen -= 11; + + if (sockptr->InputLen) + { + // Monitor control info may arrive in same packet + + int P8 = 0; + + memmove(MsgPtr, &MsgPtr[11], InputLen); + if (memcmp(MsgPtr, "\\\\\\\\", 4) == 0) + { + // Monitor Control + + int n = sscanf(&MsgPtr[4], "%llx %x %x %x %x %x %x %x", + &sockptr->MMASK, &sockptr->MTX, &sockptr->MCOM, &sockptr->MonitorNODES, + &sockptr->MonitorColour, &sockptr->MUIOnly, &sockptr->UTF8, &P8); + + if (n == 5) + sockptr->MUIOnly = sockptr->UTF8 = 0; + + if (n == 6) + sockptr->UTF8 = 0; + + if (P8 == 1) + SendPortsForMonitor(sock, sockptr->UserPointer->Secure); + + + sockptr->InputLen = 0; + } + } + } + else + { + MsgPtr[InputLen] = 13; + SendtoNode(TNC, sockptr->Number, MsgPtr, InputLen+1); + } + sockptr->InputLen = 0; + } + + Appl = sockptr->UserPointer->Appl; + + if (Appl[0]) + SendtoNode(TNC, sockptr->Number, Appl, (int)strlen(Appl)); + + return 0; + } + // Bad Password + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg, (int)strlen(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->PasswordMsg, (int)strlen(TCP->PasswordMsg), 0); + sockptr->InputLen=0; + } + + return 0; + + default: + + return 0; + } + return 0; +} + +extern char * RigWebPage; + +int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int w =1, x= 1, len=0, y = 2, maxlen, InputLen, ret; + char NLMsg[3]={13,10,0}; + UCHAR * MsgPtr; + UCHAR * CRLFCRLF; + UCHAR * LenPtr; + int BodyLen, ContentLen; + struct ConnectionInfo * sockcopy; + + ret = ioctl(sock,FIONREAD,&w); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (w > maxlen) w = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], w, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + MsgPtr = &sockptr->InputBuffer[0]; + sockptr->InputLen += len; + InputLen = sockptr->InputLen; + + MsgPtr[InputLen] = 0; + + if (sockptr->WebSocks) + { + // Websocks message + + int i, j; + int Fin, Opcode, Len, Mask; + char MaskingKey[4]; + char * ptr; + + /* + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + Octet i of the transformed data ("transformed-octet-i") is the XOR of + octet i of the original data ("original-octet-i") with octet at index + i modulo 4 of the masking key ("masking-key-octet-j"): + + j = i MOD 4 + transformed-octet-i = original-octet-i XOR masking-key-octet-j +*/ + Fin = MsgPtr[0] >> 7; + Opcode = MsgPtr[0] & 15; + Mask = MsgPtr[1] >> 7; + Len = MsgPtr[1] & 127; + memcpy(MaskingKey, &MsgPtr[2], 4); + ptr = &MsgPtr[6]; + + for (i = 0; i < Len; i++) + { + j = i & 3; + + *ptr = *ptr ^ MaskingKey[j]; + ptr++; + } + + if (Opcode == 8) + { + Debugprintf("WebSock Close"); + } + else if (Opcode == 1) + { + if (strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + // PTT Message + + char RigCMD[64]; + + sprintf(RigCMD, "%s PTT", &MsgPtr[6]); + Rig_Command(-1, RigCMD); + } + else + Debugprintf("WebSock Opcode %d Msg %s", Opcode, &MsgPtr[6]); + + } + + sockptr->InputLen = 0; + return 0; + } + + // Make sure request is complete - should end crlfcrlf, and if a post have the required input message + + + CRLFCRLF = strstr(MsgPtr, "\r\n\r\n"); + + if (CRLFCRLF == 0) + return 0; + + LenPtr = strstr(MsgPtr, "Content-Length:"); + + if (LenPtr) + { + ContentLen = atoi(LenPtr + 15); + BodyLen = InputLen - (int)((CRLFCRLF + 4 - MsgPtr)); + + if (BodyLen < ContentLen) + return 0; + } + + sockcopy = malloc(sizeof(struct ConnectionInfo)); + sockptr->TNC = TNC; + sockptr->LastSendTime = REALTIMETICKS; + + memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo)); + + if(strstr(MsgPtr, "Upgrade: websocket")) + { + if(RigWebPage[0]) + { + int LOCAL = 0, COOKIE = 0; + char * HostPtr; + char * ptr; + + sockptr->WebSocks = 1; + memcpy(sockptr->WebURL, &MsgPtr[5], 15); + strlop(sockptr->WebURL, ' '); + RigWebPage[0] = 0; + + HostPtr = strstr(MsgPtr, "Host: "); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = sockptr->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else + { + if (sockptr->sin.sin_family != AF_INET6) + { + while(LocalNet) + { + uint32_t MaskedHost = sockptr->sin.sin_addr.s_addr & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + LOCAL = 1; + break; + } + LocalNet = LocalNet->Next; + } + } + + ptr = strstr(MsgPtr, "BPQSessionCookie=N"); + + if (ptr) + COOKIE = TRUE; + } + sockptr->WebSecure = LOCAL | COOKIE; + } + } + } + + _beginthread(ProcessHTTPMessage, 2048000, (VOID *)sockcopy); + + sockptr->InputLen = 0; + return 0; +} + +int DataSocket_ReadDRATS(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int len=0, maxlen; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + DRATSConnectionLost(sockptr); + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + // Make sure request is complete - should end [EOB] + + processDRATSFrame(&sockptr->InputBuffer[sockptr->InputLen], len, sockptr); + return 0; +} + + +int DataSocket_Disconnect(struct TNCINFO * TNC, struct ConnectionInfo * sockptr) +{ + int n; + + if (sockptr->SocketActive) + { + if (sockptr->socket) + closesocket(sockptr->socket); + + n = sockptr->Number; +#ifndef LINBPQ + ModifyMenu(TNC->TCPInfo->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, "."); +#endif + sockptr->SocketActive = FALSE; + ShowConnections(TNC);; + } + return 0; +} + +int ShowConnections(struct TNCINFO * TNC) +{ +#ifndef LINBPQ + char msg[80]; + struct ConnectionInfo * sockptr; + int i,n; + + SendMessage(TNC->hMonitor,LB_RESETCONTENT,0,0); + + for (n = 0; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr=TNC->Streams[n].ConnectionInfo; + + if (!sockptr->SocketActive) + { + strcpy(msg,"Idle"); + } + else + { + if (sockptr->UserPointer == 0) + { + if (sockptr->HTTPMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg, "HTTP From %s", Addr); + } + else if (sockptr->DRATSMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg, "DRATS From %s", Addr); + } + else + strcpy(msg,"Logging in"); + } + else + { + i=sprintf(msg,"%-10s %-10s %2d", + sockptr->UserPointer->UserName, sockptr->Callsign, sockptr->FromHostBuffPutptr - sockptr->FromHostBuffGetptr); + } + } + SendMessage(TNC->hMonitor, LB_ADDSTRING ,0, (LPARAM)msg); + } +#endif + return 0; +} +byte * EncodeCall(byte * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; +} +BOOL ProcessTelnetCommand(struct ConnectionInfo * sockptr, byte * Msg, int * Len) +{ + int cmd, TelOption; + int used; + char WillSupGA[3]={IAC,WILL,suppressgoahead}; + char WillEcho[3]={IAC,WILL,echo}; + char Wont[3]={IAC,WONT,echo}; + char Dont[3]={IAC,DONT,echo}; + + // Note Msg points to the IAC, which may not be at the start of the receive buffer + // Len is number of bytes left in buffer including the IAC + + if (*Len < 2) return TRUE; //' Wait for more + + cmd = Msg[1]; + + if (cmd == DOx || cmd == DONT || cmd == WILL || cmd == WONT) + if (*Len < 3) return TRUE; //' wait for option + + TelOption = Msg[2]; + + switch (cmd) + { + case DOx: + + switch (TelOption) + { + case echo: + sockptr->DoEcho = TRUE; + send(sockptr->socket,WillEcho,3,0); + break; + + case suppressgoahead: + + send(sockptr->socket,WillSupGA,3,0); + break; + + default: + + Wont[2] = TelOption; + send(sockptr->socket,Wont,3,0); + } + + used=3; + + break; + + case DONT: + + // Debug.Print "DONT"; TelOption + + switch (TelOption) + { + case echo: + sockptr->DoEcho = FALSE; + break; + } + + Wont[2] = TelOption; + send(sockptr->socket,Wont,3,0); + + used=3; + + break; + + case WILL: + + // Debug.Print "WILL"; TelOption + +// if (TelOption == echo) sockptr->DoEcho = TRUE; + + Dont[2] = TelOption; + send(sockptr->socket, Dont, 3, 0); + + used=3; + + break; + + case WONT: + +// Debug.Print "WONT"; TelOption + + used=3; + + break; + + default: + + used=2; + + } + + // remove the processed command from the buffer + + *Len -= used; + + return FALSE; +} + + +int WriteLog(char * msg) +{ + FILE *file; + char timebuf[128]; + time_t ltime; + + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + if (LogDirectory[0] == 0) + { + strcpy(Value, "logs/Telnet_"); + } + else + { + strcpy(Value, LogDirectory); + strcat(Value, "/"); + strcat(Value, "logs/Telnet_"); + } + + sprintf(Value, "%s%02d%02d%02d.log", Value, + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + + if ((file = fopen(Value, "a")) == NULL) + return FALSE; + + time(<ime); + +#ifdef LINBPQ + { + struct tm * tmp = localtime(<ime); + strftime( timebuf, 128, + "%d/%m/%Y %H:%M:%S ", tmp ); + } +#else + { + struct tm * today; + + today = localtime(<ime); + strftime(timebuf, 128, "%d/%m/%Y %H:%M:%S ", today); + } +#endif + fputs(timebuf, file); + fputs(msg, file); + fclose(file); + return 0; +} + +char LastCMSLog[256]; + +VOID WriteCMSLog(char * msg) +{ + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + FILE * Handle; + char LogMsg[256]; + int MsgLen; + + if (CMSLogEnabled == FALSE) + return; + + T = time(NULL); + tm = gmtime(&T); + + if (LogDirectory[0] == 0) + { + strcpy(Value, "logs/CMSAccess"); + } + else + { + strcpy(Value, LogDirectory); + strcat(Value, "/"); + strcat(Value, "logs/CMSAccess"); + } + + sprintf(Value, "%s_%04d%02d%02d.log", Value, + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); + + Handle = fopen(Value, "ab"); + + if (Handle == NULL) + return; + + MsgLen = sprintf(LogMsg, "%02d:%02d:%02d %s", tm->tm_hour, tm->tm_min, tm->tm_sec, msg); + + fwrite(LogMsg , 1, MsgLen, Handle); + + fclose(Handle); + +#ifndef WIN32 + + if (strcmp(Value, LastCMSLog)) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/CMSAccessLatest.log", BPQDirectory); + unlink(SYMLINK); + strcpy(LastCMSLog, Value); + symlink(Value, SYMLINK); + } + +#endif + + return; +} + + + + + + +int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Error) +{ + struct TCPINFO * TCP = TNC->TCPInfo; + PMSGWITHLEN buffptr; + int Stream = sockptr->Number; + char Signon[80]; + int errlen = 4; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return 0; // No buffers, so ignore + +#ifndef WIN32 + +// SO_ERROR codes + +//#define ETIMEDOUT 110 /* Connection timed out */ +//#define ECONNREFUSED 111 /* Connection refused */ +//#define EHOSTDOWN 112 /* Host is down */ +//#define EHOSTUNREACH 113 /* No route to host */ +//#define EALREADY 114 /* Operation already in progress */ +//#define EINPROGRESS 115 /* Operation now in progress */ + + if (Error == 0) + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&Error, &errlen); + +// Debugprintf("Except Event Error after opts = %d", Error); +#endif + + if (Error) + { + if (sockptr->CMSSession && sockptr->RelaySession == 0) + { + // Try Next + + TCP->CMSFailed[sockptr->CMSIndex] = TRUE; + + if (CMSConnect(TNC, TNC->TCPInfo, &TNC->Streams[Stream], Stream)) + return 0; + + // Connect failure - if no more servers to check look for FALLBACKTORELAY + + return 0; + } + else + { + int err = 0; + int errlen = 4; + + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Failed to Connect\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + closesocket(sock); + TNC->Streams[Stream].Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + TNC->Streams[Stream].NeedDisc = 10; + return 0; + } + } + + sockptr->LogonSent = FALSE; + + if (sockptr->CMSSession) + { + sockptr->LoginState = 3; // Password State + + sockptr->UserPointer = &CMSUser; + strcpy(sockptr->Callsign, TNC->Streams[Stream].MyCall); + + sockptr->DoEcho = FALSE; + sockptr->FBBMode = TRUE; + sockptr->RelayMode = FALSE; + sockptr->ClientSession = FALSE; + + if (TCP->CMS) + SaveCMSHostInfo(TNC->Port, TNC->TCPInfo, sockptr->CMSIndex); + + if (CMSLogEnabled) + { + char logmsg[120]; + + if (sockptr->RelaySession) + sprintf(logmsg,"%d %s Connected to RELAY\r\n", sockptr->Number, TNC->Streams[Stream].MyCall); + else + sprintf(logmsg,"%d %s Connected to CMS\r\n", sockptr->Number, TNC->Streams[Stream].MyCall); + + WriteCMSLog (logmsg); + } + + if (sockptr->RelaySession) + buffptr->Len = sprintf(&buffptr->Data[0], "*** %s Connected to RELAY\r", TNC->Streams[Stream].MyCall); + else + buffptr->Len = sprintf(&buffptr->Data[0], "*** %s Connected to CMS\r", TNC->Streams[Stream].MyCall); + } + else + { + sockptr->LoginState = 2; // Data State + sockptr->UserPointer = &HostUser; + strcpy(sockptr->Callsign, TNC->Streams[Stream].MyCall); + sockptr->DoEcho = FALSE; + sockptr->ClientSession = TRUE; + + if (sockptr->SyncMode) + { + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to SYNC\r"); + send(sockptr->socket, sockptr->Signon, (int)strlen(sockptr->Signon), 0); + } + else + { + if (sockptr->Signon[0]) + { + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to Server\r"); + send(sockptr->socket, sockptr->Signon, (int)strlen(sockptr->Signon), 0); + } + else + { + buffptr->Len = sprintf(&buffptr->Data[0], "Connected to %s\r", + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4CROSSLINK->APPL); + + if (sockptr->NoCallsign == FALSE) + send(sockptr->socket, Signon, sprintf(Signon, "%s\r\n", TNC->Streams[Stream].MyCall), 0); + } + } + } + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; +// sockptr->Number = Stream; + sockptr->RelayMode = FALSE; + sockptr->ConnectTime = time(NULL); + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = TRUE; + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + strcpy(sockptr->ADIF->Call, TNC->Streams[Stream].MyCall); + + ShowConnections(TNC); + + if (sockptr->FromHostBuffer == 0) + { + sockptr->FromHostBuffer = malloc(10000); + sockptr->FromHostBufferSize = 10000; + } + + sockptr->FromHostBuffPutptr = sockptr->FromHostBuffGetptr = 0; + + TNC->Streams[Stream].BytesRXed = TNC->Streams[Stream].BytesTXed = 0; + + return 0; +} + +VOID ReportError(struct STREAMINFO * STREAM, char * Msg) +{ + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "Error - %s\r", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +VOID Report(struct STREAMINFO * STREAM, char * Msg) +{ + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "%s\r", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +void CheckCMSThread(void * TNC); + +BOOL CheckCMS(struct TNCINFO * TNC) +{ + if (TNC->TCPInfo->CMS) + { + TNC->TCPInfo->CheckCMSTimer = 0; + _beginthread(CheckCMSThread, 0, (void *)TNC); + } + return 0; +} + +void CheckCMSThread(void * TNCPtr) +{ + // Resolve Name and check connectivity to each address + + struct TNCINFO * TNC = (struct TNCINFO *)TNCPtr; + struct TCPINFO * TCP = TNC->TCPInfo; +// struct hostent * HostEnt; + struct in_addr addr; + struct hostent *remoteHost; + char **pAlias; int i = 0; + BOOL INETOK = FALSE; + struct addrinfo hints, *res = 0, *saveres; + int n; + unsigned long cms; + + TCP->UseCachedCMSAddrs = FALSE; + + // if TCP->CMSServer is an ip address use it + + cms = inet_addr(TCP->CMSServer); + + if (cms != INADDR_NONE) + { + Debugprintf("Using %s for CMS Server", TCP->CMSServer); + TCP->CMSAddr[0].s_addr = cms; + TCP->CMSFailed[0] = FALSE; + TCP->CMSName[0] = _strdup(TCP->CMSServer); // Save Host Name + TCP->NumberofCMSAddrs = 1; + goto CheckServers; + } + + // First make sure we have a functioning DNS + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET6; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_DGRAM; + + n = getaddrinfo("a.root-servers.net", NULL, &hints, &res); + + if (n == 0) + goto rootok; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_DGRAM; + n = getaddrinfo("b.root-servers.net", NULL, &hints, &res); + + if (n == 0) + goto rootok; + + Debugprintf("Resolve root nameserver failed"); + + // Most likely is a local Internet Outage, but we could have Internet, but no name servers + // Either way, switch to using cached CMS addresses. CMS Validation will check connectivity + + TCP->UseCachedCMSAddrs = TRUE; + goto CheckServers; + +rootok: + + freeaddrinfo(res); + + INETOK = TRUE; // We have connectivity + + res = 0; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_DGRAM; + n = getaddrinfo(TCP->CMSServer, NULL, &hints, &res); + + if (n || !res || res->ai_next == 0) // Resolve Failed, or Returned only one Host + { + // Switch to Cached Servers + + if (res) + { + // If Host is amazonaws, allow it + + remoteHost = gethostbyaddr((char *) &res->ai_addr->sa_data[2], 4, AF_INET); + + if (remoteHost && strstr(_strlwr(remoteHost->h_name), "amazonaws")) + goto resok; + + Debugprintf("Resolve CMS returned only one host"); + freeaddrinfo(res); + } + else + Debugprintf("Resolve CMS Failed"); + + TCP->UseCachedCMSAddrs = TRUE; + + goto CheckServers; + } + +resok: + + saveres = res; + + while (res) + { + memcpy(&addr.s_addr, &res->ai_addr->sa_data[2], 4); + TCP->CMSAddr[i] = addr; + TCP->CMSFailed[i] = FALSE; + i++; + res = res->ai_next; + } + + freeaddrinfo(saveres); + + TCP->NumberofCMSAddrs = i; + + i = 0; + + while (i < TCP->NumberofCMSAddrs) + { + if (TCP->CMSName[i]) + free(TCP->CMSName[i]); + + remoteHost = gethostbyaddr((char *) &TCP->CMSAddr[i], 4, AF_INET); + + if (remoteHost == NULL) + { + int dwError = WSAGetLastError(); + + TCP->CMSName[i] = NULL; + + if (dwError != 0) + { + if (dwError == HOST_NOT_FOUND) + Debugprintf("CMS - Host not found"); + else if (dwError == NO_DATA) + Debugprintf("CMS No data record found"); + else + Debugprintf("CMS Gethost failed %d", dwError); + } + } + else + { + Debugprintf("CMS #%d %s Official name : %s",i, inet_ntoa(TCP->CMSAddr[i]), remoteHost->h_name); + + TCP->CMSName[i] = _strdup(remoteHost->h_name); // Save Host Name + + for (pAlias = remoteHost->h_aliases; *pAlias != 0; pAlias++) + { + Debugprintf("\tAlternate name #%d: %s\n", i, *pAlias); + } + } + i++; + } + + TCP->NumberofCMSAddrs = i; + +CheckServers: +#ifndef LINBPQ + CheckMenuItem(TNC->TCPInfo->hActionMenu, 4, MF_BYPOSITION | TCP->UseCachedCMSAddrs<<3); +#endif + if (TCP->UseCachedCMSAddrs) + { + // Get Cached Servers from CMSInfo.txt + + GetCMSCachedInfo(TNC); + } + + if (TCP->NumberofCMSAddrs == 0) + { + TCP->CMSOK = FALSE; +#ifndef LINBPQ + SetWindowText(TCP->hCMSWnd, "NO CMS"); +#endif + return; + } + + // if we don't know we have Internet connectivity, make sure we can connect to at least one of them + + TCP->CMSOK = INETOK | CMSCheck(TNC, TCP); // If we know we have Inet, dont check connectivity + +#ifndef LINBPQ + if (TCP->CMSOK) + MySetWindowText(TCP->hCMSWnd, "CMS OK"); + else + MySetWindowText(TCP->hCMSWnd, "NO CMS"); +#endif + return; +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 255 +#define MAX_VALUE_DATA 255 + + + +VOID GetCMSCachedInfo(struct TNCINFO * TNC) +{ + struct TCPINFO * TCP = TNC->TCPInfo; + ULONG IPAD; + char inname[256]; + + FILE *in; + char Buffer[2048]; + char *buf = Buffer; + char *ptr1, *ptr2, *context; + int i = 0; + + if (LogDirectory[0] == 0) + { + strcpy(inname, "logs/CMSInfo.txt"); + } + else + { + strcpy(inname, LogDirectory); + strcat(inname, "/"); + strcat(inname, "logs/CMSInfo.txt"); + } + + TCP->NumberofCMSAddrs = 0; + + in = fopen(inname, "r"); + + if (!(in)) return; + + while(fgets(buf, 128, in)) + { + ptr1 = strtok_s(buf, ", ", &context); + ptr2 = strtok_s(NULL, ", ", &context); // Skip Time + ptr2 = strtok_s(NULL, ", ", &context); + + if (ptr1[0] < 32 || ptr1[0] > 127 || ptr2 == NULL) + continue; + + IPAD = inet_addr(ptr2); + + memcpy(&TCP->CMSAddr[i], &IPAD, 4); + + TCP->CMSFailed[i] = FALSE; + + if (TCP->CMSName[i]) + free(TCP->CMSName[i]); + + TCP->CMSName[i] = _strdup(ptr1); // Save Host Name + i++; + + if (i >= MaxCMS) + break; + } + + fclose(in); + + TCP->NumberofCMSAddrs = i; + + return; +} + +BOOL CMSCheck(struct TNCINFO * TNC, struct TCPINFO * TCP) +{ + // Make sure at least one CMS can be connected to + + u_long param=1; + BOOL bcopt=TRUE; + SOCKET sock; + struct sockaddr_in sinx; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + int n = 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(8772); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + for (n = 0; n < TCP->NumberofCMSAddrs; n++) + { + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + return FALSE; + + memcpy(&destaddr.sin_addr.s_addr, &TCP->CMSAddr[n], 4); + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) == 0) + { + closesocket(sock); + return TRUE; + } + + // Failed - try next + + if (TCP->CMSName[n]) + Debugprintf("Check CMS Failed for %s", TCP->CMSName[n]); + closesocket(sock); + } + return FALSE; +} + + + +int CMSConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, int Stream) +{ + int err; + u_long param=1; + BOOL bcopt=TRUE; + struct ConnectionInfo * sockptr; + SOCKET sock; + struct sockaddr_in sinx; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + int n; + char Msg[80]; + + sockptr = STREAM->ConnectionInfo; + + sock = sockptr->socket = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + ReportError(STREAM, "Create Socket Failed"); + return FALSE; + } + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; + sockptr->LoginState = 2; + sockptr->UserPointer = 0; + sockptr->DoEcho = FALSE; + sockptr->BPQTermMode = FALSE; + + sockptr->FBBMode = TRUE; // Raw Data + sockptr->NeedLF = FALSE; + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(8772); + + // See if current CMS is down + + n = 0; + + while (TCP->CMSFailed[TCP->NextCMSAddr]) + { + TCP->NextCMSAddr++; + if (TCP->NextCMSAddr >= TCP->NumberofCMSAddrs) TCP->NextCMSAddr = 0; + n++; + + if (n == TCP->NumberofCMSAddrs) + { + TCP->CMSOK = FALSE; +#ifndef LINBPQ + DrawMenuBar(TNC->hDlg); +#endif + ReportError(STREAM, "All CMS Servers are inaccessible"); + closesocket(sock); + + if (TCP->RELAYHOST[0] && TCP->FallbacktoRelay && STREAM->NoCMSFallback == 0) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = TRUE; + return TCPConnect(TNC, TCP, STREAM, TCP->RELAYHOST, 8772, TRUE); + } + + STREAM->NeedDisc = 10; + TNC->Streams[Stream].Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + return FALSE; + } + } + + sockptr->CMSIndex = TCP->NextCMSAddr; + + sprintf(Msg, "Trying %s", TCP->CMSName[TCP->NextCMSAddr]); + + memcpy(&destaddr.sin_addr.s_addr, &TCP->CMSAddr[TCP->NextCMSAddr++], 4); + + if (TCP->NextCMSAddr >= TCP->NumberofCMSAddrs) + TCP->NextCMSAddr = 0; + + ioctl(sockptr->socket, FIONBIO, ¶m); + + setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sockptr->socket, (struct sockaddr *) &sinx, addrlen) != 0 ) + { + ReportError(STREAM, "Bind Failed"); + return FALSE; + } + +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, Stream - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + Stream, "CMS"); +#endif + + Report(STREAM, Msg); + + if (connect(sockptr->socket,(struct sockaddr *) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ReportError(STREAM, "*** Connected"); + return TRUE; + } + else + { + err=WSAGetLastError(); + + if (err == 10035 || err == 115 || err == 36 || err == 150) //EWOULDBLOCK + { + // Connect in Progress + + sockptr->UserPointer = &CMSUser; + return TRUE; + } + else + { + // Connect failed + + closesocket(sockptr->socket); + + if (sockptr->CMSSession && sockptr->RelaySession == 0) + { + // Try Next + + TCP->CMSFailed[sockptr->CMSIndex] = TRUE; + Debugprintf("Connect Failed %d, trying next", err); + CMSConnect(TNC, TNC->TCPInfo, &TNC->Streams[Stream], Stream); + return 0; + } + + ReportError(STREAM, "Connect Failed"); + CheckCMS(TNC); + + STREAM->Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + STREAM->NeedDisc = 10; + + return FALSE; + } + } + return FALSE; + +} + +VOID SaveCMSHostInfo(int port, struct TCPINFO * TCP, int CMSNo) +{ + char Info[256]; + char inname[256]; + char outname[256]; + + unsigned char work[4]; + FILE *in, *out; + char Buffer[2048]; + char *buf = Buffer; + + if (TCP->CMS == 0) + return; + + if (CMSNo > 9 || CMSNo < 0 || TCP->CMSName[CMSNo] == 0) + { + Debugprintf("SaveCMSHostInfo invalid CMS Number %d", CMSNo); + return; + } + + if (LogDirectory[0] == 0) + { + strcpy(inname, "logs/CMSInfo.txt"); + } + else + { + strcpy(inname, LogDirectory); + strcat(inname, "/"); + strcat(inname, "logs/CMSInfo.txt"); + } + + if (LogDirectory[0] == 0) + { + strcpy(outname, "logs/CMSInfo.tmp"); + } + else + { + strcpy(outname, LogDirectory); + strcat(outname, "/"); + strcat(outname, "logs/CMSInfo.tmp"); + } + + memcpy(work, &TCP->CMSAddr[CMSNo].s_addr, 4); + + sprintf(Info,"%s %d %d.%d.%d.%d\n", TCP->CMSName[CMSNo], (int)time(NULL), + work[0], work[1], work[2], work[3]); + + + in = fopen(inname, "r"); + + if (!(in)) + { + in = fopen(inname, "w"); + + if (!(in)) + { + perror("Failed to create CMSInfo.txt"); + Debugprintf("Failed to create CMSInfo.txt"); + return; + } + fclose(in); + in = fopen(inname, "r"); + } + + if (!(in)) return; + + out = fopen(outname, "w"); + + if (!(out)) return; + + while(fgets(buf, 128, in)) + { + char addr[256]; + time_t t; + char ip[256]; + int n; + + if (sizeof(time_t) == 4) + n = sscanf(buf,"%s %d %s", addr, (int *)&t, ip); + else + n = sscanf(buf, "%s %lld %s", addr, &t, ip); + + if (n == 3) + { + time_t age = time(NULL) - t; + + // if not current server and not too old, copy across + + if (addr[0] > 31 && addr[0] < 127) + if ((age < 86400 * 30) && strcmp(addr, TCP->CMSName[CMSNo]) != 0) + fputs(buf, out); + } + } + + fputs(Info, out); + + fclose(in); + fclose(out); + + remove(inname); + rename(outname, inname); + + return; +} + +int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, char * Host, int Port, BOOL FBB) +{ + int err; + u_long param=1; + BOOL bcopt=TRUE; + struct ConnectionInfo * sockptr; + SOCKET sock; + struct sockaddr_in sinx; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + int i; + + sockptr = STREAM->ConnectionInfo; + + sock = sockptr->socket = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + ReportError(STREAM, "Create Socket Failed"); + return FALSE; + } + + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; + sockptr->LoginState = 2; + sockptr->UserPointer = 0; + sockptr->DoEcho = FALSE; + + sockptr->FBBMode = FBB; // Raw Data + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + + // Resolve Name if needed + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(Port); + + destaddr.sin_addr.s_addr = inet_addr(Host); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + struct hostent * HostEnt; + + // Resolve name to address + + HostEnt = gethostbyname(Host); + + if (!HostEnt) + { + ReportError(STREAM, "Resolve HostName Failed"); + return FALSE; // Resolve failed + } + i = 0; + while (HostEnt->h_addr_list[i] != 0) + { + struct in_addr addr; + addr.s_addr = *(u_long *) HostEnt->h_addr_list[i++]; + } + memcpy(&destaddr.sin_addr.s_addr, HostEnt->h_addr, 4); + } + + ioctl (sockptr->socket, FIONBIO, ¶m); + + setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sockptr->socket, (struct sockaddr *) &sinx, addrlen) != 0 ) + { + ReportError(STREAM, "Bind Failed"); + return FALSE; + } + + if (LogEnabled) + { + char logmsg[512]; + + sprintf(logmsg,"%d Outward Connect to %s Port %d\n", sockptr->Number, Host, Port); + WriteLog (logmsg); + } + + + if (connect(sockptr->socket,(struct sockaddr *) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ReportError(STREAM, "*** Connected"); + + // Get Send Buffer Size + + return TRUE; + } + else + { + err=WSAGetLastError(); + + if (err == 10035 || err == 115 || err == 36) //EWOULDBLOCK + { + // Connect in Progress + + sockptr->UserPointer = &HostUser; + return TRUE; + } + else + { + // Connect failed + + closesocket(sockptr->socket); + ReportError(STREAM, "Connect Failed"); + STREAM->Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + STREAM->NeedDisc = 10; + + return FALSE; + } + } + + return FALSE; + +} + + +VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst) +{ + unsigned char * src; + char zeros[12] = ""; + char * ptr; + struct + { + int base, len; + } best, cur; + unsigned int words[8]; + int i; + + if (sockptr->sin.sin_family != AF_INET6) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(dst,"%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + return; + } + + src = (unsigned char *)&sockptr->sin6.sin6_addr; + + // See if Encapsulated IPV4 addr + + if (src[12] != 0) + { + if (memcmp(src, zeros, 12) == 0) // 12 zeros, followed by non-zero + { + sprintf(dst,"::%d.%d.%d.%d", src[12], src[13], src[14], src[15]); + return; + } + } + + // Convert 16 bytes to 8 words + + for (i = 0; i < 16; i += 2) + words[i / 2] = (src[i] << 8) | src[i + 1]; + + // Look for longest run of zeros + + best.base = -1; + cur.base = -1; + + for (i = 0; i < 8; i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; // New run, save start + else + cur.len++; // Continuation - increment length + } + else + { + // End of a run of zeros + + if (cur.base != -1) + { + // See if this run is longer + + if (best.base == -1 || cur.len > best.len) + best = cur; + + cur.base = -1; // Start again + } + } + } + + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + + if (best.base != -1 && best.len < 2) + best.base = -1; + + ptr = dst; + + for (i = 0; i < 8; i++) + { + /* Are we inside the best run of 0x00's? */ + + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) + { + // Just output one : for whole string of zeros + + *ptr++ = ':'; + i = best.base + best.len - 1; // skip rest of zeros + continue; + } + + /* Are we following an initial run of 0x00s or any real hex? */ + + if (i != 0) + *ptr++ = ':'; + + ptr += sprintf (ptr, "%x", words[i]); + + // Was it a trailing run of 0x00's? + } + + if (best.base != -1 && (best.base + best.len) == 8) + *ptr++ = ':'; + + *ptr++ = '\0'; +} + +BOOL TelSendPacket(int Stream, struct STREAMINFO * STREAM, PMSGWITHLEN buffptr, struct ADIF * ADIF) +{ + int datalen; + UCHAR * MsgPtr; + SOCKET sock; + struct ConnectionInfo * sockptr = STREAM->ConnectionInfo; + + datalen = (int)buffptr->Len; + MsgPtr = &buffptr->Data[0]; + + STREAM->BytesTXed += datalen; + + sock = sockptr->socket; + + if (sockptr->UserPointer == &CMSUser) + { + WritetoTrace(Stream, MsgPtr, datalen, ADIF, 'S'); + } + + if (sockptr->UTF8) + { + // Convert any non-utf8 chars + + if (IsUTF8(MsgPtr, datalen) == FALSE) + { + unsigned char UTF[1024]; + int u, code; + + // Try to guess encoding + + code = TrytoGuessCode(MsgPtr, datalen); + + if (code == 437) + u = Convert437toUTF8(MsgPtr, datalen, UTF); + else if (code == 1251) + u = Convert1251toUTF8(MsgPtr, datalen, UTF); + else + u = Convert1252toUTF8(MsgPtr, datalen, UTF); + + SendAndCheck(sockptr, UTF, u, 0); + ReleaseBuffer(buffptr); + return TRUE; + } + } + + if (sockptr->FBBMode && sockptr->NeedLF == FALSE) + { +/* + // if Outward Connect to FBB, Replace ff with ffff + + if (0) // if we use this need to fix retry + { + char * ptr2, * ptr = &MsgPtr[0]; + int i; + do + { + ptr2 = memchr(ptr, 255, datalen); + + if (ptr2 == 0) + { + // no ff, so just send as is + + xxxsend(sock, ptr, datalen, 0); + i=0; + break; + } + + i=ptr2+1-ptr; + + xxsend(sock,ptr,i,0); + xxsend(sock,"\xff",1,0); + + datalen-=i; + ptr=ptr2+1; + } + while (datalen>0); + } +*/ + // Normal FBB Mode path + + BOOL ret = SendAndCheck(sockptr, MsgPtr, datalen, 0); + ReleaseBuffer(buffptr); + return ret; + } + + // Not FBB mode, or FBB and NEEDLF Replace cr with crlf + + { + unsigned char Out[1024]; + unsigned char c; + unsigned char * ptr2 = Out; + unsigned char * ptr = &MsgPtr[0]; + + while (datalen--) + { + c = (*ptr++); + + if (c == 13) + { + *(ptr2++) = 13; + *(ptr2++) = 10; + } + else + *(ptr2++) = c; + } + + ReleaseBuffer(buffptr); + return SendAndCheck(sockptr, Out, (int)(ptr2 - Out), 0); + } +} + +VOID ProcessTrimodeCommand(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, char * MsgPtr) +{ + struct STREAMINFO * STREAM = &TNC->Streams[sockptr->Number]; + int Port = 4; + + Debugprintf(MsgPtr); + + if (strcmp(MsgPtr, "CLOSE") == 0) + { + if (STREAM->Connected) + { + STREAM->ReportDISC = TRUE; + } + } + +// MYCALLSIGN XE2BNC + else + if (memcmp(MsgPtr, "MYCALLSIGN", 10) == 0) + { + char * call = &MsgPtr[11]; + + if (strlen(call) > 9) + call[9] = 0; + + memcpy(STREAM->MyCall, call, 10); + + ConvToAX25(call, &TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->L4USER[0]); + + strcpy(&TNCInfo[Port]->Streams[0].MyCall[0], call); + } + + +// TARGETCALLSIGN KE7XO + else + if (memcmp(MsgPtr, "TARGETCALLSIGN", 14) == 0) + { + char * call = &MsgPtr[15]; + + if (strlen(call) > 9) + call[9] = 0; + + memcpy(STREAM->RemoteCall, call, 10); + } +// INITIATECALL 50 + else + if (memcmp(MsgPtr, "INITIATECALL", 12) == 0) + { + char Cmd[80]; + int n; + + n = sprintf(Cmd,"C %s\r", STREAM->RemoteCall); + + SendtoNode(TNC, sockptr->Number, Cmd, n); + } + + +// CHANNEL 3586500,None,None + else + if (memcmp(MsgPtr, "CHANNEL", 7) == 0) + { + double Freq = atof(&MsgPtr[8]); + char Radiocmd[80]; + int n; + + strcpy(sockptr->Callsign, "G8BPQ"); + + n = sprintf(Radiocmd,"RADIO %f %s\r", Freq/1000000, "USB"); + + SendtoNode(TNC, sockptr->Number, Radiocmd, n); + } + + else + if (memcmp(MsgPtr, "PROTOCOL", 8) == 0) + { + // Attach the relevant port + + SendtoNode(TNC, sockptr->Number, "ATTACH 4\r", 9); + } + + else + if (strcmp(MsgPtr, "BUSY") == 0) + send(sockptr->socket, "BUSY False\r\n", 12,0); + + + send(sockptr->socket, "CMD\r\n", 5,0); + +// SendtoNode(TNC, sockptr->Number, NodeLine, len); +} + + +VOID ProcessTrimodeResponse(struct TNCINFO * TNC, struct STREAMINFO * STREAM, unsigned char * MsgPtr, int Msglen) +{ + MsgPtr[Msglen] = 0; + + if (STREAM->ConnectionInfo->TriModeConnected) + { + // Send over the Data Socket + + send(STREAM->ConnectionInfo->TriModeDataSock, MsgPtr, Msglen, 0); + + return; + } + + strlop(MsgPtr, 13); + Debugprintf(MsgPtr); + + if (memcmp(MsgPtr, "*** Connected to ", 17) == 0) + { + char Cmd[80]; + int n; + + n = sprintf(Cmd,"CONNECTED %s\r", &MsgPtr[17]); + + STREAM->ConnectionInfo->TriModeConnected = TRUE; + + send(STREAM->ConnectionInfo->socket, Cmd, n, 0); + } +} + +VOID ProcessTriModeDataMessage(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM) +{ + int len=0; + char NLMsg[3]={13,10,0}; + char RelayMsg[] = "No CMS connection available - using local BPQMail\r"; + struct TCPINFO * TCP = TNC->TCPInfo; + unsigned char Buffer[256]; + + ioctl(sock,FIONREAD,&len); + + if (len > 256) len = 256; + + len = recv(sock, Buffer, len, 0); + + if (len == SOCKET_ERROR || len ==0) + { + // Failed or closed - clear connection + + closesocket(sock); + return; + } + + SendtoNode(TNC, sockptr->Number, Buffer, len); +} + +extern struct DATAMESSAGE * REPLYBUFFER; +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); + + +VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + struct TNCINFO * TNC; + char * ptr1, * ptr2; + char buf[256],errbuf[256]; + char * Config; + struct TCPINFO * TCP; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TNC = TNCInfo[Port]; + + if (TNC == NULL || TNC->Hardware != H_TELNET) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TCP = TNC->TCPInfo; + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && _stricmp(ptr, "ALL") == 0) + { + // Use EXTRESTART Code + + PEXTPORTDATA PORTVEC = (PEXTPORTDATA) PORT; + PORTVEC->EXTRESTART = 1; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfig Telnet Ok\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (ptr && _stricmp(ptr, "USERS") == 0) + { + // Reconfig Users + + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to reread config file - leaving config unchanged\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Config = PortConfig[Port]; + + if (Config == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "No Config Entries found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Don't free old user records - sessions may have pointers to them + + // Free the header + + if (TCP->UserRecPtr) + { + free(TCP->UserRecPtr); + TCP->UserRecPtr = NULL; + } + + TCP->NumberofUsers = 0; + + // Look for USER lines + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1 + 1); + buf[ptr2 - ptr1 + 1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + strcpy(errbuf,buf); // save in case of erro + + if (_memicmp(buf, "USER=", 5) == 0 || _memicmp(buf, "USER ", 5) == 0) + { + char *User, *Pwd, *UserCall, *Secure, * Appl; + int End = (int)strlen(buf) -1; + struct UserRec * USER; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + char * value = &buf[5]; + + // USER=user,password,call,appl,SYSOP + + memset(Param, 0, 2048); + strlop(value, 13); + strlop(value, ';'); + + ptr1 = value; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + } + + + User = &Param[0][0]; + + if (_stricmp(User, "ANON") == 0) + { + strcpy(&Param[2][0], "ANON"); + strcpy(&Param[4][0], ""); // Dont allow SYSOP if ANON + } + + Pwd = &Param[1][0]; + UserCall = &Param[2][0]; + Appl = &Param[3][0]; + Secure = &Param[4][0]; + + if (User[0] == 0 || Pwd[0] == 0 || UserCall[0] == 0) // invalid record + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Bad USER Record %s\r", errbuf); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + _strupr(UserCall); + + if (TCP->NumberofUsers == 0) + TCP->UserRecPtr = malloc(sizeof(void *)); + else + TCP->UserRecPtr = realloc(TCP->UserRecPtr, (TCP->NumberofUsers+1) * sizeof(void *)); + + USER = zalloc(sizeof(struct UserRec)); + + TCP->UserRecPtr[TCP->NumberofUsers] = USER; + + USER->Callsign = _strdup(UserCall); + USER->Password = _strdup(Pwd); + USER->UserName = _strdup(User); + USER->Appl = zalloc(32); + USER->Secure = FALSE; + + if (_stricmp(Secure, "SYSOP") == 0) + USER->Secure = TRUE; + + if (Appl[0] && strcmp(Appl, "\"\"") != 0) + { + strcpy(USER->Appl, _strupr(Appl)); + strcat(USER->Appl, "\r\n"); + } + TCP->NumberofUsers++; + } + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Reread Telnet Users Ok - %d USER Records\r", TCP->NumberofUsers); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid parameter - use either USERS or ALL \r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) +{ + // DISPLAY Telnet Server Status Mheard + + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + int txlen = 0, n; + struct TNCINFO * TNC; + char msg[80]; + struct ConnectionInfo * sockptr; + int i; + char CMS[] = "CMS Disabled"; + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + Port = atoi(ptr); + + if (Port) + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TNC = TNCInfo[Port]; + + if (TNC == NULL || TNC->Hardware != H_TELNET) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (TNC->TCPInfo->CMS) + if (TNC->TCPInfo->CMSOK) + strcpy(CMS, "CMS Ok"); + else + strcpy(CMS, "No CMS"); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Telnet Status for Port %d %s\r", Port, CMS); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr=TNC->Streams[n].ConnectionInfo; + + if (!sockptr->SocketActive) + { + strcpy(msg,"Idle"); + } + else + { + if (sockptr->UserPointer == 0) + { + if (sockptr->HTTPMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg, "HTTP From %s", Addr); + } + else if (sockptr->DRATSMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg, "DRATS From %s", Addr); + } + else + strcpy(msg,"Logging in"); + } + else + { + i=sprintf(msg,"%-10s %-10s %2d", + sockptr->UserPointer->UserName,sockptr->Callsign,sockptr->BPQStream); + } + } + Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", msg); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} diff --git a/TermTCP.c b/TermTCP.c new file mode 100644 index 0000000..05ff82e --- /dev/null +++ b/TermTCP.c @@ -0,0 +1,1604 @@ +#define GTK_ENABLE_BROKEN +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +GdkPixbuf *create_pixbuf(const gchar * filename) +{ + GdkPixbuf *pixbuf; + GError *error = NULL; + pixbuf = gdk_pixbuf_new_from_file(filename, &error); + if(!pixbuf) { + fprintf(stderr, "%s\n", error->message); + g_error_free(error); + } + + return pixbuf; +} + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET int +#define closesocket close +typedef gint32 COLORREF; +#define RGB(r,g,b) ((COLORREF)(((guint8)(r)|((guint16)((guint8)(g))<<8))|(((guint32)(guint8)(b))<<16))) + +gchar *fontname; +gint vhandle; +char Host[5][100]; +char Port[5][10]; +char UserName[5][80]; +char Password[5][80]; +char Path[5][100]; +char Test1[5][100]; +char Test2[5][100]; +char Pass[5][100]; +char path[5][100]; + +char HN[9][10] = {"Host1", "Host2", "Host3", "Host4", "Host5","Host6", "Host7", "Host8"}; +char PN[9][10] = {"Port1", "Port2", "Port3", "Port4", "Port5", "Port6", "Port7", "Port8"}; +char PASSN[9][10] = {"Pass1", "Pass2", "Pass3", "Pass4", "Pass5", "Pass6", "Pass7", "Pass8"}; +char UN[9][10] = {"User1", "User2", "User3", "User4", "User5", "User6", "User7", "User8"}; +char pn[5][6] = {"Path"}; +int CurrentHost = 0; +char VersionString[80] = "1.0.0.47a BPQ"; + +char DisMsg[] = "*** Disconnected\r"; + +int PortMask=65535; +int mtxparam=1; +int MCOM=1; +int Split; +int x, y; +int vhandle; +int Bells = FALSE; +int StripLF = FALSE; +int LogMonitor = FALSE; +int LogOutput = FALSE; +int SendDisconnected = TRUE; +int MonNODES = FALSE; +int MONColour = TRUE; +int ChatMode = FALSE; +int MonPorts = 1; +int muionly = 1; +gchar * font; +char Font[50]; +int position; +int left = 100, top = 100, right = 500, bottom = 500; +int height; +int width; +char Position[50]; +gint gx, gy; +gint xx, yy; +gint xxx, yyy; +char outputmon[50]; +char monitormon [50]; +int Connecting = FALSE; +int Disconnecting = FALSE; +int Connected = FALSE; +int SocketActive = FALSE; +int monitormon1; + +char Title[80]; + +int SlowTimer; + +void ReadConfig(); +void SendTraceOptions(); + +int TCPConnect(char * Host, char * Port); +void WritetoOutputWindow(const char * Text, int Len); +void WritetoMonWindow(char * Text, int Len); +int Telnet_Connected(SOCKET sock, int Error); +int SendMsg(const char * msg, int len); + + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(64,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,2552,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,2552,255) +}; + + +SOCKET RecvSocket; +SOCKET sock; + +GtkWidget *dialog; +GtkWidget *window; +GtkWidget *box1; +GtkWidget *box2; +GtkWidget *box3; +GtkWidget *box10; +GtkWidget *hbox; +GtkWidget *button; +GtkWidget *check; +GtkWidget *separator; +GtkWidget *table; +GtkWidget *vscrollbar; +GtkWidget *vscrollbar2; +GtkTextBuffer *text; +GtkTextBuffer *text2; +GtkTextBuffer *text3; +GtkWidget *entry; +GtkWidget *vpaned; +GtkWidget *frame1; +GtkWidget *frame2; +GIOChannel *RecvChannel; +GtkWidget *menubar; +GtkWidget *view; +GtkWidget *scrolledwin; +GtkWidget *view2; +GtkWidget *scrolledwin2; +GtkWidget *scrolledwin3; +GtkWidget *montx; +GtkWidget *monsup; +GtkWidget *monnode; +GtkWidget *encol; +GtkWidget *mui; +GtkWidget *addpor; +GtkWidget *menubar; +GtkWidget *conmenu, *conn_item, *Conn[8], *Conn2, *Conn3, *Conn4, *Conn5, *Conn6, *Conn7, *Conn8; +GtkWidget *discmenu, *dis_item, sid_item; +GtkWidget *cfgmenu, *tcp_item, *font_item, *strip_item, *logmon_item, *logout_item, *cfg_item, *chat_term, *Cfg[8], *Cfg2, *Cfg3, *Cfg4, *Cfg5, *Cfg6, *Cfg7, *Cfg8; +GtkWidget *monmenu, *mon_item, *mon[32]; +GtkWidget *tcpmenu; +GtkWidget *enbel, *enbel_item; +GtkWidget *propmenu,*propitem,*Prop; +GtkWidget *text_view, *font_select_dlg; +GtkWidget *font_select_dlg_ok, *font_select_dlg_apply, *font_select_dlg_cancel; +gchar *fontname; +GtkTextTag *rtag, *btag, *tag[256], *tagm[256]; + +void EnableDisconnectMenu(); +void DisableConnectMenu(); +void EnableConnectMenu(); +void DisableDisconnectMenu(); +void get_fontname(GtkWidget *, gpointer); +void apply_fontname(GtkWidget *, gpointer); +void open_font_select_dlg(GtkWidget *, gpointer); +void font_select_dlg_exit(GtkWidget *, gpointer); +void close_application(GtkWidget *widget, gpointer data) +{ + gtk_main_quit (); + return; +} + +void enter_callback( GtkWidget *widget, + GtkWidget *entry ) +{ + const gchar *entry_text; + entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); + + if (!Connected && ! Connecting) + { + TCPConnect(Host[CurrentHost], Port[CurrentHost]); + gtk_entry_set_text (GTK_ENTRY (entry), ""); + return ; + } + + SendMsg(entry_text, strlen(entry_text)); + SendMsg("\r", 1); + WritetoOutputWindow(entry_text, strlen(entry_text)); + WritetoOutputWindow("\r ", 1); + gtk_entry_set_text (GTK_ENTRY (entry), ""); +} + +static void Disconnect(GtkWidget *w, gpointer data); +static void Toggled(GtkWidget *w, int * data ) +{ + int NewVal = gtk_check_menu_item_get_active((GtkCheckMenuItem *)w); + *(data) = NewVal; + + SendTraceOptions(); + + return; +} + +static void Configure(GtkWidget *w, gpointer data) +{ + GtkWidget * dialog = gtk_dialog_new_with_buttons( "Configuration", + GTK_WINDOW(window), + GTK_DIALOG_MODAL, + GTK_STOCK_OK, 1, + GTK_STOCK_CANCEL, 2, + NULL ); + + GtkWidget *entry1; + GtkWidget *entry2; + GtkWidget *entry3; + GtkWidget *entry4; + + GtkWidget *label, *content_area; + GtkWidget *label2, *label3, *label4; + + int HostNum = (int)data; + const gchar *entry_text; + gint result; + char Key[10]; + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + label = gtk_label_new ("Host"); + label2 = gtk_label_new ("Port"); + label3 = gtk_label_new ("Username"); + label4 = gtk_label_new ("Password"); + + /* Add the label, and show everything we've added to the dialog. */ + + gtk_container_add (GTK_CONTAINER (content_area), label); + + entry1 = gtk_entry_new(); + gtk_entry_set_max_length (GTK_ENTRY (entry1), 100); + gtk_entry_set_text (GTK_ENTRY (entry1), &Host[HostNum][0]); + + gtk_container_add (GTK_CONTAINER (content_area), entry1); + + gtk_container_add (GTK_CONTAINER (content_area), label2); + entry2 = gtk_entry_new(); + gtk_entry_set_max_length (GTK_ENTRY (entry2), 10); + gtk_entry_set_text (GTK_ENTRY (entry2), &Port[HostNum][0]); + + gtk_container_add (GTK_CONTAINER (content_area), entry2); + + gtk_container_add (GTK_CONTAINER (content_area), label3); + + entry3 = gtk_entry_new(); + gtk_entry_set_max_length (GTK_ENTRY (entry3), 100); + gtk_entry_set_text (GTK_ENTRY (entry3), &UserName[HostNum][0]); + gtk_container_add (GTK_CONTAINER (content_area), entry3); + + gtk_container_add (GTK_CONTAINER (content_area), label4); + + entry4 = gtk_entry_new(); + gtk_entry_set_max_length (GTK_ENTRY (entry4), 100); + gtk_entry_set_text (GTK_ENTRY (entry4), &Password[HostNum][0]); + gtk_container_add (GTK_CONTAINER (content_area), entry4); + + gtk_widget_show_all (dialog); + + result = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (result == 1) + { + GKeyFile * KF; + gchar * Value; + GError *error = NULL; + gsize length; + FILE *outfile; + char path[PATH_MAX]; + char *fname = ".BPQTermTCP.ini"; + snprintf(path, PATH_MAX, "%s/%s", getenv("HOME"), fname); + + entry_text = gtk_entry_get_text (GTK_ENTRY (entry1)); + strcpy(&Host[HostNum][0], entry_text); + + entry_text = gtk_entry_get_text (GTK_ENTRY (entry2)); + strcpy(&Port[HostNum][0], entry_text); + + entry_text = gtk_entry_get_text (GTK_ENTRY (entry3)); + strcpy(&UserName[HostNum][0], entry_text); + + entry_text = gtk_entry_get_text (GTK_ENTRY (entry4)); + strcpy(&Password[HostNum][0], entry_text); + + KF = g_key_file_new(); + g_key_file_load_from_file(KF, path, 0, NULL); + + sprintf(Key, "Host%d", HostNum + 1); + g_key_file_set_string(KF, "Session 1", Key, &Host[HostNum][0]); + + sprintf(Key, "Port%d", HostNum + 1); + g_key_file_set_string(KF, "Session 1", Key, &Port[HostNum][0]); + + sprintf(Key, "User%d", HostNum + 1); + g_key_file_set_string(KF, "Session 1", Key, &UserName[HostNum][0]); + + sprintf(Key, "Pass%d", HostNum + 1); + g_key_file_set_string(KF, "Session 1", Key, &Password[HostNum][0]); + + + Value = g_key_file_to_data(KF, &length, &error); + + outfile = fopen ( path, "w"); + fputs(Value, outfile); + fclose(outfile); + + g_free(Value); + + g_key_file_free(KF); + ReadConfig(); + } + + gtk_widget_destroy (dialog); +} + +static void Connect( GtkWidget *w, gpointer data ) +{ + CurrentHost = (int)data; + TCPConnect(Host[CurrentHost], Port[CurrentHost]); +} + + + // Port Line Callback. data Param is Port Number +static void PToggled( GtkWidget *w, int data ) +{ + // Create Port Mask bit from Port Number + int Mask = 1 << data; + + // Get current state of Item + int NewVal = gtk_check_menu_item_get_active((GtkCheckMenuItem *)w); + PortMask &= ~Mask; // Clear portmask bit for this port + + // Shift the new bit to the right place in the mask + NewVal = NewVal << data; + + // OR into Mask + PortMask |= NewVal; + + SendTraceOptions(); + + return; + +} + +static void AddPortItem( GtkWidget *w, int * data ) +{ + char Port[10]; + sprintf(Port, "Port %d", MonPorts + 1); + mon[MonPorts] = gtk_check_menu_item_new_with_label (Port); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)mon[MonPorts], (PortMask >> MonPorts) & MonPorts); + + // Set Callback to PToggled. Parameter is Port Number + g_signal_connect (mon[MonPorts], "toggled", G_CALLBACK (PToggled), (void *) MonPorts); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, mon[MonPorts]); + + MonPorts++; + + gtk_widget_show_all (menubar); + + return; +} + +GtkWidget *get_menubar_menu(GtkWidget *window) +{ + int i; + + menubar = gtk_menu_bar_new(); + conmenu = gtk_menu_new(); + cfgmenu = gtk_menu_new(); + discmenu = gtk_menu_new(); + monmenu = gtk_menu_new(); + montx = gtk_menu_new(); + monsup = gtk_menu_new(); + monnode = gtk_menu_new(); + encol = gtk_menu_new(); + addpor = gtk_menu_new(); + tcpmenu = gtk_menu_new(); + enbel = gtk_menu_new(); + mui = gtk_menu_new(); + propmenu = gtk_menu_new(); + + /* Create the menu items */ + + for (i = 0; i < 8; i++) + { + Conn[i] = gtk_check_menu_item_new_with_label (Host[i]); + gtk_menu_shell_append (GTK_MENU_SHELL (conmenu), Conn[i]); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) Conn[i], CurrentHost == i); + g_signal_connect (Conn[i], ("toggled"), G_CALLBACK (Toggled), (void *) &CurrentHost); + + Cfg[i] = gtk_menu_item_new_with_label (Host[i]); + gtk_menu_shell_append (GTK_MENU_SHELL (tcpmenu), Cfg[i]); + + + /* Attach the callback functions to the activate signal */ + + g_signal_connect (Conn[i], "activate", G_CALLBACK (Connect), (void *) i); + g_signal_connect (Cfg[i], "activate", G_CALLBACK (Configure), (void *) i); + + } + + conn_item = gtk_menu_item_new_with_label ("Connect"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (conn_item), conmenu); + + dis_item = gtk_menu_item_new_with_label ("Disconnect"); + g_signal_connect (dis_item, "activate", G_CALLBACK (Disconnect), 0); + gtk_widget_set_sensitive(dis_item, TRUE); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (dis_item), discmenu); + + cfg_item = gtk_menu_item_new_with_label ("Setup"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (cfg_item), cfgmenu); + + enbel = gtk_check_menu_item_new_with_label ("Enable Bells"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)enbel, Bells); + g_signal_connect (enbel, "toggled", G_CALLBACK(Toggled), (void *) &Bells); + + logmon_item = gtk_menu_item_new_with_label ("Log Monitor"); + logout_item = gtk_menu_item_new_with_label ("Log Output"); + + chat_term = gtk_check_menu_item_new_with_label ("Chat Terminal Mode"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) chat_term, ChatMode); + g_signal_connect (chat_term, ("toggled"), G_CALLBACK (Toggled), (void *) &ChatMode); + + propitem = gtk_menu_item_new_with_label ("Properties"); + g_signal_connect(propitem, "activate", G_CALLBACK (open_font_select_dlg), (void*) &font); + //gtk_menu_item_set_submenu (GTK_MENU_ITEM (propitem), propmenu); + + + tcp_item = gtk_menu_item_new_with_label ("TCP Hosts"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (tcp_item), tcpmenu); + + mon_item = gtk_menu_item_new_with_label ("Monitor"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (mon_item), monmenu); + + montx = gtk_check_menu_item_new_with_label ("Monitor TX"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)montx, mtxparam); + g_signal_connect (montx, "toggled", G_CALLBACK (Toggled), (void *) &mtxparam); + + monsup = gtk_check_menu_item_new_with_label ("Monitor Supervisor"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)monsup, MCOM); + g_signal_connect (monsup, "toggled", G_CALLBACK (Toggled), (void *) &MCOM); + + monnode = gtk_check_menu_item_new_with_label ("Monitor Nodes"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)monnode, MonNODES); + g_signal_connect (monnode, "toggled", G_CALLBACK (Toggled), (void *) &MonNODES); + + mui = gtk_check_menu_item_new_with_label ("Monitor UI Only"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)mui, muionly); + g_signal_connect (mui, "toggled", G_CALLBACK (Toggled), (void *) &muionly); + + encol = gtk_check_menu_item_new_with_label ("Enable Colour"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *)encol, MONColour); + g_signal_connect (encol, "toggled", G_CALLBACK (Toggled), (void *) &MONColour); + + addpor = gtk_menu_item_new_with_label ("Add Port"); + g_signal_connect (addpor, "activate", G_CALLBACK (AddPortItem), (void *) 0); + + + /* Add them to the menu */ + + gtk_menu_shell_append ((GtkMenuShell *)menubar, conn_item); + gtk_menu_shell_append ((GtkMenuShell *)menubar, dis_item); + gtk_menu_shell_append ((GtkMenuShell *)menubar, cfg_item); + gtk_menu_shell_append ((GtkMenuShell *)menubar, mon_item); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, montx); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, monsup); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, monnode); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, mui); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, encol); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, addpor); + gtk_menu_shell_append ((GtkMenuShell *)cfgmenu, tcp_item); + gtk_menu_shell_append ((GtkMenuShell *)cfgmenu, propitem); + gtk_menu_shell_append ((GtkMenuShell *)cfgmenu, enbel); + gtk_menu_shell_append ((GtkMenuShell *)cfgmenu, logmon_item); + gtk_menu_shell_append ((GtkMenuShell *)cfgmenu, logout_item); + gtk_menu_shell_append ((GtkMenuShell *)cfgmenu, chat_term); + + for (i = 0; i < MonPorts; i++) + + { + + char Port[10]; + sprintf(Port, "Port %d", i + 1); + mon[i] = gtk_check_menu_item_new_with_label (Port); + + // Set the Checked flag from the corresponding bit of PortMask + gtk_check_menu_item_set_active((GtkCheckMenuItem *)mon[i], (PortMask >> i) & 1); + + // Call PToggled() when menu is selected. Data to PToggled is Port Number + g_signal_connect (mon[i], "toggled", G_CALLBACK (PToggled), (void *) i); + gtk_menu_shell_append ((GtkMenuShell *)monmenu, mon[i]); + + } + + gtk_widget_show_all (menubar); + + SendTraceOptions(); + + return menubar; + +} + +gint ScrollTimer(gpointer data) +{ + GtkTextIter iter; + + gtk_text_buffer_get_end_iter(text, &iter); + gtk_text_view_scroll_to_iter ((GtkTextView *)view, &iter, 0.0, FALSE, 0.0, 0.0); + + return FALSE; +} +gint ScrollTimer2(gpointer data) +{ + GtkTextIter iter; + + gtk_text_buffer_get_end_iter(text2, &iter); + gtk_text_view_scroll_to_iter ((GtkTextView *)view2, &iter, 0.0, FALSE, 0.0, 0.0); + + return FALSE; +} + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +int MonData = FALSE; + + +//int Connecting = FALSE; +//int Disconnecting = FALSE; +//int Connected = FALSE; +//int SocketActive = FALSE; + +int ProcessReceivedData(); + +gint PollTimer(gpointer data) +{ + FD_ZERO(&readfs); + + if (Connecting ||Connected) + FD_SET(sock,&errorfs); + else + return TRUE; + + if (Connected) FD_SET(sock,&readfs); + + FD_ZERO(&writefs); + + if (Connecting) FD_SET(sock,&writefs); // Need notification of Connect + + FD_ZERO(&errorfs); + + if (Connecting ||Connected) FD_SET(sock,&errorfs); + + if (select(sock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(sock, &readfs)) + { + // data available + + ProcessReceivedData(); + + } + + if (FD_ISSET(sock, &writefs)) + { + // Connect success + + Connecting = FALSE; + Connected = TRUE; + + } + + if (FD_ISSET(sock, &errorfs)) + { + // if connecting, then failed, if connected then has just disconnected + + if (Connecting) + { + // Falied + + Connecting = FALSE; + Connected = FALSE; + } + else + { + if (SocketActive) + closesocket(sock); + else + return TRUE; + + sprintf(Title,"TermTCP Version %s - Disconnected", VersionString); + gtk_window_set_title (GTK_WINDOW (window), Title); + DisableDisconnectMenu(); + EnableConnectMenu(); + + WritetoOutputWindow(DisMsg, strlen(DisMsg)); + SocketActive = FALSE; + Connected = FALSE; + Disconnecting = FALSE; + MonData = FALSE; + return TRUE; + + } + } + } + + if (!Connected) + return TRUE; + + if (!ChatMode) + return TRUE; + + SlowTimer++; + + if (SlowTimer > 5 * 60 * 9) // About 9 mins + { + SlowTimer = 0; + SendMsg("\0", 1); + } + + return TRUE; +} + +static GtkWidget *create_monitor ( void ) +{ + view = gtk_text_view_new (); + text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); + gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE); + + scrolledwin = gtk_scrolled_window_new(NULL,NULL); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwin), 3); + gtk_widget_set_size_request(scrolledwin, 500, 100); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER (scrolledwin), view); + + gtk_widget_show(scrolledwin); + return scrolledwin; + +} + +static GtkWidget *create_output ( void ) +{ + view2 = gtk_text_view_new (); + text2 = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view2)); + gtk_text_view_set_editable (GTK_TEXT_VIEW(view2), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW(view2), FALSE); + scrolledwin2 = gtk_scrolled_window_new(NULL,NULL); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwin2), 3); + gtk_widget_set_size_request(scrolledwin2, 500, 100); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin2),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin2), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(scrolledwin2), view2); + + gtk_widget_show(scrolledwin2); + return scrolledwin2; +} + +gint delete_event( GtkWidget *widget, + GdkEvent *event, + gpointer data ) +{ + /* If you return FALSE in the "delete_event" signal handler, + * GTK will emit the "destroy" signal. Returning TRUE means + * you don't want the window to be destroyed. + * This is useful for popping up 'are you sure you want to quit?' + * type dialogs. */ + + g_print ("delete event occurred\n"); + gtk_window_get_size(GTK_WINDOW(widget), &width, &height); + gtk_window_get_position(GTK_WINDOW(widget), &x, &y); + vhandle = gtk_paned_get_position((GtkPaned *)vpaned); + printf("%d %d %d %d %d\n", x, y, width, height, vhandle); + //printf("%s\n", Font); + //gtk_widget_get_style(GtkWidget* window); + /* Change TRUE to FALSE and the main window will be destroyed with + * a "delete_event". */ + + return(FALSE); +} + + +int main(int argc, char *argv[]) +{ + + int i; + + PangoFontDescription *font_desc; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_container_set_border_width (GTK_CONTAINER (window), 10); + + ReadConfig(); + + gtk_window_set_default_size(GTK_WINDOW (window), width, height); + gtk_widget_set_uposition(GTK_WIDGET(window),gx, gy); + gtk_window_set_resizable (GTK_WINDOW (window), TRUE); + g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (close_application), NULL); + gtk_window_set_title (GTK_WINDOW (window), "TermTCP"); + gtk_container_set_border_width (GTK_CONTAINER (window), 0); + gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("/usr/share/pixmaps/bpqicon.png")); + //gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + //gtk_window_get_frame_dimensions(GTK_WINDOW(window),&left,&top,&right,&bottom); + + + gtk_signal_connect (GTK_OBJECT (window), "delete_event", + GTK_SIGNAL_FUNC (delete_event), NULL); + + // Create a box for the menu + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + box10 = gtk_vbox_new (FALSE, 0); + + menubar = get_menubar_menu (window); + + gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 1); + gtk_container_add (GTK_CONTAINER (box1), box10); + gtk_widget_show (window); + + vpaned = gtk_vpaned_new (); + gtk_container_add (GTK_CONTAINER (box10), vpaned); + gtk_paned_set_position(GTK_PANED(vpaned), vhandle); + gtk_widget_show (vpaned); + + /* Now create the contents of the two halves of the window */ + + frame1 = create_monitor (); + gtk_paned_add1 (GTK_PANED (vpaned), frame1); + gtk_widget_show (frame1); + + frame2 = create_output (); + gtk_paned_add2 (GTK_PANED (vpaned), frame2); + gtk_widget_show (frame2); + + /* Separator */ +// separator = gtk_hseparator_new (); +// gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0); + + box2 = gtk_vbox_new (FALSE, 10); + gtk_container_set_border_width (GTK_CONTAINER (box2), 1); + gtk_box_pack_start (GTK_BOX (box10), box2, FALSE, FALSE, 0); + + // set up the text entry line + + entry = gtk_entry_new(); + //gtk_entry_new_with_buffer(text); + gtk_entry_set_max_length (GTK_ENTRY (entry), width); + gtk_entry_set_activates_default(GTK_ENTRY (entry), TRUE); + g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (enter_callback), (gpointer) entry); + gtk_box_pack_start (GTK_BOX (box2), entry, FALSE, FALSE, 0); + gtk_widget_grab_focus(entry); + + gtk_widget_show_all (window); + + /* Change default font throughout the widget */ + + font_desc=pango_font_description_from_string(Font); + gtk_widget_modify_font (entry, font_desc); + gtk_widget_modify_font (view, font_desc); + gtk_widget_modify_font (view2, font_desc); +// gtk_entry_new_with_buffer(); + //gtk_signal_connect(G_OBJECT(window), "configure-event", + // G_CALLBACK(frame_callback), NULL); + + rtag = gtk_text_buffer_create_tag (text, NULL, "foreground", "red", NULL); + btag = gtk_text_buffer_create_tag (text, NULL, "foreground", "blue", NULL); + + for (i = 0; i < 100; i++) + { + tag[i] = gtk_text_buffer_create_tag (text2, NULL, "foreground", "red", NULL); + tag[i]->values->appearance.fg_color.red = (Colours[i] & 0xff) << 8; + tag[i]->values->appearance.fg_color.green = (Colours[i] & 0xff00); + tag[i]->values->appearance.fg_color.blue = (Colours[i] & 0xff0000) >> 8; + } + + for (i = 0; i < 100; i++) + { + tagm[i] = gtk_text_buffer_create_tag (text, NULL, "foreground", "red", NULL); + tagm[i]->values->appearance.fg_color.red = (Colours[i] & 0xff) << 8; + tagm[i]->values->appearance.fg_color.green = (Colours[i] & 0xff00); + tagm[i]->values->appearance.fg_color.blue = (Colours[i] & 0xff0000) >> 8; + } + + g_timeout_add (200, PollTimer, 0); + + gtk_main (); + + { + + GKeyFile * KF; + gchar * Value; + GError *error = NULL; + gsize length; + FILE *outfile; + + char path[PATH_MAX]; + char *fname = ".BPQTermTCP.ini"; + snprintf(path, PATH_MAX, "%s/%s", getenv("HOME"), fname); + + KF = g_key_file_new(); + g_key_file_load_from_file(KF, path, 0, NULL); + + + //printf("%d %d\n", width, height); + + g_key_file_set_integer(KF, "Session 1", "MTX", mtxparam); + g_key_file_set_integer(KF, "Session 1", "MCOM", MCOM); + g_key_file_set_integer(KF, "Session 1", "MonNODES", MonNODES); + g_key_file_set_integer(KF, "Session 1", "ChatMode", ChatMode); + g_key_file_set_integer(KF, "Session 1", "Bells", Bells); + g_key_file_set_integer(KF, "Session 1", "CurrentHost", CurrentHost); + g_key_file_set_integer(KF, "Session 1", "MONColour", MONColour); + g_key_file_set_integer(KF, "Session 1", "MonPorts", MonPorts); + g_key_file_set_integer(KF, "Session 1", "PortMask", PortMask); + g_key_file_set_integer(KF, "Session 1", "MUIONLY", muionly); + + //sprintf(Font, "%s", Font); + //g_key_file_set_string(KF, "Session 1", "Font", Font); + + + sprintf(Position, "%d,%d,%d,%d,%d", x, y, width, height, vhandle); + g_key_file_set_string(KF, "Session 1", "Position", Position); + + //sprintf(monitormon, "%d,%d", xx, yy); + //g_key_file_set_string(KF, "Session 1", "Scrollwin", monitormon); + Value = g_key_file_to_data(KF, &length, &error); + + outfile = fopen ( path, "w"); + fputs(Value, outfile); + fclose(outfile); + + g_free(Value); + + g_key_file_free(KF); + } + + return 0; +} + + +void open_font_select_dlg(GtkWidget *widget, gpointer data) +{ + font_select_dlg=gtk_font_selection_dialog_new( "Select Font"); + font_select_dlg_ok=GTK_FONT_SELECTION_DIALOG(font_select_dlg)->ok_button; + font_select_dlg_apply=GTK_FONT_SELECTION_DIALOG(font_select_dlg)->apply_button; + font_select_dlg_cancel=GTK_FONT_SELECTION_DIALOG(font_select_dlg)->cancel_button; + g_signal_connect(G_OBJECT(font_select_dlg_ok), "clicked", G_CALLBACK(get_fontname), NULL); + g_signal_connect(G_OBJECT(font_select_dlg_apply), "clicked", G_CALLBACK(apply_fontname), NULL); + g_signal_connect(G_OBJECT(font_select_dlg_cancel), "clicked", G_CALLBACK(font_select_dlg_exit), NULL); + gtk_widget_show_all(font_select_dlg); +} + +void get_fontname(GtkWidget *widget, gpointer data) +{ + gchar *fontname; + fontname=gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(font_select_dlg)); + PangoFontDescription *font_desc; + font_desc=pango_font_description_from_string(Font); + strcpy(Font, font_desc); + gtk_widget_modify_font (entry, font_desc); + gtk_widget_modify_font (view, font_desc); + gtk_widget_modify_font (view2, font_desc); + gtk_widget_modify_font (window, font_desc); + pango_font_description_free (font_desc); + + GKeyFile * KF; + gchar * Value; + GError *error = NULL; + gsize length; + FILE *outfile; + + char path[PATH_MAX]; + char *fname = ".BPQTermTCP.ini"; + snprintf(path, PATH_MAX, "%s/%s", getenv("HOME"), fname); + + KF = g_key_file_new(); + g_key_file_load_from_file(KF, path, 0, NULL); + + sprintf(Font, "%s", fontname); + g_key_file_set_string(KF, "Session 1", "Font", Font); + + Value = g_key_file_to_data(KF, &length, &error); + + outfile = fopen ( path, "w"); + fputs(Value, outfile); + fclose(outfile); + + g_free(Value); + + g_key_file_free(KF); + gtk_widget_destroy(font_select_dlg); +// gtk_entry_new_with_buffer(); + //return; +} + + +void apply_fontname(GtkWidget *widget, gpointer data) +{ + gchar *fontname; + fontname=gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(font_select_dlg)); + + PangoFontDescription *font_desc; + font_desc=pango_font_description_from_string(fontname); + gtk_widget_modify_font(window, font_desc); + gtk_widget_modify_font (entry, font_desc); + gtk_widget_modify_font (view, font_desc); + gtk_widget_modify_font (view2, font_desc); + gtk_widget_modify_font(window, font_desc); + + GKeyFile * KF; + gchar * Value; + GError *error = NULL; + gsize length; + FILE *outfile; + + char path[PATH_MAX]; + char *fname = ".BPQTermTCP.ini"; + snprintf(path, PATH_MAX, "%s/%s", getenv("HOME"), fname); + + KF = g_key_file_new(); + g_key_file_load_from_file(KF, path, 0, NULL); + + sprintf(Font, "%s", fontname); + g_key_file_set_string(KF, "Session 1", "Font", Font); + + Value = g_key_file_to_data(KF, &length, &error); + + outfile = fopen ( path, "w"); + fputs(Value, outfile); + fclose(outfile); + + g_free(Value); + + g_key_file_free(KF); +} + +void font_select_dlg_exit(GtkWidget *widget, gpointer data) +{ + gtk_widget_destroy(font_select_dlg); +} + + +void SendTraceOptions() +{ + char Buffer[80]; + + //int Len = sprintf(Buffer,"\\\\\\\\%x %x %x %x %x %x\r", PortMask, mtxparam, MCOM, MonNODES, MONColour, muionly); + // ** makes the system use utf8 ** + int Len = sprintf(Buffer,"\\\\\\\\%x %x %x %x %x %x %x\r", PortMask, mtxparam, MCOM, MonNODES, MONColour, muionly, 1); + send(sock, Buffer, Len, 0); + +} + +char Save[1000]; +int SaveLen; + +void WritetoOutputWindow(const char * Msg, int len) +{ + const char * ptr1 = Msg; + char * ptr2; + GtkTextIter iter; + GtkTextIter enditer; + int start, end; + GtkTextTag *mtag; + + if (SaveLen) + { + // Have part line - append to it + memcpy(&Save[SaveLen], Msg, len); + SaveLen += len; + ptr1 = Save; + len = SaveLen; + SaveLen = 0; + } + +lineloop: + + if (len <= 0) + { + g_timeout_add (100, ScrollTimer2, 0); + return; + } + + // copy text to control a line at a time + + ptr2 = memchr(ptr1, 13, len); + + if (ptr2 == 0) // No CR + { + memmove(Save, ptr1, len); + SaveLen = len; + return; + } + +// *(ptr2++) = 0; + + if (ptr1[0] == 0x1b) + { + mtag = tag[ptr1[1] - 10]; + + gtk_text_buffer_get_end_iter(text2, &iter); + start = gtk_text_buffer_get_char_count(text2); + gtk_text_buffer_insert(text2, &iter, ptr1 + 2, (ptr2 - ptr1) - 2); + end = gtk_text_buffer_get_char_count(text2); + + gtk_text_buffer_get_iter_at_offset (text2, &iter, start); + gtk_text_buffer_get_iter_at_offset (text2, &enditer, end); + gtk_text_buffer_apply_tag (text2, mtag, &iter, &enditer); + + } + else + { +// gtk_text_insert (GTK_TEXT (text), fixed_font, &text->style->black, NULL, ptr1, -1); + gtk_text_buffer_get_end_iter(text2, &iter); + gtk_text_buffer_insert(text2, &iter, ptr1, ptr2 - ptr1); + + } + +// gtk_text_insert (GTK_TEXT (text), fixed_font, &text->style->black, NULL, "\n", -1); + gtk_text_buffer_get_end_iter(text2, &iter); + gtk_text_buffer_insert(text2, &iter, "\n", -1); +// gtk_text_view_place_cursor_onscreen (view2); + gtk_text_buffer_get_end_iter(text2, &iter); + gtk_text_view_scroll_to_iter ((GtkTextView *)view2, &iter, 0.0, FALSE, 0.0, 0.0); + + +// if (LogMonitor) WriteMonitorLine(ptr1, ptr2 - ptr1); + + len -= (++ptr2 - ptr1); + ptr1 = ptr2; + + goto lineloop; + +} + +char MonSave[1000]; +int MonSaveLen; + +void WritetoMonWindow(char * Msg, int len) +{ + char * ptr1 = Msg, * ptr2; + GtkTextIter iter; + GtkTextIter enditer; + int start, end; + GtkTextTag *mtag; + + if (MonSaveLen) + { + // Have part line - append to it + memcpy(&MonSave[MonSaveLen], Msg, len); + MonSaveLen += len; + ptr1 = MonSave; + len = MonSaveLen; + MonSaveLen = 0; + } + if (enbel) + { + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + gdk_beep(); + } + } while (ptr2); + } + + +lineloop: + + if (len <= 0) + { + g_timeout_add (100, ScrollTimer, 0); + return; + } + // copy text to control a line at a time + + ptr2 = memchr(ptr1, 13, len); + + if (ptr2 == 0) // No CR + { + memmove(MonSave, ptr1, len); + MonSaveLen = len; + return; + } + + if (ptr1[0] == 0x1b) + { + mtag = tagm[ptr1[1] - 10]; + + gtk_text_buffer_get_end_iter(text, &iter); + start = gtk_text_buffer_get_char_count(text); + gtk_text_buffer_insert(text, &iter, ptr1 + 2, (ptr2 - ptr1) - 2); + end = gtk_text_buffer_get_char_count(text); + + gtk_text_buffer_get_iter_at_offset (text, &iter, start); + gtk_text_buffer_get_iter_at_offset (text, &enditer, end); + gtk_text_buffer_apply_tag (text, mtag, &iter, &enditer); + } + else + { + gtk_text_buffer_get_end_iter(text, &iter); + gtk_text_buffer_insert(text, &iter, ptr1, (ptr2 - ptr1)); + + } + + gtk_text_buffer_get_end_iter(text, &iter); + gtk_text_buffer_insert(text, &iter, "\n", 1); + + gtk_text_view_scroll_to_iter ((GtkTextView *)view, &iter, 0.0, FALSE, 0.0, 0.0); + +// if (LogMonitor) WriteMonitorLine(ptr1, ptr2 - ptr1); + + len -= (++ptr2 - ptr1); + ptr1 = ptr2; + + goto lineloop; +} + + +int SendMsg(const char * msg, int len) +{ + send(sock, msg, len, 0); + return 0; +} + +int TCPConnect(char * Host, char * Port) +{ + + int err = 0; +// u_long param=1; +// int bcopt=TRUE; +// struct sockaddr_in sinx; +// int addrlen=sizeof(sinx); + char Title[80]; + + struct addrinfo hints, *res = NULL; + + Disconnecting = FALSE; + + // get host info, make socket, and connect it + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_STREAM; + getaddrinfo(Host, Port, &hints, &res); + + if (!res) + { + dialog = gtk_message_dialog_new ((GtkWindow *)window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + "Resolve HostName Failed"); + + gtk_window_set_title (GTK_WINDOW (dialog), "TermTCP"); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + + sprintf(Title,"TermTCP Version %s - Disconnected", VersionString); + + gtk_window_set_title (GTK_WINDOW (window), Title); + + return FALSE; // Resolve failed + + } + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) + { + // + // Connected successful + // + + Telnet_Connected(sock, 0); + + return TRUE; + } + else + { + err=errno; + + if (err == 10035) + { + // Connect in Progress + + sprintf(Title,"BPQTermTCP Version %s - Connecting to %s", VersionString, Host); + gtk_window_set_title (GTK_WINDOW (window), Title); + + EnableDisconnectMenu(); + DisableConnectMenu(); + + return TRUE; + } + else + { + // Connect failed + + closesocket(sock); + dialog = gtk_message_dialog_new ((GtkWindow *)window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Connect Failed"); + + gtk_window_set_title (GTK_WINDOW (dialog), "BPQTermTCP"); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + return FALSE; + } + } + + return FALSE; +} + +#define MAX_MSG_LEN 512 + + +int ProcessReceivedData() +{ + char message[MAX_MSG_LEN + 10]; + gchar * ptr; + char * Buffptr; + char * FEptr = 0; + int len = 0, MonLen; + + len = recv(sock, message, MAX_MSG_LEN, 0); + + if (len <= 0) + { + if (Disconnecting == FALSE) + { + shutdown(sock, 2); // SD_BOTH + Disconnecting = TRUE; + } + else + if (SocketActive) + closesocket(sock); + + sprintf(Title,"BPQTermTCP Version %s - Disconnected", VersionString); + gtk_window_set_title (GTK_WINDOW (window), Title); + DisableDisconnectMenu(); + EnableConnectMenu(); + + WritetoOutputWindow(DisMsg, strlen(DisMsg)); + SocketActive = FALSE; + Connected = FALSE; + MonData = FALSE; + return TRUE; + } + + if (len == 0) + { + printf("recv - len = 0\r\n"); + if (Disconnecting == FALSE) + { + shutdown(sock, 2); // SD_BOTH + Disconnecting = TRUE; + } + else + closesocket(sock); + + return TRUE; + } + + message[len] = 0; + + // Look for MON delimiters (FF/FE) + + Buffptr = message; + + if (MonData) + { + // Already in MON State + + FEptr = memchr(Buffptr, 0xfe, len); + + if (!FEptr) + { + // no FE - so send all to monitor + + WritetoMonWindow(Buffptr, len); + return TRUE; + } + + MonData = FALSE; + + MonLen = FEptr - Buffptr; // Mon Data, Excluding the FE + + WritetoMonWindow(Buffptr, MonLen); + + Buffptr = ++FEptr; // Char following FE + + if (++MonLen < len) + { + len -= MonLen; + goto MonLoop; // See if next in MON or Data + } + + // Nothing Left + + return TRUE; + } + +MonLoop: + + ptr = memchr(Buffptr, 0xff, len); + + if (ptr) + { + // Buffer contains Mon Data + + if (ptr > Buffptr) + { + // Some Normal Data before the FF + + int NormLen = ptr - Buffptr; // Before the FF + WritetoOutputWindow(Buffptr, NormLen); + + len -= NormLen; + Buffptr = ptr; + goto MonLoop; + } + + MonData = TRUE; + + FEptr = memchr(Buffptr, 0xfe, len); + + if (FEptr) + { + MonData = FALSE; + + MonLen = FEptr + 1 - Buffptr; // MonLen includes FF and FE + WritetoMonWindow(Buffptr+1, MonLen - 2); + + len -= MonLen; + Buffptr += MonLen; // Char Following FE + + if (len <= 0) + { + return TRUE; + } + goto MonLoop; + } + else + { + // No FE, so rest of buffer is MON Data + + WritetoMonWindow(Buffptr+1, len -1); // Exclude FF +// dorefresh(); + return TRUE; + } + } + + // No FF, so must be session data + + WritetoOutputWindow(Buffptr, len); + SlowTimer = 0; + + return TRUE; +} + +int Telnet_Connected(SOCKET sock, int Error) +{ + char Msg[80]; + int Len; + + // Connect Complete + + if (Error) + { + dialog = gtk_message_dialog_new ((GtkWindow *)window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Connect Failed"); + + gtk_window_set_title (GTK_WINDOW (dialog), "TermTCP"); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + closesocket(sock); + Connecting = FALSE; + SocketActive = FALSE; + + sprintf(Title,"TermTCP Version %s - Disconnected", VersionString); + gtk_window_set_title (GTK_WINDOW (window), Title); + DisableDisconnectMenu(); + EnableConnectMenu(); + + return 0; + + } + +// RecvChannel = g_io_channel_unix_new((gint)sock); +// RecvChannel = g_io_channel_win32_new_socket((gint)sock); +// g_io_channel_set_encoding (RecvChannel, NULL, NULL); +// g_io_channel_set_flags(RecvChannel, G_IO_FLAG_APPEND| G_IO_FLAG_NONBLOCK, NULL); +// g_io_add_watch(RecvChannel, G_IO_IN | G_IO_HUP, GtkMsg_ShowMessage, 0); + + SocketActive = TRUE; + Connecting = FALSE; + Connected = TRUE; + + Len = sprintf(Msg, "%s\r%s\rBPQTermTCP\r", UserName[CurrentHost], Password[CurrentHost]); + + SendMsg(Msg, Len); + + SendTraceOptions(); + + SlowTimer = 0; + + sprintf(Title,"TermTCP Version %s - Connected to %s", VersionString, Host[CurrentHost]); + gtk_window_set_title (GTK_WINDOW (window), Title); + DisableConnectMenu(); + EnableDisconnectMenu(); + + return 0; +} + +static void Disconnect(GtkWidget *w, gpointer data) +{ + if (Disconnecting) + { + // Force close + + if (SocketActive) + closesocket(sock); + + sprintf(Title,"TermTCP Version %s - Disconnected", VersionString); + gtk_window_set_title (GTK_WINDOW (window), Title); + + DisableDisconnectMenu(); + EnableConnectMenu(); + + + WritetoOutputWindow(DisMsg, strlen(DisMsg)); + SocketActive = FALSE; + Connected = FALSE; + Disconnecting = FALSE; + return; + } + shutdown(sock, 2); // SD_BOTH + Disconnecting = TRUE; +} + +void EnableDisconnectMenu() +{ + gtk_widget_set_sensitive(dis_item, TRUE); +} +void DisableConnectMenu() +{ + gtk_widget_set_sensitive(conn_item, FALSE); +} +void EnableConnectMenu() +{ + gtk_widget_set_sensitive(conn_item, TRUE); +} +void DisableDisconnectMenu() +{ + gtk_widget_set_sensitive(dis_item, FALSE); + +} + +void ReadConfig() +{ + FILE *infile; + GKeyFile * KF; +// gchar * Value; + gchar * Posn; +// gchar * font; + + GError *error = NULL; + char path[PATH_MAX]; + char *fname = ".BPQTermTCP.ini"; + snprintf(path, PATH_MAX, "%s/%s", getenv("HOME"), fname); + + KF = g_key_file_new(); + g_key_file_load_from_file(KF, path, 0, NULL); + + Posn = g_key_file_get_string (KF, "Session 1", "Position", &error); + if (Posn) + sscanf(Posn,"%d,%d,%d,%d,%d",&gx,&gy, &width, &height, &vhandle); + + printf("%s %d %d %d %d %d\n", Posn, gx, gy, width, height, vhandle); + + fontname = g_key_file_get_string (KF, "Session 1", "Font", &error); + if (fontname) + strcpy(Font, fontname); + + PortMask = g_key_file_get_integer(KF, "Session 1", "PortMask", &error); + Bells = g_key_file_get_integer(KF, "Session 1", "Bells", &error); + MCOM = g_key_file_get_integer(KF, "Session 1", "MCOM", &error); + MONColour = g_key_file_get_integer(KF, "Session 1", "MONColour", &error); + MonNODES= g_key_file_get_integer(KF, "Session 1", "MonNODES", &error); + MonPorts= g_key_file_get_integer(KF, "Session 1", "MonPorts", &error); + ChatMode= g_key_file_get_integer(KF, "Session 1", "ChatMode", &error); + CurrentHost = g_key_file_get_integer (KF, "Session 1", "CurrentHost", &error); + mtxparam = g_key_file_get_integer (KF, "Session 1", "MTX", &error); +// fontname = g_key_file_intger (KF, "Session 1", "FontName", &error); +// charset = g_key_file_intger (KF, "Session 1", "CharSet", &error); +// codepage = g_key_file_intger (KF, "Session 1", "CodePage", &error); +// fontsize = g_key_file_intger (KF, "Session 1", "FontSize", &error); +// fontwidth = g_key_file_intger (KF, "Session 1", "FontWidth", &error); + muionly = g_key_file_get_integer (KF, "Session 1", "MUIONLY", &error); + + g_key_file_free(KF); + + infile = fopen ( path, "r"); + + if (infile) + { + char buffer[1024]; + char * ret; + char * ptr; + + while (1) + { + ret = fgets(buffer, 1024, infile); + + if (ret == 0) + { + fclose (infile); + return; + } + + ptr = strchr(buffer, 10); + + if (ptr) + *ptr = 0; + + if (memcmp(buffer, "Host", 4) == 0) + { + int port = atoi(&buffer[4]) - 1; + strcpy(&Host[port][0], &buffer[6]); + continue; + } + if (memcmp(buffer, "Port", 4) == 0) + { + int port = atoi(&buffer[4]) - 1; + strcpy(&Port[port][0], &buffer[6]); + continue; + } + if (memcmp(buffer, "User", 4) == 0) + { + int port = atoi(&buffer[4]) - 1; + strcpy(&UserName[port][0], &buffer[6]); + continue; + } + if (memcmp(buffer, "Pass", 4) == 0) + { + int port = atoi(&buffer[4]) - 1; + strcpy(&Password[port][0], &buffer[6]); + continue; + } + } + } +// return 0 +} diff --git a/UDPtoTCP/UDPtoTCP.sln b/UDPtoTCP/UDPtoTCP.sln new file mode 100644 index 0000000..2e4adcf --- /dev/null +++ b/UDPtoTCP/UDPtoTCP.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UDPtoTCP", "UDPtoTCP\UDPtoTCP.vcproj", "{879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Debug|Win32.ActiveCfg = Debug|Win32 + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Debug|Win32.Build.0 = Debug|Win32 + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Release|Win32.ActiveCfg = Release|Win32 + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/UDPtoTCP/UDPtoTCP/UDPtoTCP.c b/UDPtoTCP/UDPtoTCP/UDPtoTCP.c new file mode 100644 index 0000000..95e58b7 --- /dev/null +++ b/UDPtoTCP/UDPtoTCP/UDPtoTCP.c @@ -0,0 +1,391 @@ +// UDPtoTCP.cpp : Defines the entry point for the console application. +// + +#include + +#ifdef WIN32 +#include "winsock2.h" +#include "WS2tcpip.h" +#else +#define VOID void +#define SOCKET int +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#define SOCKADDR_IN struct sockaddr_in +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define closesocket close +#include +#include +#include +#include +#include +#include +#endif + +#define MaxSockets 64 + + +struct SocketConnectionInfo +{ + int Number; // Number of record - for AGWConnections display + SOCKET socket; + struct sockaddr_in sin; + BOOL SocketActive; + }; + + +static struct SocketConnectionInfo Sockets[MaxSockets+1]; + +int CurrentConnections; + +static int CurrentSockets=0; + +int TCPPort = 10110; +int UDPPort = 10110; + +SOCKET tcpsock; +SOCKET udpsock; + +void Poll(); +int Init(); +int Socket_Accept(SOCKET SocketId); +int DataSocket_Disconnect(struct SocketConnectionInfo * sockptr); +int DataSocket_Read(struct SocketConnectionInfo * sockptr, SOCKET sock); + + +#ifndef WIN32 + + +#include + +pthread_t _beginthread(void(*start_address)(), unsigned stack_size, VOID * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*) arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +int Sleep(int ms) +{ + usleep(ms * 1000); + return 0; +} + +#endif + + +int main(int argc, char * argv[]) +{ + if (Init() == 0) + return 0; + + while (1) + { + Sleep(1000); + Poll(); + } + return 0; +} + + +VOID Poll() +{ + struct SocketConnectionInfo * sockptr; + + // Look for incoming connects + + fd_set readfd, writefd, exceptfd; + struct timeval timeout; + int retval; + int n; + int Active = 0; + SOCKET maxsock; + + // Look for connects + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + FD_ZERO(&readfd); + + FD_SET(tcpsock, &readfd); + + retval = select((int)tcpsock + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + perror("listen select"); + } + + if (retval) + if (FD_ISSET((int)tcpsock, &readfd)) + Socket_Accept(tcpsock); + + // Look for UDP Data + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + FD_ZERO(&readfd); + + FD_SET(udpsock, &readfd); + + retval = select((int)udpsock + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + perror("udp poll select"); + } + + if (retval) + { + if (FD_ISSET((int)udpsock, &readfd)) + { + char Message[512] = ""; + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + int len, n; + struct SocketConnectionInfo * sockptr; + + len = recvfrom(udpsock, Message, 512, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (len > 0) + printf("%s", Message); + + // Send to all connected clients + + for (n = 1; n <= CurrentSockets; n++) + { + sockptr = &Sockets[n]; + + if (sockptr->SocketActive) + { + send(sockptr->socket, Message, len, 0); + } + } + } + } + + + // look for data on any active sockets + + maxsock = 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + + // Check for data on active streams + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + FD_SET(sock, &readfd); + FD_SET(sock, &exceptfd); + + Active++; + if (sock > maxsock) + maxsock = sock; + } + } + + if (Active) + { + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("data select"); + } + else + { + if (retval) + { + // see who has data + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + if (FD_ISSET(sock, &exceptfd)) + DataSocket_Disconnect(sockptr); + + if (FD_ISSET(sock, &readfd)) + DataSocket_Read(sockptr, sock); + + } + } + } + } + } +} + + +SOCKADDR_IN local_sin; +SOCKADDR_IN sinx; + +SOCKADDR_IN * psin; + +int Init() +{ + char opt = TRUE; +#ifdef WIN32 + WSADATA WsaData; // receives data from WSAStartup + WSAStartup(MAKEWORD(2, 0), &WsaData); +#endif + + if (TCPPort == 0) + return 0; + +// Create listening socket + + tcpsock = socket( AF_INET, SOCK_STREAM, 0); + + if (tcpsock == INVALID_SOCKET) + { + printf("socket() failed error %d\r\n", errno); + return 0; + } + + setsockopt (tcpsock, SOL_SOCKET, SO_REUSEADDR, &opt, 1); + + psin=&local_sin; + + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + psin->sin_port = htons(TCPPort); /* Convert to network ordering */ + + if (bind(tcpsock, (struct sockaddr *)&local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + printf("bind(sock) failed Port %d Error %d\r\n", TCPPort, errno); + closesocket(tcpsock); + + return 0; + } + + udpsock = socket(AF_INET,SOCK_DGRAM,0); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(UDPPort); + + if (bind(udpsock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = errno; + printf("Bind Failed for UDP port %d - error code = %d\r\n", UDPPort, err); + return 0; + } + + if (listen(tcpsock, 5) < 0) + { + printf("listen(tcpsock) failed Error %d\r\n", errno); + return 0; + } + + _beginthread(Poll,0,0); + + return 1; +} + + + + + + + +int Socket_Accept(SOCKET SocketId) +{ + int n,addrlen; + struct SocketConnectionInfo * sockptr; + SOCKET sock; + unsigned char work[4]; + +// Find a free Socket + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive == FALSE) + { + addrlen=sizeof(struct sockaddr); + + memset(sockptr, 0, sizeof(struct SocketConnectionInfo)); + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + printf("accept() failed Error %d\r", errno); + return FALSE; + } + + sockptr->socket = sock; + sockptr->SocketActive = TRUE; + sockptr->Number = n; + + if (CurrentSockets < n) CurrentSockets = n; //Record max used to save searching all entries + + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + + printf("Connected Session %d from %d.%d.%d.%d\r\n", n, work[0], work[1], work[2], work[3]); + return 0; + } + } + + // Should accept, then immediately close + + return 0; +} + +int DataSocket_Read(struct SocketConnectionInfo * sockptr, SOCKET sock) +{ + int i; + char Message[512] = ""; + + i = recv(sock, Message, 512, 0); + + if (i == SOCKET_ERROR || i == 0) + { + // Failed or closed - clear connection + + DataSocket_Disconnect(sockptr); + return 0; + } + + printf(Message); + + return 0; +} + +int DataSocket_Disconnect(struct SocketConnectionInfo * sockptr) +{ + closesocket(sockptr->socket); + + sockptr->SocketActive = FALSE; + + printf("Session %d Disconnected\r\n", sockptr->Number); + + return 0; +} + + + diff --git a/UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj b/UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj new file mode 100644 index 0000000..4bac56b --- /dev/null +++ b/UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj.SKIGACER.johnw.user b/UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj.SKIGACER.johnw.user new file mode 100644 index 0000000..b5b0536 --- /dev/null +++ b/UDPtoTCP/UDPtoTCP/UDPtoTCP.vcproj.SKIGACER.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/UIARQ.c b/UIARQ.c new file mode 100644 index 0000000..a0d7de9 --- /dev/null +++ b/UIARQ.c @@ -0,0 +1,1850 @@ +/* +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 +*/ + +// +// Runs FLARQ-like protocol over UI Packets +// + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + + +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define MAXARQ 10 + +#define DLE 0x10 +#define SOH 1 +#define STX 2 +#define EOT 4 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; +int SemHeldByAPI; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +static VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len); +VOID ProcessFLDigiKISSPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer, int Stream); +VOID SendRPBeacon(struct TNCINFO * TNC); +unsigned int CalcCRC(UCHAR * ptr, int Len); +static VOID ARQTimer(struct TNCINFO * TNC); +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +static VOID ProcessARQStatus(struct TNCINFO * TNC, int Stream, struct ARQINFO * ARQ, char *Input); +VOID CheckFLDigiData(struct TNCINFO * TNC); +static VOID SendPacket(struct TNCINFO * TNC, struct STREAMINFO * STREAM, UCHAR * Msg, int MsgLen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer); +char * strlop(char * buf, char delim); + +extern UCHAR BPQDirectory[]; +extern char MYALIASLOPPED[10]; + + +#define MAXBPQPORTS 32 +#define MAXMPSKPORTS 16 + +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +extern int Blocksizes[10]; + +static char WindowTitle[] = "UIARQ"; +static char ClassName[] = "UIARQSTATUS"; +static int RigControlRow = 165; + + + +static int ExtProc(int fn, int port,unsigned char * buff) +{ + UINT * buffptr; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + + for (Stream = 0; Stream <= MAXARQ; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + SetWindowText(STREAM->xIDC_STATUS, "Attached"); + SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall); + + } + } + + switch (fn) + { + case 7: + + // 100 mS Timer. + + for (Stream = 0; Stream <= MAXARQ; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + TidyClose(TNC, Stream); + } + } + } + + ARQTimer(TNC); + + return 0; + + case 1: // poll + + // See if any frames for this port + + for (Stream = 0; Stream <= MAXARQ; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff[4] = Stream; + + return -1; + } + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen=buffptr[1]; + + buff[4] = Stream; + buff[7] = 0xf0; + memcpy(&buff[8],buffptr+2,datalen); // Data goes to +7, but we have an extra byte + datalen+=8; + + PutLengthinBuffer((PDATAMESSAGE)buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + int SendLen; + char Reply[256]; + int UILen; + char * UIMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + UILen = buffptr->LENGTH; + UILen -= 23; + UIMsg = buffptr->L2DATA; + + UIMsg[UILen] = 0; + + if (UILen < 129 && STREAM->Attached == FALSE) // Be sensible! + { + // >00uG8BPQ:72 TestA + SendLen = sprintf(Reply, "u%s:72 %s", TNC->NodeCall, UIMsg); + SendPacket(TNC, STREAM, Reply, SendLen); + } + ReleaseBuffer(buffptr); + } + + return (0); + + case 2: // send + + + Stream = buff[4]; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr[1] = txlen; + memcpy(buffptr+2, &buff[8], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + return (0); + } + else + { + buff[8 + txlen] = 0; + _strupr(&buff[8]); + + if (_memicmp(&buff[8], "D\r", 2) == 0) + { + if (STREAM->Connected) + TidyClose(TNC, buff[4]); + + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if a Connect Command. + + if (toupper(buff[8]) == 'C' && buff[9] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + struct ARQINFO * ARQ = STREAM->ARQInfo; + int SendLen; + char Reply[80]; + + _strupr(&buff[8]); + buff[8 + txlen] = 0; + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->OurStream = Stream + 64; + ARQ->FarStream = 64; // Not yet defined + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff[10], " ,\r", &context); + strcpy(STREAM->RemoteCall, ptr); + +//00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + SetWindowText(STREAM->xIDC_STATUS, "Connecting"); + SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall); + SetWindowText(STREAM->xIDC_DESTCALL, STREAM->RemoteCall); + SetWindowText(STREAM->xIDC_DIRN, "Out"); + + STREAM->Connecting = TRUE; + + return 0; + } + } + return (0); + + case 3: + + Stream = (int)buff; + + STREAM = &TNC->Streams[Stream]; + { + // Busy if TX Window reached + + struct ARQINFO * ARQ = STREAM->ARQInfo; + int Outstanding; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + return (1 | 1 << 8 | STREAM->Disconnecting << 15); // 3rd Nibble is frames unacked + else + return 1 << 8 | STREAM->Disconnecting << 15; + + } + return 1 << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + return 0; + } + + return 0; +} + +VOID UIHook(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + PORT = GetPortTableEntryFromPortNum(PORT->HookPort->PORTNUMBER); + if (PORT) + ProcessARQPacket(PORT, Buffer); +} +static VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + char Count[16]; + + sprintf(Count, "%d", STREAM->BytesRXed); + SetWindowText(STREAM->xIDC_RXED, Count); + + sprintf(Count, "%d", STREAM->BytesTXed); + SetWindowText(STREAM->xIDC_SEND, Count); + + sprintf(Count, "%d", STREAM->BytesResent); + SetWindowText(STREAM->xIDC_RESENT, Count); + + sprintf(Count, "%d", STREAM->BytesAcked); + SetWindowText(STREAM->xIDC_ACKED, Count); +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FLDigi Status" + "

FLDIGI Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +UINT UIARQExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + struct PORTCONTROL * PORT; + struct STREAMINFO * STREAM; + + srand((unsigned int)time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return (int) ExtProc; + } + + for (i = 0; i Streams[i].ARQInfo = zalloc(sizeof(struct ARQINFO)); + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0 || PortEntry->PORTCONTROL.PORTPACLEN > 128) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_UIARQ; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + PortEntry->MAXHOSTMODESESSIONS = MAXARQ; + + i = 0; + + while (TNC->ARQPorts[i]) + { + PORT = GetPortTableEntryFromPortNum(TNC->ARQPorts[i]); + PORT->UIHook = (FARPROCY)UIHook; + PORT->HookPort = (struct PORTCONTROL *)PortEntry; + i++; + } + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 560, 350, ForcedClose); + + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " MyCall", WS_CHILD | WS_VISIBLE, 5,6,79,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " DestCall", WS_CHILD | WS_VISIBLE, 85,6,79,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Status", WS_CHILD | WS_VISIBLE, 165,6,84,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Sent", WS_CHILD | WS_VISIBLE, 250,6,59,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Rxed", WS_CHILD | WS_VISIBLE, 310,6,59,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Resent", WS_CHILD | WS_VISIBLE, 370,6,59,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Acked", WS_CHILD | WS_VISIBLE, 430,6,59,20, TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Dirn", WS_CHILD | WS_VISIBLE, 490,6,49,20, TNC->hDlg, NULL, hInstance, NULL); + + for (i = 0; i Streams[i]; + + STREAM->xIDC_MYCALL = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 5, 26 + i*20, 79, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_DESTCALL = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 85, 26 + i*20, 79, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_STATUS = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 165, 26 + i*20, 84, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_SEND = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 250, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_RXED = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 310, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_RESENT = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 370, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_ACKED = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 430, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL); + STREAM->xIDC_DIRN = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 490, 26 + i*20, 49, 20, TNC->hDlg, NULL, hInstance, NULL); + } + + TNC->ClientHeight = 360; + TNC->ClientWidth = 560; + + MoveWindows(TNC); + +#endif + + i=sprintf(Msg,"UIARQ\n"); + WritetoConsole(Msg); + + return ((int) ExtProc); +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + char * p_ipad = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + + char errbuf[256]; + + strcpy(errbuf, buf); + + BPQport = Port; + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->Timeout = 50; // Default retry = 5 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + // Read Initialisation lines + + while(TRUE) + { + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "TIMEOUT", 7) == 0) + TNC->Timeout = atoi(&buf[8]) * 10; + else if (_memicmp(buf, "RETRIES", 7) == 0) + TNC->Retries = atoi(&buf[8]); + else if (_memicmp(buf, "WINDOW", 6) == 0) + TNC->Window = atoi(&buf[7]); + else + { + char * ptr, * p_value, * p_port; + int i; + + ptr = strtok(buf, "=\t\n\r"); + p_value = strtok(NULL, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + if (_stricmp(ptr,"Ports") == 0) + { + int n = 0; + + p_port = strtok(p_value, " ,\t\n\r"); + + while (p_port != NULL) + { + i = atoi(p_port); + if (i == 0) return FALSE; + if (i > NUMBEROFPORTS) return FALSE; + + TNC->ARQPorts[n++] = i; + p_port = strtok(NULL, " ,\t\n\r"); + } + } + } + if (GetLine(buf) == 0) + return TRUE; + + } + return FALSE; +} +static VOID SendPacket(struct TNCINFO * TNC, struct STREAMINFO * STREAM, UCHAR * Msg, int MsgLen) +{ + DIGIMESSAGE Block = {0}; + int Port = TNC->ARQPorts[0]; + + Block.CTL = 3; + Block.PID = 0xF0; + + ConvToAX25(STREAM->RemoteCall, Block.DEST); + memcpy(Block.ORIGIN, MYCALL, 7); + + Block.L2DATA[0] = STREAM->ARQInfo->FarStream; + memcpy(&Block.L2DATA[1], Msg, MsgLen); + MsgLen += 1; + + Send_AX_Datagram(&Block, MsgLen + 2, Port); // Inulude CTL and PID +} + +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, int Stream); + +VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // ARQ Packet from KISS-Like Hardware + + struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER]; + UCHAR * Input; + int Len; + int Stream = Buffer->L2DATA[0] - 64; + + if (Stream < 0 || Stream > MAXARQ) + return; + + // First Bytes is Stream Number (as ASCII Letter) + + Input = &Buffer->L2DATA[1]; + Len = Buffer->LENGTH - 24; + + ProcessFLDigiData(TNC, Input, Len, Stream); +} + +/* + +00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06 +00kG8BPQ:24 G8BPQ 4 85F9B + +00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5 (128, 5) + +,00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 5 89FCA + +First no sees to be a connection counter. Next may be stream + + +08s___ABFC +08tG8BPQ:73 xxx 33FA +00tG8BPQ:73 yyy 99A3 +08dG8BPQ:90986C +00bG8BPQ:911207 + +call:90 for dis 91 for dis ack 73 for chat) + +08pG8BPQ?__645E +00s_??4235 + +08pG8BPQ?__645E +00s_??4235 + +i Ident +c Connect +k Connect Ack +r Connect NAK +d Disconnect req +s Data Ack/ Retransmit Req )status) +p Poll +f Format Fail +b dis ack +t talk + +a Abort +o Abort ACK + + +00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 6 49A3A +08s___ABFC +08 ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::90 +ARQ::STX +//FLARQ COMPOSER +Date: 09/01/2014 23:24:42 +To: gm8bpq +From: +SubjectA0E0 +08!: Test + +Test Message + +ARQ::ETX +F0F2 +08pG8BPQ!__623E +08pG8BPQ!__623E +08pG8BPQ!__623E + + + + +*/ +static VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, int Stream) +{ + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + char CTRL = Input[0]; + struct ARQINFO * ARQ = STREAM->ARQInfo; + char Channel = Stream + 64; + int SendLen; + char Reply[80]; + + Input[Len] = 0; + + // Process Message + + // This processes eitrher message from the KISS or RAW interfaces. + // Headers and RAW checksum have been removed, so packet starts with Control Byte + + // Only a connect request is allowed with no session, so check first + + if (CTRL == 'c') + { + // Connect Request + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = TNC->Window; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + if (Stream) + return; // Shouldn't have Stream on Connect Request + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + // See if for us + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (_stricmp(call2, Appl) == 0) + break; + + memcpy(Appl, APPL->APPLALIAS_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (_stricmp(call2, Appl) == 0) + break; + + } + + if (App > 31) + if (strcmp(TNC->NodeCall, call2) !=0) + if (strcmp(call2, MYALIASLOPPED) !=0) + return; // Not Appl or Port/Node Call + + ptr = strtok_s(NULL, " ", &context); + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + BlockSize = atoi(ptr); + + if (ARQ->ARQState > ARQ_CONNECTING) + { + // We have already received a connect request - just ACK it + + goto AckConnectRequest; + } + + // Get a Session + + Stream = 1; + + while(Stream <= MAXARQ) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + goto GotStream; + + Stream++; + } + + // No free streams - send Disconnect + + return; + + GotStream: + + STREAM = &TNC->Streams[Stream]; + + ProcessIncommingConnect(TNC, call1, Stream, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + strcpy(STREAM->MyCall, call2); + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + ARQ = STREAM->ARQInfo; + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->FarStream = FarStream; + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->ARQState = ARQ_ACTIVE; + ARQ->OurStream = Stream + 64; + + STREAM->NeedDisc = 0; + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char Buffer[32]; + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + STREAM->NeedDisc = 50; // 1 sec + } + } + + ARQ->TXWindow = Window; + + if (BlockSize < 4) BlockSize = 4; + if (BlockSize < 9) BlockSize = 9; + + ARQ->MaxBlock = Blocksizes[BlockSize]; + + + ARQ->ARQTimer = 1; // To force CTEXT to be Queued + + if (App == 32) + { + // Connect to Node - send CTEXT + + if (HFCTEXTLEN > 1) + { + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = HFCTEXTLEN; + memcpy(&buffptr[2], HFCTEXT, HFCTEXTLEN); + SendARQData(TNC, buffptr, Stream); + } + } + } + + if (STREAM->NeedDisc) + { + // Send Not Avail + + buffptr = GetBuff(); + if (buffptr) + { + buffptr[1] = sprintf((char *)&buffptr[2], "Application Not Available\r"); + SendARQData(TNC, buffptr, Stream); + } + } + + SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall); + SetWindowText(STREAM->xIDC_DESTCALL, STREAM->RemoteCall); + SetWindowText(STREAM->xIDC_STATUS, "ConPending"); + SetWindowText(STREAM->xIDC_DIRN, "In"); + + +AckConnectRequest: + + SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_CONNECTACK; + + return; + } + + // All others need a session + +// if (!STREAM->Connected && !STREAM->Connecting) +// return; + + if (CTRL == 'k') + { + // Connect ACK + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = 16; + + char Reply[80]; + int ReplyLen; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ptr = strtok_s(NULL, " ", &context); + if (ptr) + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + if (ptr) + BlockSize = atoi(ptr); + + if (STREAM->Connected) + goto SendKReply; // Repeated ACK + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + STREAM->Connected = TRUE; + + ARQ->ARQTimerState = 0; + ARQ->ARQTimer = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall); + SetWindowText(STREAM->xIDC_DESTCALL, STREAM->RemoteCall); + SetWindowText(STREAM->xIDC_DIRN, "Out"); + + UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z'); + + ARQ->ARQTimerState = 0; + ARQ->FarStream = FarStream; + ARQ->TXWindow = TNC->Window; + ARQ->MaxBlock = Blocksizes[BlockSize]; + + ARQ->ARQState = ARQ_ACTIVE; + + STREAM->NeedDisc = 0; + + buffptr = GetBuff(); + + if (buffptr) + { + ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall); + + buffptr[1] = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(STREAM->xIDC_STATUS, "Connected"); + +SendKReply: + + // Reply with status + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + // All others need a session + + //if (!STREAM->Connected) + // return; + + + if (CTRL == 's') + { + // Status + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; // Stop retry timer + Input[Len] = 0; + ProcessARQStatus(TNC, Stream, ARQ, &Input[1]); + + return; + } + + if (CTRL == 'p') + { + // Poll + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " \x1A", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + Debugprintf("Sending Poll Resp TX NOGaps High %d %d %d", ARQ->TXSeq, ARQ->RXNoGaps, ARQ->RXHighest); + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + else + ARQ->TurnroundTimer = 15; // Allow us to send it all acked + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + return; + } + + + if (CTRL == 'a') + { + // Abort. Send Abort ACK - same as + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " :", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + if (CTRL == 'i') + { + // Ident + + return; + } + + if (CTRL == 't') + { + // Talk - not sure what to do with these + + return; + } + + if (CTRL == 'd') + { + // Disconnect Request + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + + // As the Disc ACK isn't repeated, we have to clear session now + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + + SetWindowText(STREAM->xIDC_MYCALL, ""); + SetWindowText(STREAM->xIDC_DESTCALL, ""); + SetWindowText(STREAM->xIDC_DIRN, ""); + SetWindowText(STREAM->xIDC_STATUS, ""); + + ARQ->ARQState = 0; + + SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->Retries = 2; + return; + } + + if (CTRL == 'b') + { + // Disconnect ACK + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + ARQ->ARQState = 0; + + if (STREAM->Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(STREAM->xIDC_MYCALL, ""); + SetWindowText(STREAM->xIDC_DESTCALL, ""); + SetWindowText(STREAM->xIDC_DIRN, ""); + SetWindowText(STREAM->xIDC_STATUS, ""); + + return; + } + + if (CTRL == 'u') + { + // Beacon + + //>00uGM8BPQ:72 GM8BPQ TestingAD67 + + char * Call = &Input[1]; + strlop(Call, ':'); + + UpdateMH(TNC, Call, '!', 0); + return; + } + + if (STREAM->Connected) + { + if (Channel != ARQ->OurStream) + return; // Wrong Session + + if (CTRL >= ' ' && CTRL < 96) + { + // ARQ Data + + int Seq = CTRL - 32; + int Work; + +// if (rand() % 5 == 2) +// { +// Debugprintf("Dropping %d", Seq); +// return; +// } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return; // Sould never run out, but cant do much else + + // Remove any DLE transparency + + Len -= 1; + + buffptr[1] = Len; + memcpy(&buffptr[2], &Input[1], Len); + STREAM->BytesRXed += Len; + + UpdateStatsLine(TNC, STREAM); + + // Safest always to save, then see what we can process + + if (ARQ->RXHOLDQ[Seq]) + { + // Wot! Shouldn't happen + + ReleaseBuffer(ARQ->RXHOLDQ[Seq]); +// Debugprintf("ARQ Seq %d Duplicate"); + } + + ARQ->RXHOLDQ[Seq] = buffptr; +// Debugprintf("ARQ saving %d", Seq); + + // If this is higher that highest received, save. But beware of wrap' + + // Hi = 2, Seq = 60 dont save s=h = 58 + // Hi = 10 Seq = 12 save s-h = 2 + // Hi = 14 Seq = 10 dont save s-h = -4 + // Hi = 60 Seq = 2 save s-h = -58 + + Work = Seq - ARQ->RXHighest; + + if ((Work > 0 && Work < 32) || Work < -32) + ARQ->RXHighest = Seq; + + // We may now be able to process some + + Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need + + while (ARQ->RXHOLDQ[Work]) + { + // We have it + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]); +// ReleaseBuffer(ARQ->RXHOLDQ[Work]); + + ARQ->RXHOLDQ[Work] = NULL; +// Debugprintf("Processing %d from Q", Work); + + ARQ->RXNoGaps = Work; + Work = (Work + 1) & 63; // The next one we need + } + + ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data + return; + } + } +} + + +static VOID SendARQData(struct TNCINFO * TNC, UINT * Buffer, int Stream) +{ + // Send Data, saving a copy until acked. + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + struct ARQINFO * ARQ = STREAM->ARQInfo; + + + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + UCHAR * ptr; + int Origlen = Buffer[1]; + + ARQ->TXSeq++; + ARQ->TXSeq &= 63; + + SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32); + + ptr = (UCHAR *)&Buffer[2]; // Start of data; + + ptr[Buffer[1]] = 0; + + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Origlen); + SendLen += Origlen; + + TXBuffer[SendLen] = 0; + +// if (rand() % 5 == 2) +// Debugprintf("Dropping %d", ARQ->TXSeq); +// else + + ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer; + + STREAM->BytesTXed += Origlen; + + UpdateStatsLine(TNC, STREAM); + + + // if waiting for ack, don't send, just queue. Will be sent when ack received + + if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA) + { + SendPacket(TNC, STREAM, TXBuffer, SendLen); + ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + } + else + STREAM->BytesResent -= Origlen; // So wont be included in resent bytes +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Reply[80]; + int SendLen; + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + struct ARQINFO * ARQ = STREAM->ARQInfo; + + SendLen = sprintf(Reply, "d%s:90", STREAM->MyCall); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_DISC; +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ +} + +static VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Used for Messages that need a reply. Save, send and set timeout + + memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null + ARQ->LastLen = MsgLen; + + // Delay the send for a shot while + +// SendPacket(sock, Msg, MsgLen, 0); + + ARQ->ARQTimer = 1; + ARQ->Retries = TNC->Retries + 1; // First timout is the real send + + return; +} + + + + +static VOID ARQTimer(struct TNCINFO * TNC) +{ + UINT * buffptr; + struct STREAMINFO * STREAM; + struct ARQINFO * ARQ; + int SendLen; + char Reply[80]; + int Stream; + + //Send frames, unless held by TurnroundTimer or Window + + int Outstanding; + + for (Stream = 0; Stream Streams[Stream]; + ARQ = STREAM->ARQInfo; + + // TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't + // need to check for busy (or anything else (I think!) + + if (ARQ->TXDelay) + { + ARQ->TXDelay--; + + if (ARQ->TXDelay) + continue; + + SendPacket(TNC, STREAM, ARQ->TXMsg, ARQ->TXLen); + } + + // if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end) + + if (ARQ->ARQTimerState == ARQ_WAITDATA) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding >= ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr, Stream); + } + + ARQ->ARQTimer--; + + if (ARQ->ARQTimer > 0) + continue; // Timer Still Running + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", STREAM->MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + + Debugprintf("Sending Poll After Data"); + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, STREAM, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(STREAM->xIDC_STATUS, "Wait ACK"); + + continue; + + } + + // TrunroundTimer is used to allow time for far end to revert to RX + + if (ARQ->TurnroundTimer) + ARQ->TurnroundTimer--; + + if (ARQ->TurnroundTimer == 0) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS + + if (Outstanding >= ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr, Stream); + } + } + + if (ARQ->ARQTimer) + { + // Only decrement if running send poll timer + +// if (ARQ->ARQTimerState != ARQ_WAITDATA) +// return; + + ARQ->ARQTimer--; + { + if (ARQ->ARQTimer) + continue; // Timer Still Running + } + + ARQ->Retries--; + + if (ARQ->Retries) + { + // Retry Current Message + + SendPacket(TNC, STREAM, ARQ->LastMsg, ARQ->LastLen); + ARQ->ARQTimer = TNC->Timeout + (rand() % 30); + + continue; + } + + // Retried out. + + switch (ARQ->ARQTimerState) + { + case ARQ_WAITDATA: + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", STREAM->MyCall); + + Debugprintf("Sending Poll After Timeout??"); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, STREAM, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(STREAM->xIDC_STATUS, "Wait ACK"); + + continue; + + case ARQ_CONNECTING: + + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "UIARQ} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + // Send Disc to TNC in case it got the Connects, but we missed the ACKs + + TidyClose(TNC, Stream); + ARQ->Retries = 2; // First timout is the real send, only send once + STREAM->Connecting = FALSE; // Back to Command Mode + ARQ->ARQState = FALSE; + + break; + + case ARQ_WAITACK: + case ARQ_CONNECTACK: + case ARQ_DISC: + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; + ARQ->ARQState = FALSE; + + while (STREAM->PACTORtoBPQ_Q) + ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q)); + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + + SetWindowText(STREAM->xIDC_MYCALL, ""); + SetWindowText(STREAM->xIDC_DESTCALL, ""); + SetWindowText(STREAM->xIDC_DIRN, ""); + SetWindowText(STREAM->xIDC_STATUS, ""); + + break; + + } + } + } +} + +static VOID ProcessARQStatus(struct TNCINFO * TNC, int Stream, struct ARQINFO * ARQ, char * Input) +{ + // Release any acked frames and resend any outstanding + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + int LastInSeq = Input[1] - 32; + int LastRXed = Input[2] - 32; + int FirstUnAcked = ARQ->TXLastACK; + int n = strlen(Input) - 3; + char * ptr; + int NexttoResend; + int First, Last, Outstanding; + UINT * Buffer; + int Acked = 0; + + // First status is an ack of Connect ACK + + if (ARQ->ARQTimerState == ARQ_CONNECTACK) + { + ARQ->Retries = 0; + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(STREAM->xIDC_STATUS, "Connected"); + } + + Debugprintf("Lsast In Seq, LastRXed %d %d", LastInSeq, LastRXed); + + // Release all up to LastInSeq + + while (FirstUnAcked != LastInSeq) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { + Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + ARQ->TXLastACK = FirstUnAcked; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (FirstUnAcked == ARQ->TXSeq) + { + UpdateStatsLine(TNC, STREAM); + ARQ->NoAckRetries = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(STREAM->xIDC_STATUS, "Connected"); + + return; // All Acked + } + + // Release any not in retry list up to LastRXed. + + ptr = &Input[3]; + + while (n) + { + NexttoResend = *(ptr++) - 32; + + FirstUnAcked++; + FirstUnAcked &= 63; + + while (FirstUnAcked != NexttoResend) + { + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { + Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + + FirstUnAcked++; + FirstUnAcked &= 63; + } + + // We don't ACK this one. Process any more resend values, then release up to LastRXed. + + n--; + } + + // Release rest up to LastRXed + + while (FirstUnAcked != LastRXed) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { + Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer[1]; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + // Resend anything in TX Buffer (From LastACK to TXSeq + + Last = ARQ->TXSeq + 1; + Last &= 63; + + First = LastInSeq; + + while (First != Last) + { + First++; + First &= 63; + + if(ARQ->TXHOLDQ[First]) + { + UINT * Buffer = ARQ->TXHOLDQ[First]; + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + + Debugprintf("Resend %d", First); + + STREAM->BytesResent += Buffer[1]; + + SendLen = sprintf(TXBuffer, "%c", First + 32); + + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer[2], Buffer[1]); + SendLen += Buffer[1]; + + TXBuffer[SendLen] = 0; + + SendPacket(TNC, STREAM, TXBuffer, SendLen); + + ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + + if (Acked == 0) + { + // Nothing acked by this statis message + + Acked = 1; // Dont count more thna once + ARQ->NoAckRetries++; + if (ARQ->NoAckRetries > TNC->Retries) + { + // Too many retries - just disconnect + + TidyClose(TNC, Stream); + return; + } + } + } + } + UpdateStatsLine(TNC, STREAM); +} + diff --git a/UIRoutines.c b/UIRoutines.c new file mode 100644 index 0000000..ebf56a8 --- /dev/null +++ b/UIRoutines.c @@ -0,0 +1,636 @@ +/* +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 +// +// UI Handling Routines + +#include "bpqmail.h" + + +char UIDEST[10] = "FBB"; +char UIMAIL[10] = "MAIL"; +char AXDEST[7]; +char AXMAIL[7]; +static char MAILMYCALL[7]; + +#pragma pack(1) + +UINT UIPortMask = 0; +BOOL UIEnabled[33]; +BOOL UIMF[33]; +BOOL UIHDDR[33]; +BOOL UINull[33]; +char * UIDigi[33]; +char * UIDigiAX[33]; // ax.25 version of digistring +int UIDigiLen[33]; // Length of AX string + + + +#pragma pack() + +PMESSAGEX DG_Q; // Queue of messages to be sent to node + +struct SEM DGSemaphore = {0, 0}; // For locking access to DG_Q; + +VOID UnQueueRaw(void * Param); + +static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); +DllExport char * APIENTRY GetApplName(int Appl); + +int APIENTRY SendRaw(int port, char * msg, int len); +int APIENTRY GetNumberofPorts(); + +VOID SetupUIInterface() +{ + int i, NumPorts = GetNumberofPorts(); +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; +#endif + + ConvToAX25(GetApplCall(BBSApplNum), MAILMYCALL); + ConvToAX25(UIDEST, AXDEST); + ConvToAX25(UIMAIL, AXMAIL); + + UIPortMask = 0; + + for (i = 1; i <= NumPorts; i++) + { + if (UIEnabled[i]) + { + char DigiString[100], * DigiLeft; + + UIPortMask |= 1 << (i-1); + UIDigiLen[i] = 0; + + if (UIDigi[i]) + { + UIDigiAX[i] = zalloc(100); + strcpy(DigiString, UIDigi[i]); + DigiLeft = strlop(DigiString,','); + + while(DigiString[0]) + { + ConvToAX25(DigiString, &UIDigiAX[i][UIDigiLen[i]]); + UIDigiLen[i] += 7; + + if (DigiLeft) + { + memmove(DigiString, DigiLeft, strlen(DigiLeft) + 1); + DigiLeft = strlop(DigiString,','); + } + else + DigiString[0] = 0; + } + } + } + } + + _beginthread(UnQueueRaw, 0, NULL); + + if (EnableUI) +#ifdef LINBPQ + SendLatestUI(0); +#else + __try + { + SendLatestUI(0); + } + My__except_Routine("SendLatestUI"); +#endif + +} + +VOID Free_UI() +{ + int i; + PMESSAGEX AXMSG; + + for (i = 1; i <= 32; i++) + { + if (UIDigi[i]) + { + free(UIDigi[i]); + UIDigi[i] = NULL; + } + + if (UIDigiAX[i]) + { + free(UIDigiAX[i]); + UIDigiAX[i] = NULL; + } + } + + if (DG_Q) + { + AXMSG = DG_Q; + DG_Q = AXMSG->CHAIN; + free(AXMSG); + } +} + +VOID QueueRaw(int Port, PMESSAGEX AXMSG, int Len) +{ + PMESSAGEX AXCopy = zalloc(400); + PMESSAGEX AXNext; + + AXMSG->PORT = Port; + AXMSG->LENGTH = Len; + AXMSG->CHAIN = 0; // Clear chain in new buffer + + memcpy(AXCopy, AXMSG, Len + 10); + + GetSemaphore(&DGSemaphore, 0); + + if (DG_Q == 0) // Empty + { + DG_Q = AXCopy; + FreeSemaphore(&DGSemaphore); + return; + } + + AXNext = DG_Q; + + while (AXNext->CHAIN) + AXNext = AXNext->CHAIN; // Chain to end of queue + + AXNext->CHAIN = AXCopy; // New one on end + + FreeSemaphore(&DGSemaphore); +} + +VOID SendMsgUI(struct MsgInfo * Msg) +{ + char msg[200]; + int len, i; + int Mask = UIPortMask; + int NumPorts = GetNumberofPorts(); + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + len = sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + + for (i=1; i <= NumPorts; i++) + { + if ((Mask & 1) && UIHDDR[i]) + Send_AX_Datagram(msg, len, i, AXDEST, TRUE); + + Mask>>=1; + } +} + +VOID SendHeaders(int Number, int Port) +{ + // Send headers in response to a resync request + + char msg[256]; + unsigned len=0; + struct tm *tm; + struct MsgInfo * Msg; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + while (Number <= LatestMsg) + { + Msg = FindMessageByNumber(Number); + + if (Msg) + { + if (len > (200 - strlen(Msg->title))) + { + Send_AX_Datagram(msg, len, Port, AXDEST, FALSE); + len=0; + } + + tm = gmtime((time_t *)&Msg->datecreated); + + len += sprintf(&msg[len], "%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + } + else + { + if (len > 230) + { + Send_AX_Datagram(msg, len, Port, AXDEST, FALSE); + len=0; + } + len += sprintf(&msg[len], "%-6d #\r", Number); + } + + Number++; + } + + Send_AX_Datagram(msg, len, Port, AXDEST, FALSE); + +} +VOID SendDummyUI(int num) +{ + char msg[100]; + int len, i; + int Mask = UIPortMask; + int NumPorts = GetNumberofPorts() +; + len = sprintf_s(msg, sizeof(msg),"%-6d #\r", num); + + for (i=1; i <= NumPorts; i++) + { + if (Mask & 1) + Send_AX_Datagram(msg, len, i, AXDEST, TRUE); + + Mask>>=1; + } +} + +VOID SendLatestUI(int Port) +{ + char msg[20]; + int len, i; + int Mask = UIPortMask; + int NumPorts = GetNumberofPorts(); + + len = sprintf_s(msg, sizeof(msg),"%-6d !!\r", LatestMsg); + + if (Port) + { + Send_AX_Datagram(msg, len, Port, AXDEST, FALSE); + return; + } + + for (i=1; i <= NumPorts; i++) + { + if ((Mask & 1) && UIHDDR[i]) + Send_AX_Datagram(msg, len, i, AXDEST, TRUE); + + Mask>>=1; + } +} + +static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue) +{ + MESSAGEX AXMSG; + + PMESSAGEX AXPTR = &AXMSG; + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, HWADDR, 7); + memcpy(AXPTR->ORIGIN, MAILMYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + if (UIDigi[Port] && UIDigiAX[Port]) + { + // This port has a digi string + + int DigiLen = UIDigiLen[Port]; + UCHAR * ptr; + + memcpy(&AXPTR->CTL, UIDigiAX[Port], DigiLen); + + ptr = (UCHAR *)AXPTR; + ptr += DigiLen; + AXPTR = (PMESSAGEX)ptr; + + Len += DigiLen; + + } + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->DATA, Msg, Len); + + if (Queue) + QueueRaw(Port, &AXMSG, Len + 16); + else + SendRaw(Port, (char *)&AXMSG.DEST, Len + 16); + + return; + +} + +VOID UnQueueRaw(void * Param) +{ + PMESSAGEX AXMSG; + + while (TRUE) + { + GetSemaphore(&DGSemaphore, 0); + + if (DG_Q) + { + AXMSG = DG_Q; + DG_Q = AXMSG->CHAIN; + + SendRaw(AXMSG->PORT, (char *)&AXMSG->DEST, AXMSG->LENGTH); + free(AXMSG); + } + + FreeSemaphore(&DGSemaphore); + + Sleep(5000); + } +} + +VOID ProcessUItoMe(char * msg, int len) +{ + msg[len] = 0; + return; +} + +VOID ProcessUItoFBB(char * msg, int len, int Port) +{ + // ? 0000006464 + // The first 8 digits are the hexadecimal number of the requested start of the list + // (here 00002EE0 -> 12000) and the last two digits are the sum of the four bytes anded with FF (0E). + + int Number, Sum, Sent = 0; + char cksum[3]; + + if (msg[0] == '?') + { + memcpy(cksum, &msg[10], 2); + msg[10]=0; + + sscanf(&msg[1], "%X", &Number); + sscanf(cksum, "%X", &Sum); + + if (Number >= LatestMsg) + { + SendLatestUI(Port); + return; + } + + SendHeaders(Number+1, Port); + } + + return; +} + +UCHAR * AdjustForDigis(PMESSAGEX * buff, int * len) +{ + PMESSAGEX buff1 = *(buff); + UCHAR * ptr, * ptr1; + + if ((buff1->ORIGIN[6] & 1) == 1) + { + // End of Call Set + + return 0; // No Digis + } + + ptr1 = &buff1->ORIGIN[6]; // End of add + ptr = (UCHAR *)*buff; + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1 += 7; + ptr+= 7; + } + + *buff = (PMESSAGEX)ptr; + return (&buff1->CTL); // Start of Digi String +} +VOID SeeifBBSUIFrame(PMESSAGEX buff, int len) +{ + UCHAR * Digis; + UCHAR From[7], To[7]; + int Port = buff->PORT; + + if (Port > 128) + return; // Only look at received frames + + memcpy(From, buff->ORIGIN, 7); // Save Origin and Dest before adjucting for Digis + memcpy(To, buff->DEST, 7); + + Digis = AdjustForDigis(&buff, &len); + + if (Digis) + { + // Make sure all are actioned + + DigiLoop: + + if ((Digis[6] & 0x80) == 0) + return; // Not repeated + + if ((Digis[6] & 1) == 0) // Not end of list + { + Digis +=7; + goto DigiLoop; + } + } + + if (buff->CTL != 3) + return; + + if (buff->PID != 0xf0) + return; + +// if (memcmp(buff->ORIGIN, MAILMYCALL,6) == 0) // From me? +// if (buff->ORIGIN[6] == (MAILMYCALL[6] | 1)) // Set End of Call +// return; + + From[6] &= 0x7e; + To[6] &= 0x7e; + + if (memcmp(To, MAILMYCALL, 7) == 0) + { + ProcessUItoFBB(buff->DATA, len-23, Port); + return; + } + + if (memcmp(To, AXDEST, 7) == 0) + { + ProcessUItoFBB(buff->DATA, len-23, Port); + return; + } + + len++; + + return; +} + +// ConvToAX25(MYNODECALL, MAILMYCALL); +// len=ConvFromAX25(Routes->NEIGHBOUR_DIGI1,Portcall); +// Portcall[len]=0; + +char MailForHeader[] = "Mail For:"; + +char MailForExpanded[100]; + +VOID ExpandMailFor() +{ + char * OldP = MailForText; + char * NewP = MailForExpanded; + char * ptr, * pptr; + size_t len; + char Dollar[] = "\\"; + char CR[] = "\r"; + + ptr = strchr(OldP, '\\'); + + while (ptr) + { + len = ptr - OldP; // Chars before Backslash + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'r': // Inserts a carriage return. + case 'R': // Inserts a carriage return. + + pptr = CR; + break; + + default: + + pptr = Dollar; // Just Copy Backslash + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '\\'); + } + + strcpy(NewP, OldP); +} + + +VOID SendMailFor(char * Msg, BOOL HaveCalls) +{ + int Mask = UIPortMask; + int NumPorts = GetNumberofPorts(); + int i; + + if (!HaveCalls) + strcat(Msg, "None "); + + Sleep(1000); + + for (i=1; i <= NumPorts; i++) + { + if (Mask & 1) + { + if (UIMF[i] && (HaveCalls || UINull[i])) + { + Send_AX_Datagram(Msg, (int)strlen(Msg) - 1, i, AXMAIL, TRUE); + } + } + Mask>>=1; + } +} + +VOID SendMailForThread(VOID * Param) +{ + struct UserInfo * user; + char MailForMessage[256] = ""; + BOOL HaveMailFor; + struct UserInfo * ptr = NULL; + int i, Unread; + + while (MailForInterval) + { + ExpandMailFor(); + + if (MailForText[0]) // User supplied header + strcpy(MailForMessage, MailForExpanded); + else + strcpy(MailForMessage, MailForHeader); + + HaveMailFor = FALSE; + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + CountMessagesTo(user, &Unread); + + if (Unread) + { + if (strlen(MailForMessage) > 240) + { + SendMailFor(MailForMessage, TRUE); + + if (MailForText[0]) // User supplied header + strcpy(MailForMessage, MailForExpanded); + else + strcpy(MailForMessage, MailForHeader); + } + strcat(MailForMessage, user->Call); + strcat(MailForMessage, " "); + HaveMailFor = TRUE; + } + } + + SendMailFor(MailForMessage, HaveMailFor); + Sleep(MailForInterval * 60000); + } +} + + + + + + + + +/* +20:09:00R GM8BPQ-10>FBB Port=1 : +103 !! +20:10:06R GM8BPQ-10>FBB Port=1 : +19-Jul 21:08 <<< Mailbox GM8BPQ Skigersta >>> 2 active messages. +Messages for + ALL + +20:11:11R GM8BPQ-10>FBB Port=1 : +104 P 5 G8BPQ GM8BPQ 090719 *** +20:12:17R GM8BPQ-10>FBB Port=1 : +105 B 5 ALL GM8BPQ 090719 test + +12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + + +20:13:23R GM8BPQ-10>FBB Port=1 : +? 0000006464 + +20:15:34R GM8BPQ-10>FBB Port=1 : +105 !! +20:15:45T GM8BPQ-10>MAIL Port=2 : + +20:16:40R GM8BPQ-10>FBB Port=1 : +19-Jul 21:15 <<< Mailbox GM8BPQ Skigersta >>> 4 active messages. +Messages for + ALL G8BPQ +20:17:46R GM8BPQ-10>FBB Port=1 : +106 P 5 GM8BPQ GM8BPQ 090719 *** +20:20:54R GM8BPQ-10>FBB Port=1 : +? 0000006464 +20:21:05T GM8BPQ-10>FBB Port=2 : +? 0000006464 +*/ diff --git a/UZ7HODrv.c b/UZ7HODrv.c new file mode 100644 index 0000000..53d8e4b --- /dev/null +++ b/UZ7HODrv.c @@ -0,0 +1,3011 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use UZ7HOPE as a Port Driver +// +// Uses BPQ EXTERNAL interface +// + +// Interlock and scanning with UZ7HO driver. + +// A UZ7HO port can be used in much the same way as any other HF port, so that it only allows one connect at +// a time and takes part in Interlock and Scan Control proessing. But it can also be used as a multisession +// driver so it does need special treatment. + + +#define _CRT_SECURE_NO_DEPRECATE + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +extern char * PortConfig[33]; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +void ConnecttoUZ7HOThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +int ConnecttoUZ7HO(); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +VOID ProcessAGWPacket(struct TNCINFO * TNC, UCHAR * Message); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendData(int Stream, struct TNCINFO * TNC, char * key, char * Msg, int MsgLen); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +int standardParams(struct TNCINFO * TNC, char * buf); + +extern UCHAR BPQDirectory[]; + +#define MAXBPQPORTS 32 +#define MAXUZ7HOPORTS 16 + +static char ClassName[]="ARDOPSTATUS"; +static char WindowTitle[] = "UZ7HO"; +static int RigControlRow = 165; + + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int UZ7HOChannel[MAXBPQPORTS+1]; // BPQ Port to UZ7HO Port +static int BPQPort[MAXUZ7HOPORTS][MAXBPQPORTS+1]; // UZ7HO Port and Connection to BPQ Port +static void * UZ7HOtoBPQ_Q[MAXBPQPORTS+1]; // Frames for BPQ, indexed by BPQ Port +static void * BPQtoUZ7HO_Q[MAXBPQPORTS+1]; // Frames for UZ7HO. indexed by UZ7HO port. Only used it TCP session is blocked + +static int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific UZ7HO host +static struct TNCINFO * SlaveTNC[MAXBPQPORTS+1];// TNC Record Slave if present + +// Each port may be on a different machine. We only open one connection to each UZ7HO instance + +static char * UZ7HOSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int UZ7HOInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static struct sockaddr_in sinx; +static struct sockaddr_in rxaddr; +static struct sockaddr_in destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short UZ7HOPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +unsigned int reverse(unsigned int val) +{ + char x[4]; + char y[4]; + + memcpy(x, &val,4); + y[0] = x[3]; + y[1] = x[2]; + y[2] = x[1]; + y[3] = x[0]; + + memcpy(&val, y, 4); + + return val; +} + + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + char FN[MAX_PATH] = ""; + HANDLE hProc; + + if (TNC->ProgramPath == NULL) + return FALSE; + + GetWindowText(hwnd,wtext,99); + + if (strstr(wtext,"SoundModem")) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessId); + + if (hProc && GetModuleFileNameExPtr) + { + GetModuleFileNameExPtr(hProc, NULL, FN, MAX_PATH); + + // Make sure this is the right copy + + CloseHandle(hProc); + + if (_stricmp(FN, TNC->ProgramPath)) + return TRUE; //Wrong Copy + } + + TNC->PID = ProcessId; + + sprintf (wtext, "Soundmodem - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + + + +void RegisterAPPLCalls(struct TNCINFO * TNC, BOOL Unregister) +{ + // Register/Deregister Nodecall and all applcalls + + struct AGWINFO * AGW; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + char * ptr; + + char NodeCall[11]; + + memcpy(NodeCall, MYNODECALL, 10); + strlop(NodeCall, ' '); + + AGW = TNC->AGWInfo; + + + AGW->TXHeader.Port=0; + AGW->TXHeader.DataLength=0; + + if (Unregister) + AGW->TXHeader.DataKind = 'x'; // UnRegister + else + AGW->TXHeader.DataKind = 'X'; // Register + + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, TNC->NodeCall); + send(TNC->TCPSock,(const char FAR *)&AGW->TXHeader,AGWHDDRLEN,0); + + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, NodeCall); + send(TNC->TCPSock,(const char FAR *)&AGW->TXHeader,AGWHDDRLEN,0); + + // Add Alias + + memcpy(NodeCall, MYALIASTEXT, 10); + strlop(NodeCall, ' '); + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, NodeCall); + send(TNC->TCPSock,(const char FAR *)&AGW->TXHeader,AGWHDDRLEN,0); + + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (Appl[0]) + { + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, Appl); + send(TNC->TCPSock,(const char FAR *)&AGW->TXHeader,AGWHDDRLEN,0); + } + } +} + + +VOID UZ7HOSuspendPort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortStopped = TRUE; + RegisterAPPLCalls(TNC, TRUE); +} + +VOID UZ7HOReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortStopped = FALSE; + RegisterAPPLCalls(TNC, FALSE); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes,txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct AGWINFO * AGW; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + AGW = TNC->AGWInfo; + + // Look for attach on any call + + for (Stream = 0; Stream <= TNC->AGWInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER[6] |= 0x60; // Ensure P or T aren't used on ax.25 + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command(-1, Cmd); + + SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop + + } + } + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + // Only on first port using a host + + time(<ime); + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + if (ltime - lasttime[port] > 9) + { + ConnecttoUZ7HO(port); + lasttime[port] = ltime; + } + } + else + { + // See if time to refresh registrations + + if (TNC->CONNECTED) + { + if (ltime - AGW->LastParamTime > 60) + { + AGW->LastParamTime = ltime; + + if (TNC->PortRecord->PORTCONTROL.PortStopped == FALSE) + RegisterAPPLCalls(TNC, FALSE); + } + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPSock, &readfs); + + + FD_ZERO(&writefs); + + if (TNC->CONNECTING) FD_SET(TNC->TCPSock, &writefs); // Need notification of Connect + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock, &writefs); // Need notification of busy clearing + + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPSock, &errorfs); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if (select((int)TNC->TCPSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock, &writefs)) + { + if (BPQtoUZ7HO_Q[port] == 0) + { + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + char * ptr; + + char NodeCall[11]; + + memcpy(NodeCall, MYNODECALL, 10); + strlop(NodeCall, ' '); + + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // If required, send signon + + if (UZ7HOSignon[port]) + send(TNC->TCPSock, UZ7HOSignon[port], 546, 0); + + // Request Raw Frames + + AGW->TXHeader.Port = 0; + AGW->TXHeader.DataKind = 'k'; // Raw Frames + AGW->TXHeader.DataLength = 0; + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + + AGW->TXHeader.DataKind = 'm'; // Monitor Frames + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + + AGW->TXHeader.DataKind = 'R'; // Version + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + + AGW->TXHeader.DataKind = 'g'; // Port Capabilities + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + + // Register all applcalls + + AGW->TXHeader.DataKind = 'X'; // Register + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, TNC->NodeCall); + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, NodeCall); + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + + for (App = 0; App < 32; App++) + { + APPL = &APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr = strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (Appl[0]) + { + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, Appl); + send(TNC->TCPSock, (const char FAR *)&AGW->TXHeader, AGWHDDRLEN, 0); + } + } +#ifndef LINBPQ + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + else + { + // Write block has cleared. Send rest of packet + + buffptr = Q_REM(&BPQtoUZ7HO_Q[port]); + + txlen = (int)buffptr->Len; + + memcpy(txbuff, buffptr->Data, txlen); + + bytes = send(TNC->TCPSock, (const char FAR *)&txbuff, txlen, 0); + + ReleaseBuffer(buffptr); + + } + + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { + + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "UZ7HO Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port] = FALSE; + CONNECTED[port] = FALSE; + + } + + } + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= TNC->AGWInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // Have to time out connects, as TNC doesn't report failure + + if (STREAM->Connecting) + { + STREAM->Connecting--; + + if (STREAM->Connecting == 0) + { + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "UZ7HO} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->DiscWhenAllSent = 10; + + // Send Disc to TNC + + TidyClose(TNC, Stream); + } + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->Connected && STREAM->FramesOutstanding) + { + struct AGWINFO * AGW = TNC->AGWInfo; + + AGW->PollDelay++; + + if (AGW->PollDelay > 10) + { + char * Key = &STREAM->AGWKey[0]; + + AGW->PollDelay = 0; + + AGW->TXHeader.Port = Key[0] - '1'; + AGW->TXHeader.DataKind = 'Y'; + strcpy(AGW->TXHeader.callfrom, &Key[11]); + strcpy(AGW->TXHeader.callto, &Key[1]); + AGW->TXHeader.DataLength = 0; + + send(TNCInfo[MasterPort[port]]->TCPSock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + } + } + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct AGWINFO * AGW = TNC->AGWInfo; + + int MsgLen; + struct _MESSAGE * buffptr; + char * Buffer; + SOCKET Sock; + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + if (TNC->PortRecord->PORTCONTROL.PortStopped == TRUE) // Interlock Disabled Port + { + ReleaseBuffer((UINT *)buffptr); + return (0); + } + + Sock = TNCInfo[MasterPort[port]]->TCPSock; + + MsgLen = buffptr->LENGTH - 6; // 7 Header, need extra Null + buffptr->LENGTH = 0; // Need a NULL on front + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer--; // Need to send an extra byte on front + + AGW->TXHeader.Port = UZ7HOChannel[port]; + AGW->TXHeader.DataKind = 'K'; + memset(AGW->TXHeader.callfrom, 0, 10); + memset(AGW->TXHeader.callto, 0, 10); +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(MsgLen); +#else + AGW->TXHeader.DataLength = MsgLen; +#endif + send(Sock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + send(Sock, Buffer, MsgLen, 0); + + ReleaseBuffer((UINT *)buffptr); + } + + + return (0); + + + + case 2: // send + + if (TNC->PortRecord->PORTCONTROL.PortStopped == TRUE) // Interlock Disabled Port + return 0; + + if (!TNCInfo[MasterPort[port]]->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + AGW = TNC->AGWInfo; + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + if (STREAM->Connected) + { + SendData(Stream, TNC, &STREAM->AGWKey[0], &buff->L2DATA[0], txlen); + STREAM->FramesOutstanding++; + } + else + { + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + TidyClose(TNC, buff->PORT); + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (STREAM->Connecting) + return 0; + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + int s = 0; + + while (s <= TNC->AGWInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "VERSION", 7) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Version %d.%d.%d.%d\r", + AGW->Version[0], AGW->Version[1], AGW->Version[2], AGW->Version[3]); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "FREQ", 4) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + // May be read or set frequency + + if (txlen == 5) + { + // Read Freq + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Freqency %d\r", AGW->CenterFreq); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + AGW->CenterFreq = atoi(&buff->L2DATA[5]); + + if (AGW->CenterFreq == 0) + { + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Invalid Modem Freqency\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (TNCInfo[MasterPort[port]]->AGWInfo->isQTSM == 3) + { + // QtSM so can send Set Freq Command + + char Buffer[32] = ""; + int MsgLen = 32; + + memcpy(Buffer, &AGW->CenterFreq, 4); + + AGW->TXHeader.Port = UZ7HOChannel[port]; + AGW->TXHeader.DataKind = 'g'; + memset(AGW->TXHeader.callfrom, 0, 10); + memset(AGW->TXHeader.callto, 0, 10); +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(MsgLen); +#else + AGW->TXHeader.DataLength = MsgLen; +#endif + send(TNCInfo[MasterPort[port]]->TCPSock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + send(TNCInfo[MasterPort[port]]->TCPSock, Buffer, MsgLen, 0); + } +#ifdef WIN32 + else if (AGW->hFreq) + { + //Using real UZ7HO on Windows + + char Freq[16]; + sprintf(Freq, "%d", AGW->CenterFreq - 1); + + SendMessage(AGW->hFreq, WM_SETTEXT, 0, (LPARAM)Freq); + SendMessage(AGW->hSpin, WM_LBUTTONDOWN, 1, 1); + SendMessage(AGW->hSpin, WM_LBUTTONUP, 0, 1); + } +#endif + else + { + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Sorry Setting UZ7HO params not supported on this system\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + return 1; + } + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Freq Set Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MODEM", 5) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (txlen == 6) + { + // Read Modem + + if (buffptr) + { + if (AGW->ModemName[0]) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem %s\r", AGW->ModemName); + else + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Number %d\r", AGW->Modem); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + else if (TNCInfo[MasterPort[port]]->AGWInfo->isQTSM == 3) + { + // Can send modem name to QTSM + + char Buffer[32] = ""; + int MsgLen = 32; + + strlop(buff->L2DATA, '\r'); + strlop(buff->L2DATA, '\n'); + + if (strlen(&buff->L2DATA[6]) > 20) + buff->L2DATA[26] = 0; + + strcpy(&Buffer[4], &buff->L2DATA[6]); + + AGW->TXHeader.Port = UZ7HOChannel[port]; + AGW->TXHeader.DataKind = 'g'; + memset(AGW->TXHeader.callfrom, 0, 10); + memset(AGW->TXHeader.callto, 0, 10); +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(MsgLen); +#else + AGW->TXHeader.DataLength = MsgLen; +#endif + send(TNCInfo[MasterPort[port]]->TCPSock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + send(TNCInfo[MasterPort[port]]->TCPSock, Buffer, MsgLen, 0); + } +#ifdef WIN32 + else if (AGW->cbinfo.cbSize) + { + // Real QTSM on Windows + + AGW->Modem = atoi(&buff->L2DATA[6]); + + if (AGW->cbinfo.cbSize && AGW->cbinfo.hwndCombo) + { + // Set it + + LRESULT ret = SendMessage(AGW->cbinfo.hwndCombo, CB_SETCURSEL, AGW->Modem, 0); + int pos = 13 * AGW->Modem + 7; + + ret = SendMessage(AGW->cbinfo.hwndCombo, WM_LBUTTONDOWN, 1, 1); + ret = SendMessage(AGW->cbinfo.hwndCombo, WM_LBUTTONUP, 0, 1); + ret = SendMessage(AGW->cbinfo.hwndList, WM_LBUTTONDOWN, 1, pos << 16); + ret = SendMessage(AGW->cbinfo.hwndList, WM_LBUTTONUP, 0, pos << 16); + ret = 0; + } + } +#endif + else + { + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Sorry Setting UZ7HO params not supported this system\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "UZ7HO} Modem Set Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + struct AGWINFO * AGW = TNC->AGWInfo; + char ViaList[82] = ""; + int Digis = 0; + char * viaptr; + char * ptr; + char * context; + int S; + struct STREAMINFO * TSTREAM; + char Key[21]; + int sent = 0; + + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + memset(STREAM->RemoteCall, 0, 10); + + // See if any digis - accept V VIA or nothing, seps space or comma + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + + if (*ptr == '!') + ptr++; + + strcpy(STREAM->RemoteCall, ptr); + + Key[0] = UZ7HOChannel[port] + '1'; + memset(&Key[1], 0, 20); + strcpy(&Key[11], STREAM->MyCall); + strcpy(&Key[1], ptr); + + // Make sure we don't already have a session for this station + + S = 0; + + while (S <= AGW->MaxSessions) + { + TSTREAM = &TNC->Streams[S]; + + if (memcmp(TSTREAM->AGWKey, Key, 21) == 0) + { + // Found it; + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], + "UZ7HO} Sorry - Session between %s and %s already Exists\r", STREAM->MyCall, STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + STREAM->DiscWhenAllSent = 10; + + return 0; + } + S++; + } + + // Not Found + + memcpy(&STREAM->AGWKey[0], &Key[0], 21); + + AGW->TXHeader.Port = UZ7HOChannel[port]; + AGW->TXHeader.DataKind = 'C'; + memcpy(AGW->TXHeader.callfrom, &STREAM->AGWKey[11], 10); + memcpy(AGW->TXHeader.callto, &STREAM->AGWKey[1], 10); + AGW->TXHeader.DataLength = 0; + + ptr = strtok_s(NULL, " ,\r", &context); + + if (ptr) + { + // we have digis + + viaptr = &ViaList[1]; + + if (strcmp(ptr, "V") == 0 || strcmp(ptr, "VIA") == 0) + ptr = strtok_s(NULL, " ,\r", &context); + + while (ptr) + { + strcpy(viaptr, ptr); + Digis++; + viaptr += 10; + ptr = strtok_s(NULL, " ,\r", &context); + } + +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(Digis * 10 + 1); +#else + AGW->TXHeader.DataLength = Digis * 10 + 1; +#endif + + AGW->TXHeader.DataKind = 'v'; + ViaList[0] = Digis; + } + + sent = send(TNCInfo[MasterPort[port]]->TCPSock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + if (Digis) + send(TNCInfo[MasterPort[port]]->TCPSock, ViaList, Digis * 10 + 1, 0); + + STREAM->Connecting = TNC->AGWInfo->ConnTimeOut; // It doesn't report failure + +// sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); +// SetDlgItemText(TNC->hDlg, IDC_TNCSTATE, Status); + } + } + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNCInfo[MasterPort[port]]->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesOutstanding > 8) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + break; + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Disconnected from TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +extern char sliderBit[]; +extern char WebProcTemplate[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ +/* + int Len = sprintf(Buff, "" + "" + "UZ7HO Status" + "

UZ7HO Status" + "

", + TNC->Port); +*/ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "UZ7HO Status", "UZ7HO 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_MODE); + Len += sprintf(&Buff[Len], "
Comms State%s
Modem%s
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + + + +void * UZ7HOExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // Will be called once for each UZ7HO port to be mapped to a BPQ Port + // The UZ7HO port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + TNC->SuspendPortProc = UZ7HOSuspendPort; + TNC->ReleasePortProc = UZ7HOReleasePort; + + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.UICAPABLE = 1; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->Hardware = H_UZ7HO; + + UZ7HOChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = TNC->AGWInfo->MaxSessions; + + i=sprintf(Msg,"UZ7HO Host %s Port %d Chan %c\n", + TNC->HostName, TNC->TCPPort, PortEntry->PORTCONTROL.CHANNELNUM); + WritetoConsole(Msg); + + // See if we already have a port for this host + + MasterPort[port] = port; + + for (i = 1; i < port; i++) + { + if (i == port) continue; + + if (TNCInfo[i] && TNCInfo[i]->TCPPort == TNC->TCPPort && + _stricmp(TNCInfo[i]->HostName, TNC->HostName) == 0) + { + MasterPort[port] = i; + SlaveTNC[i] = TNC; + break; + } + } + + BPQPort[PortEntry->PORTCONTROL.CHANNELNUM-65][MasterPort[port]] = port; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_LEVELS = zalloc(32); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Modem", WS_CHILD | WS_VISIBLE, 10,50,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,50,520,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(); + + MoveWindows(TNC); + + if (MasterPort[port] == port) + { + // First port for this TNC - start TNC if sonfigured and connect + +#ifndef LINBPQ + if (EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC)) + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); +#else + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); +#endif + ConnecttoUZ7HO(port); + } + else + { + // Slave Port + + sprintf(TNC->WEB_COMMSSTATE, "Slave to Port %d", MasterPort[port] ); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + + time(&lasttime[port]); // Get initial time value + +#endif + return ExtProc; +} + +/* + +# Config file for BPQtoUZ7HO +# +# For each UZ7HO port defined in BPQCFG.TXT, Add a line here +# Format is BPQ Port, Host/IP Address, Port + +# +# Any unspecified Ports will use 127.0.0.1 and port for BPQCFG.TXT IOADDR field +# + +1 127.0.0.1 8000 +2 127.0.0.1 8001 + +*/ + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + struct AGWINFO * AGW; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + AGW = TNC->AGWInfo = zalloc(sizeof(struct AGWINFO)); // AGW Sream Mode Specific Data + + AGW->MaxSessions = 10; + AGW->ConnTimeOut = CONTIMEOUT; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + if (TNC->TCPPort == 0) + TNC->TCPPort = 8000; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort); + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + + if (ptr &&_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, "MAXSESSIONS", 11) == 0) + { + AGW->MaxSessions = atoi(&buf[12]); + if (AGW->MaxSessions > 26 ) AGW->MaxSessions = 26; + } + if (_memicmp(buf, "CONTIMEOUT", 10) == 0) + AGW->ConnTimeOut = atoi(&buf[11]) * 10; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "BEACONAFTERSESSION", 18) == 0) // Send Beacon after each session + TNC->RPBEACON = TRUE; + else + if (_memicmp(buf, "WINDOW", 6) == 0) + TNC->Window = atoi(&buf[7]); + else + if (_memicmp(buf, "DEFAULTMODEM", 12) == 0) + TNC->AGWInfo->Modem = atoi(&buf[13]); + else + if (_memicmp(buf, "MODEMCENTER", 11) == 0 || _memicmp(buf, "MODEMCENTRE", 11) == 0) + TNC->AGWInfo->CenterFreq = atoi(&buf[12]); + else + if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + + + return (TRUE); +} + +#ifndef LINBPQ + +typedef struct hINFO +{ + HWND Freq1; + HWND Freq2; + HWND Spin1; + HWND Spin2; + COMBOBOXINFO cinfo1; + COMBOBOXINFO cinfo2; +}; + + +BOOL CALLBACK EnumChildProc(HWND handle, LPARAM lParam) +{ + char classname[100]; + struct hINFO * hInfo = (struct hINFO *)lParam; + + // We collect the handles for the two modem and freq boxs here and set into correct TNC record later + + GetClassName(handle, classname, 99); + + if (strcmp(classname, "TComboBox") == 0) + { + // Get the Combo Box Info + + if (hInfo->cinfo1.cbSize == 0) + { + hInfo->cinfo1.cbSize = sizeof(COMBOBOXINFO); + GetComboBoxInfo(handle, &hInfo->cinfo1); + } + else + { + hInfo->cinfo2.cbSize = sizeof(COMBOBOXINFO); + GetComboBoxInfo(handle, &hInfo->cinfo2); + } + } + + if (strcmp(classname, "TSpinEdit") == 0) + { + if (hInfo->Freq1 == 0) + hInfo->Freq1 = handle; + else + hInfo->Freq2 = handle; + } + + if (strcmp(classname, "TSpinButton") == 0) + { + if (hInfo->Spin1 == 0) + hInfo->Spin1 = handle; + else + hInfo->Spin2 = handle; + } + return TRUE; +} + +BOOL CALLBACK uz_enum_windows_callback(HWND handle, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + struct AGWINFO * AGW = TNC->AGWInfo; + char Freq[16]; + + UINT ProcessId; + + GetWindowText(handle, wtext, 99); + + GetWindowThreadProcessId(handle, &ProcessId); + + if (TNC->PID == ProcessId) + { + if (strstr(wtext,"SoundModem ")) + { + // Our Process + + // Enumerate Child Windows + + struct hINFO hInfo; + + memset(&hInfo, 0, sizeof(hInfo)); + + EnumChildWindows(handle, EnumChildProc, (LPARAM)&hInfo); + + // Set handles + + if (TNC->PortRecord->PORTCONTROL.CHANNELNUM == 'A') + { + AGW->hFreq = hInfo.Freq1; + AGW->hSpin = hInfo.Spin1; + memcpy(&AGW->cbinfo, &hInfo.cinfo1, sizeof(COMBOBOXINFO)); + } + else + { + AGW->hFreq = hInfo.Freq2; + AGW->hSpin = hInfo.Spin2; + memcpy(&AGW->cbinfo, &hInfo.cinfo2, sizeof(COMBOBOXINFO)); + } + + if (AGW->CenterFreq && AGW->hFreq) + { + // Set it + + sprintf(Freq, "%d", AGW->CenterFreq - 1); + + SendMessage(AGW->hFreq, WM_SETTEXT, 0, (LPARAM)Freq); + SendMessage(AGW->hSpin, WM_LBUTTONDOWN, 0, 1); + SendMessage(AGW->hSpin, WM_LBUTTONUP, 0, 1); + } + + // Set slave port + + TNC = SlaveTNC[TNC->Port]; + + if (TNC) + { + AGW = TNC->AGWInfo; + + if (TNC->PortRecord->PORTCONTROL.CHANNELNUM == 'A') + { + AGW->hFreq = hInfo.Freq1; + AGW->hSpin = hInfo.Spin1; + memcpy(&AGW->cbinfo, &hInfo.cinfo1, sizeof(COMBOBOXINFO)); + } + else + { + AGW->hFreq = hInfo.Freq2; + AGW->hSpin = hInfo.Spin2; + memcpy(&AGW->cbinfo, &hInfo.cinfo2, sizeof(COMBOBOXINFO)); + } + + if (AGW->CenterFreq && AGW->hFreq) + { + // Set it + + sprintf(Freq, "%d", AGW->CenterFreq - 1); + + SendMessage(AGW->hFreq, WM_SETTEXT, 0, (LPARAM)Freq); + SendMessage(AGW->hSpin, WM_LBUTTONDOWN, 0, 1); + SendMessage(AGW->hSpin, WM_LBUTTONUP, 0, 1); + } + + } + + return FALSE; + } + } + + return (TRUE); +} + +#endif + +int ConnecttoUZ7HO(int port) +{ + _beginthread(ConnecttoUZ7HOThread, 0, (void *)(size_t)port); + return 0; +} + +VOID ConnecttoUZ7HOThread(void * portptr) +{ + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + struct AGWINFO * AGW = TNC->AGWInfo; + + Sleep(3000); // Allow init to complete + + TNC->AGWInfo->isQTSM = 0; + +#ifndef LINBPQ + + AGW->hFreq = AGW->hSpin = 0; + AGW->cbinfo.cbSize = 0; + + 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) + return; + +// goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + // Get Window Handles so we can change centre freq and modem + + EnumWindows(uz_enum_windows_callback, (LPARAM)TNC); + } +#endif + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + { + Debugprintf("UZ7HO Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for UZ7HO socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(struct sockaddr *) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connect successful + // + + TNC->CONNECTED = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + ioctl(TNC->TCPSock, FIONBIO, ¶m); + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for UZ7HO socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + RegisterAPPLCalls(TNC, FALSE); // Register Calls + + return; + +} + +static int ProcessReceivedData(int port) +{ + unsigned int bytes; + int datalen,i; + char ErrMsg[255]; + char Message[1000]; + struct TNCINFO * TNC = TNCInfo[port]; + struct AGWINFO * AGW = TNC->AGWInfo; + struct TNCINFO * SaveTNC; + + // Need to extract messages from byte stream + + // Use MSG_PEEK to ensure whole message is available + + bytes = recv(TNC->TCPSock, (char *) &AGW->RXHeader, AGWHDDRLEN, MSG_PEEK); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for UZ7HO socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Disconnected from TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "UZ7HO Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Disconnected from TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return (0); + } + + // Have some data + + if (bytes == AGWHDDRLEN) + { + // Have a header - see if we have any associated data + + datalen = AGW->RXHeader.DataLength; + +#ifdef __BIG_ENDIAN__ + datalen = reverse(datalen); +#endif + if (datalen < 0 || datalen > 500) + { + // corrupt - reset connection + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Disconnected from TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return 0; + } + + if (datalen > 0) + { + // Need data - See if enough there + + bytes = recv(TNC->TCPSock, (char *)&Message, AGWHDDRLEN + datalen, MSG_PEEK); + } + + if (bytes == AGWHDDRLEN + datalen) + { + bytes = recv(TNC->TCPSock, (char *)&AGW->RXHeader, AGWHDDRLEN,0); + + if (datalen > 0) + { + bytes = recv(TNC->TCPSock,(char *)&Message, datalen,0); + } + + // Have header, and data if needed + + SaveTNC = TNC; + ProcessAGWPacket(TNC, Message); // Data may be for another port + TNC = SaveTNC; + + return (0); + } + + // Have header, but not sufficient data + + return (0); + + } + + // Dont have at least header bytes + + return (0); + +} +/* +VOID ConnecttoMODEMThread(port); + +int ConnecttoMODEM(int port) +{ + _beginthread(ConnecttoMODEMThread,0,port); + + return 0; +} + +VOID ConnecttoMODEMThread(port) +{ + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname(TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET || TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for UZ7HO socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + setsockopt (TNC->TCPDataSock, 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 UZ7HO socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + + TNC->CONNECTED=TRUE; + SetDlgItemText(TNC->hDlg, IDC_COMMSSTATE, "Connected to UZ7HO TNC"); + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for UZ7HO socket - error code = %d\n", err); + WritetoConsole(Msg); + SetDlgItemText(TNC->hDlg, IDC_COMMSSTATE, "Connection to TNC failed"); + + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + + return; +} +*/ +/* +UZ7HO C GM8BPQ GM8BPQ-2 *** CONNECTED To Station GM8BPQ-0 + +UZ7HO D GM8BPQ GM8BPQ-2 asasasas +M8BPQ +New Disconnect Port 7 Q 0 +UZ7HO d GM8BPQ GM8BPQ-2 *** DISCONNECTED From Station GM8BPQ-0 + +New Disconnect Port 7 Q 0 +*/ + +extern VOID PROCESSUZ7HONODEMESSAGE(); + +VOID ProcessAGWPacket(struct TNCINFO * TNC, UCHAR * Message) +{ + PMSGWITHLEN buffptr; + MESSAGE Monframe; + struct HDDRWITHDIGIS MonDigis; + + struct AGWINFO * AGW = TNC->AGWInfo; + struct AGWHEADER * RXHeader = &AGW->RXHeader; + char Key[21]; + int Stream; + struct STREAMINFO * STREAM; + UCHAR AGWPort; + UCHAR MHCall[10] = ""; + int n; + unsigned char c; + unsigned char * ptr; + int Totallen = 0; + struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; + + +#ifdef __BIG_ENDIAN__ + RXHeader->DataLength = reverse(RXHeader->DataLength); +#endif + + switch (RXHeader->DataKind) + { + case 'D': // Appl Data + + TNC = GetSessionKey(Key, TNC); + + if (TNC == NULL) + return; + + // Find our Session + + Stream = 0; + + while (Stream <= AGW->MaxSessions) + { + STREAM = &TNC->Streams[Stream]; + + if (memcmp(STREAM->AGWKey, Key, 21) == 0) + { + // Found it; + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = RXHeader->DataLength; + memcpy(buffptr->Data, Message, RXHeader->DataLength); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + Stream++; + } + + // Not Found + + return; + + + case 'd': // Disconnected + + TNC = GetSessionKey(Key, TNC); + + if (TNC == NULL) + return; + + // Find our Session + + Stream = 0; + + while (Stream <= AGW->MaxSessions) + { + STREAM = &TNC->Streams[Stream]; + + if (memcmp(STREAM->AGWKey, Key, 21) == 0) + { + // Found it; + + if (STREAM->DiscWhenAllSent) + return; // Already notified + + if (STREAM->Connecting) + { + // Report Connect Failed, and drop back to command mode + + STREAM->Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "UZ7HO} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->DiscWhenAllSent = 10; + + if (TNC->RPBEACON) + SendRPBeacon(TNC); + + return; + } + + // Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + // if (STREAM->Disconnecting) // + // ReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + STREAM->DiscWhenAllSent = 10; + STREAM->FramesOutstanding = 0; + + if (TNC->RPBEACON) + SendRPBeacon(TNC); + + return; + } + Stream++; + } + + return; + + case 'C': + + // Connect. Can be Incoming or Outgoing + + // "*** CONNECTED To Station [CALLSIGN]" When the other station starts the connection + // "*** CONNECTED With [CALLSIGN]" When we started the connection + + // Create Session Key from port and callsign pair + + TNC = GetSessionKey(Key, TNC); + + if (TNC == NULL) + return; + + if (strstr(Message, " To Station")) + { + char noStreams[] = "No free sessions - disconnecting\r"; + + // Incoming. Look for a free Stream + + Stream = 1; + + while(Stream <= AGW->MaxSessions) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + goto GotStream; + + Stream++; + } + + // No free streams - send message then Disconnect + + // Do we need to swap From and To? - yes + + memcpy(RXHeader->callfrom, &Key[11], 10); + memcpy(RXHeader->callto, &Key[1], 10); + +#ifdef __BIG_ENDIAN__ + AGW->RXHeader.DataLength = reverse(strlen(noStreams)); +#else + AGW->RXHeader.DataLength = (int)strlen(noStreams); +#endif + RXHeader->DataKind = 'D'; + AGW->RXHeader.PID = 0xf0; + + send(TNCInfo[MasterPort[TNC->Port]]->TCPSock, (char *)&AGW->RXHeader, AGWHDDRLEN, 0); + send(TNCInfo[MasterPort[TNC->Port]]->TCPSock, noStreams, (int)strlen(noStreams), 0); + + Sleep(500); + RXHeader->DataKind = 'd'; + RXHeader->DataLength = 0; + + + send(TNCInfo[MasterPort[TNC->Port]]->TCPSock, (char *)&AGW->RXHeader, AGWHDDRLEN, 0); + return; + +GotStream: + + STREAM = &TNC->Streams[Stream]; + memcpy(STREAM->AGWKey, Key, 21); + STREAM->Connected = TRUE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + SuspendOtherPorts(TNC); + + strcpy(TNC->TargetCall, RXHeader->callto); + + ProcessIncommingConnect(TNC, RXHeader->callfrom, Stream, FALSE); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN > 0) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + else if (FULL_CTEXT) + { + Totallen = CTEXTLEN; + ptr = CTEXTMSG; + } + + while (Totallen) + { + int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + + if (sendLen == 0) + sendLen = 80; + + if (Totallen < sendLen) + sendLen = Totallen; + + SendData(Stream, TNC, &STREAM->AGWKey[0], ptr, sendLen); + + Totallen -= sendLen; + ptr += sendLen; + } + +/* + if (HFCTEXTLEN) + { + if (HFCTEXTLEN > 1) + SendData(Stream, TNC, &STREAM->AGWKey[0], HFCTEXT, HFCTEXTLEN); + } + else + { + if (FULL_CTEXT) + { + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + SendData(Stream, TNC, &STREAM->AGWKey[0], &CTEXTMSG[Next], CTPaclen); + Next += CTPaclen; + Len -= CTPaclen; + } + SendData(Stream, TNC, &STREAM->AGWKey[0], &CTEXTMSG[Next], Len); + } + } +*/ + if (strcmp(RXHeader->callto, TNC->NodeCall) != 0) // Not Connect to Node Call + { + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + char * ptr; + char Buffer[80]; // Data portion of frame + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(RXHeader->callto, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + int MsgLen = sprintf(Buffer, "%s\r", AppName); + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + SendData(Stream, TNC, Key, Msg, (int)strlen(Msg)); + + STREAM->DiscWhenAllSent = 100; // 10 secs + } + return; + } + } + + // Not to a known appl - drop through to Node + + return; + } + else + { + // Connect Complete + + // Find our Session + + Stream = 0; + + while (Stream <= AGW->MaxSessions) + { + STREAM = &TNC->Streams[Stream]; + + if (memcmp(STREAM->AGWKey, Key, 21) == 0) + { + // Found it; + + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = 0; + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", RXHeader->callfrom); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + UpdateMH(TNC, RXHeader->callfrom, '+', 'O'); + return; + } + Stream++; + } + + // Not Found + + return; + } + + case 'T': // Trasmitted Dats + + DoMonitorHddr(TNC, RXHeader, Message); + + return; + + case 'S': // Monitored Supervisory + + // Check for SABM + + if (strstr(Message, " '8') + return; + + AGWPort = BPQPort[AGWPort - '1'][TNC->Port]; + + TNC = TNCInfo[AGWPort]; + + if (TNC == NULL) + return; + + strlop(Message, '<'); + +// if (strchr(Message, '*')) +// UpdateMH(TNC, RXHeader->callfrom, '*', 0); +// else +// UpdateMH(TNC, RXHeader->callfrom, ' ', 0); + + return; + + case 'K': // raw data + + memset(&Monframe, 0, sizeof(Monframe)); + memset(&MonDigis, 0, sizeof(MonDigis)); + + Monframe.PORT = BPQPort[RXHeader->Port][TNC->Port]; + + if (Monframe.PORT == 0) // Unused UZ7HO port? + return; + + // Get real port + + TNC = TNCInfo[Monframe.PORT]; + + if (TNC == 0) + return; + + Monframe.LENGTH = RXHeader->DataLength + (MSGHDDRLEN - 1); + + memcpy(&Monframe.DEST[0], &Message[1], RXHeader->DataLength); + + // Check address - may have Single bit correction activated and non-x.25 filter off + + ptr = &Monframe.ORIGIN[0]; + n = 6; + + while(n--) + { + // Try a bit harder to detect corruption + + c = *(ptr++); + + if (c & 1) + return; // bottom bit set + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + return; + } + + +/* // if NETROM is enabled, and it is a NODES broadcast, process it + + if (TNC->PortRecord->PORTCONTROL.PORTQUALITY) + { + int i; + char * fiddle; + + if (Message[15] == 3 && Message[16] == 0xcf && Message[17] == 255) + i = 0; + + _asm + { + pushad + + mov al, Monframe.PORT + lea edi, Monframe + + call PROCESSUZ7HONODEMESSAGE + + popad + } + } +*/ + // Pass to Monitor + + time(&Monframe.Timestamp); + + MHCall[ConvFromAX25(&Monframe.ORIGIN[0], MHCall)] = 0; + + // I think we need to check if UI and if so report to nodemap + + // should skip digis and report last digi but for now keep it simple + + // if there are digis process them + + if (Monframe.ORIGIN[6] & 1) // No digis + { + if (Monframe.CTL == 3) + UpdateMHEx(TNC, MHCall, ' ', 0, NULL, TRUE); + else + UpdateMHEx(TNC, MHCall, ' ', 0, NULL, FALSE); + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + } + else + { + UCHAR * ptr1 = Monframe.DEST; + UCHAR * ptr2 = MonDigis.DEST; + int Rest = Monframe.LENGTH - (MSGHDDRLEN - 1); + + MonDigis.PORT = Monframe.PORT; + MonDigis.LENGTH = Monframe.LENGTH; + MonDigis.Timestamp = Monframe.Timestamp; + + + while ((ptr1[6] & 1) == 0) // till end of address + { + memcpy(ptr2, ptr1, 7); + ptr2 += 7; + ptr1 += 7; + Rest -= 7; + } + + memcpy(ptr2, ptr1, 7); // Copy Last + ptr2 += 7; + ptr1 += 7; + Rest -= 7; + + // Now copy CTL PID and Data + + memcpy(ptr2, ptr1, Rest); + + BPQTRACE((MESSAGE *)&MonDigis, TRUE); + + if (TNC->PortRecord->PORTCONTROL.PORTMHEARD) + MHPROC((struct PORTCONTROL *)TNC->PortRecord, (MESSAGE *)&MonDigis); + +// if (ptr1[0] == 3) +// UpdateMHEx(TNC, MHCall, ' ', 0, NULL, TRUE); +// else +// UpdateMHEx(TNC, MHCall, ' ', 0, NULL, FALSE); + + + + } + + return; + + case 'I': + break; + + case 'R': + { + // Version + + int v1, v2; + + memcpy(&v1, Message, 4); + memcpy(&v2, &Message[4], 4); + + if (v1 == 2019 && v2 == 'B') + TNC->AGWInfo->isQTSM |= 1; + + break; + } + + case 'g': + + // Capabilities - along with Version used to indicate QtSoundModem + // with ability to set and read Modem type and frequency/ + + if (Message[2] == 24 && Message[3] == 3 && Message[4] == 100) + { + // Set flag on any other ports on same TNC (all ports with this as master port) + + int p; + int This = TNC->Port; + + if (RXHeader->DataLength == 12) + { + // First reply - request Modem Freq and Name + for (p = This; p < 33; p++) + { + if (MasterPort[p] == This) + { + TNC = TNCInfo[p]; + + if (TNC) + { + char Buffer[32] = ""; + int MsgLen = 32; + SOCKET sock = TNCInfo[MasterPort[This]]->TCPSock; + + + TNC->AGWInfo->isQTSM |= 2; + + AGW->TXHeader.Port = UZ7HOChannel[p]; + AGW->TXHeader.DataKind = 'g'; + memset(AGW->TXHeader.callfrom, 0, 10); + memset(AGW->TXHeader.callto, 0, 10); +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(MsgLen); +#else + AGW->TXHeader.DataLength = MsgLen; +#endif + send(sock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + send(sock, Buffer, MsgLen, 0); + } + } + } + return; + } + + if (RXHeader->DataLength == 44) + { + // Modem Freq and Type Report from QtSM + + int p = BPQPort[RXHeader->Port][TNC->Port]; // Get subchannel port + + TNC = TNCInfo[p]; + + if (p == 0 || TNC == NULL) + return; + + memcpy(&TNC->AGWInfo->CenterFreq, &Message[12], 4); + memcpy(&TNC->AGWInfo->ModemName, &Message[16], 20); + memcpy(&TNC->AGWInfo->Version, &Message[38], 4); + + sprintf(TNC->WEB_MODE, "%s / %d Hz", TNC->AGWInfo->ModemName, TNC->AGWInfo->CenterFreq); + SetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + } + } + break; + + case 'X': + break; + + case 'x': + break; + + case 'Y': // Session Queue + + AGWPort = RXHeader->Port; + Key[0] = AGWPort + '1'; + + memset(&Key[1], 0, 20); + strcpy(&Key[11], RXHeader->callfrom); // Wrong way round for GetSessionKey + strcpy(&Key[1], RXHeader->callto); + + // Need to get BPQ Port from AGW Port + + if (AGWPort > 8) + return; + + AGWPort = BPQPort[AGWPort][TNC->Port]; + + TNC = TNCInfo[AGWPort]; + + if (TNC == NULL) + return; + +// Debugprintf("UZ7HO Port %d %d %c %s %s %d", TNC->Port, RXHeader->Port, +// RXHeader->DataKind, RXHeader->callfrom, RXHeader->callto, Message[0]); + + Stream = 0; + + while (Stream <= AGW->MaxSessions) + { + STREAM = &TNC->Streams[Stream]; + + if (memcmp(STREAM->AGWKey, Key, 21) == 0) + { + // Found it; + + memcpy(&STREAM->FramesOutstanding, Message, 4); + + if (STREAM->FramesOutstanding == 0) // All Acked + if (STREAM->Disconnecting && STREAM->BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + Stream++; + } + + // Not Found + + return; + + default: + + Debugprintf("UZ7HO Port %d %c %s %s %s %d", TNC->Port, RXHeader->DataKind, RXHeader->callfrom, RXHeader->callto, Message, Message[0]); + + return; + } +} +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC) +{ + struct AGWINFO * AGW = TNC->AGWInfo; + struct AGWHEADER * RXHeader = &AGW->RXHeader; + int AGWPort; + +// Create Session Key from port and callsign pair + + AGWPort = RXHeader->Port; + key[0] = AGWPort + '1'; + + memset(&key[1], 0, 20); + strcpy(&key[1], RXHeader->callfrom); + strcpy(&key[11], RXHeader->callto); + + // Need to get BPQ Port from AGW Port + + if (AGWPort > 8) + return 0; + + AGWPort = BPQPort[AGWPort][TNC->Port]; + + if (AGWPort == 0) + return 0; + + TNC = TNCInfo[AGWPort]; + return TNC; +} + +/* +Port field is the port where we want the data to tx +DataKind field =MAKELONG('D',0); The ASCII value of letter D +CallFrom is our call +CallTo is the call of the other station +DataLen is the length of the data that follow +*/ + +VOID SendData(int Stream, struct TNCINFO * TNC, char * Key, char * Msg, int MsgLen) +{ + struct AGWINFO * AGW = TNC->AGWInfo; + SOCKET sock = TNCInfo[MasterPort[TNC->Port]]->TCPSock; + int Paclen = 0; + + AGW->TXHeader.Port = Key[0] - '1'; + AGW->TXHeader.DataKind='D'; + memcpy(AGW->TXHeader.callfrom, &Key[11], 10); + memcpy(AGW->TXHeader.callto, &Key[1], 10); + + // If Length is greater than Paclen we should fragment + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) + Paclen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + + if (Paclen == 0) + Paclen = 80; + + if (MsgLen > Paclen) + { + // Fragment it. + // Is it best to send Paclen packets then short or equal length? + // I think equal length; + + int Fragments = (MsgLen + Paclen - 1) / Paclen; + int Fraglen = MsgLen / Fragments; + + if ((MsgLen & 1)) // Odd + Fraglen ++; + + while (MsgLen > Fraglen) + { +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(MsgLen); +#else + AGW->TXHeader.DataLength = Fraglen; +#endif + AGW->TXHeader.PID = 0xf0; + + send(sock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + send(sock, Msg, Fraglen, 0); + + Msg += Fraglen; + MsgLen -= Fraglen; + } + + // Drop through to send last bit + } +#ifdef __BIG_ENDIAN__ + AGW->TXHeader.DataLength = reverse(MsgLen); +#else + AGW->TXHeader.DataLength = MsgLen; +#endif + AGW->TXHeader.PID = 0xf0; + send(sock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); + send(sock, Msg, MsgLen, 0); +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char * Key = &TNC->Streams[Stream].AGWKey[0]; + struct AGWINFO * AGW = TNC->AGWInfo; + + AGW->TXHeader.Port = Key[0] - '1'; + AGW->TXHeader.DataKind='d'; + strcpy(AGW->TXHeader.callfrom, &Key[11]); + strcpy(AGW->TXHeader.callto, &Key[1]); + AGW->TXHeader.DataLength = 0; + + send(TNCInfo[MasterPort[TNC->Port]]->TCPSock, (char *)&AGW->TXHeader, AGWHDDRLEN, 0); +} + + + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Status[80]; + int s; + + // Clear Session Key + + memset(TNC->Streams[Stream].AGWKey, 0, 21); + + // if all streams are free, start scanner + + s = 0; + + while(s <= TNC->AGWInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + return; // Busy + } + s++; + } + + ReleaseOtherPorts(TNC); + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); +} + +static MESSAGE Monframe; // I frames come in two parts. + + +MESSAGE * AdjMsg; // Adjusted fir digis + + +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg) +{ + // Convert to ax.25 form and pass to monitor + + // Only update MH on UI, SABM, UA + + UCHAR * ptr, * starptr, * CPPtr, * nrptr, * nsptr; + char * context; + char MHCall[11]; + int ILen; + char * temp; + + Msg[RXHeader->DataLength] = 0; + + Monframe.LENGTH = MSGHDDRLEN + 16; // Control Frame + Monframe.PORT = BPQPort[RXHeader->Port][TNC->Port]; + + if (RXHeader->DataKind == 'T') // Transmitted + Monframe.PORT += 128; + + /* +UZ7HO T GM8BPQ-2 G8XXX 1:Fm GM8BPQ-2 To G8XXX [12:08:42] +UZ7HO d G8XXX GM8BPQ-2 *** DISCONNECTED From Station G8XXX-0 +UZ7HO T GM8BPQ-2 G8XXX 1:Fm GM8BPQ-2 To G8XXX [12:08:48] +UZ7HO T GM8BPQ-2 APRS 1:Fm GM8BPQ-2 To APRS Via WIDE2-2 [12:08:54] +=5828.54N/00612.69W- {BPQ32} +*/ + + + AdjMsg = &Monframe; // Adjusted for digis + ptr = strstr(Msg, "Fm "); + + ConvToAX25(&ptr[3], Monframe.ORIGIN); + + memcpy(MHCall, &ptr[3], 11); + strlop(MHCall, ' '); + + ptr = strstr(ptr, "To "); + + ConvToAX25(&ptr[3], Monframe.DEST); + + ptr = strstr(ptr, "Via "); + + if (ptr) + { + // We have digis + + char Save[100]; + + memcpy(Save, &ptr[4], 60); + + ptr = strtok_s(Save, ", ", &context); +DigiLoop: + + temp = (char *)AdjMsg; + temp += 7; + AdjMsg = (MESSAGE *)temp; + + Monframe.LENGTH += 7; + + starptr = strchr(ptr, '*'); + if (starptr) + *(starptr) = 0; + + ConvToAX25(ptr, AdjMsg->ORIGIN); + + if (starptr) + AdjMsg->ORIGIN[6] |= 0x80; // Set end of address + + ptr = strtok_s(NULL, ", ", &context); + + if (ptr[0] != '<') + goto DigiLoop; + } + AdjMsg->ORIGIN[6] |= 1; // Set end of address + + ptr = strstr(Msg, "<"); + + if (memcmp(&ptr[1], "SABM", 4) == 0) + { + AdjMsg->CTL = 0x2f; +// UpdateMH(TNC, MHCall, ' ', 0); + } + else + if (memcmp(&ptr[1], "DISC", 4) == 0) + AdjMsg->CTL = 0x43; + else + if (memcmp(&ptr[1], "UA", 2) == 0) + { + AdjMsg->CTL = 0x63; +// UpdateMH(TNC, MHCall, ' ', 0); + } + else + if (memcmp(&ptr[1], "DM", 2) == 0) + AdjMsg->CTL = 0x0f; + else + if (memcmp(&ptr[1], "UI", 2) == 0) + { + AdjMsg->CTL = 0x03; + + if (RXHeader->DataKind != 'T') + { + // only report RX + + if (strstr(Msg, "To BEACON ")) + { + // Update MH with Received Beacons + + char * ptr1 = strchr(Msg, ']'); + + if (ptr1) + { + ptr1 += 2; // Skip ] and cr + if (memcmp(ptr1, "QRA ", 4) == 0) + { + char Call[10], Loc[10] = ""; + sscanf(&ptr1[4], "%s %s", &Call[0], &Loc[0]); + + UpdateMHEx(TNC, MHCall, ' ', 0, Loc, TRUE); + } + } + else + UpdateMH(TNC, MHCall, ' ', 0); + } + } + } + else + if (memcmp(&ptr[1], "RR", 2) == 0) + { + nrptr = strchr(&ptr[3], '>'); + AdjMsg->CTL = 0x1 | (nrptr[-2] << 5); + } + else + if (memcmp(&ptr[1], "RNR", 3) == 0) + { + nrptr = strchr(&ptr[4], '>'); + AdjMsg->CTL = 0x5 | (nrptr[-2] << 5); + } + else + if (memcmp(&ptr[1], "REJ", 3) == 0) + { + nrptr = strchr(&ptr[4], '>'); + AdjMsg->CTL = 0x9 | (nrptr[-2] << 5); + } + else + if (memcmp(&ptr[1], "FRMR", 4) == 0) + AdjMsg->CTL = 0x87; + else + if (ptr[1] == 'I') + { + nsptr = strchr(&ptr[3], 'S'); + + AdjMsg->CTL = (nsptr[-2] << 5) | (nsptr[1] & 7) << 1 ; + } + + CPPtr = strchr(ptr, ' '); + if (CPPtr == NULL) + return; + + if (strchr(&CPPtr[1], 'P')) + { + if (AdjMsg->CTL != 3) + AdjMsg->CTL |= 0x10; +// Monframe.DEST[6] |= 0x80; // SET COMMAND + } + + if (strchr(&CPPtr[1], 'F')) + { + if (AdjMsg->CTL != 3) + AdjMsg->CTL |= 0x10; +// Monframe.ORIGIN[6] |= 0x80; // SET P/F bit + } + + if ((AdjMsg->CTL & 1) == 0 || AdjMsg->CTL == 3) // I or UI + { + ptr = strstr(ptr, "pid"); + sscanf(&ptr[4], "%x", (unsigned int *)&AdjMsg->PID); + + ptr = strstr(ptr, "Len"); + ILen = atoi(&ptr[4]); + + ptr = strstr(ptr, "]"); + ptr += 2; // Skip ] and cr + memcpy(AdjMsg->L2DATA, ptr, ILen); + Monframe.LENGTH += ILen; + } + else if (AdjMsg->CTL == 0x97) // FRMR + { + ptr = strstr(ptr, ">"); + sscanf(ptr+1, "%hhx %hhx %hhx", &AdjMsg->PID, &AdjMsg->L2DATA[0], &AdjMsg->L2DATA[1]); + Monframe.LENGTH += 3; + } + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, TRUE); + +} + +/* + +1:Fm GM8BPQ To GM8BPQ-2 [17:36:17] + 1:Fm GM8BPQ To GM8BPQ-2 [17:36:29] +BPQ:GM8BPQ-2} G8BPQ Win32 Test Switch, Skigersta, Isle o + + 1:Fm GM8BPQ-2 To GM8BPQ [17:36:32] + 1:Fm GM8BPQ To GM8BPQ-2 [17:36:33] +f Lewis. + + 1:Fm GM8BPQ-2 To GM8BPQ [17:36:36] + +1:Fm GM8BPQ To GM8BPQ-2 [17:36:18R] +1:Fm GM8BPQ To GM8BPQ-2 [17:36:30R] +BPQ:GM8BPQ-2} G8BPQ Win32 Test Switch, Skigersta, Isle o +1:Fm GM8BPQ-2 To GM8BPQ [17:36:32T] +1:Fm GM8BPQ To GM8BPQ-2 [17:36:34R] +f Lewis. + +1:Fm GM8BPQ-2 To GM8BPQ [17:36:36T] + + + +1:Fm GM8BPQ To GM8BPQ-2 [17:36:17T] +1:Fm GM8BPQ To GM8BPQ-2 [17:36:29T] +BPQ:GM8BPQ-2} G8BPQ Win32 Test Switch, Skigersta, Isle o +1:Fm GM8BPQ-2 To GM8BPQ [17:36:32R] +1:Fm GM8BPQ To GM8BPQ-2 [17:36:33T] +f Lewis. + +1:Fm GM8BPQ-2 To GM8BPQ [17:36:36R] +*/ diff --git a/V4-HPLaptop.c b/V4-HPLaptop.c new file mode 100644 index 0000000..7673a10 --- /dev/null +++ b/V4-HPLaptop.c @@ -0,0 +1,1821 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use the V4 TNC as a Port Driver +// +// Uses BPQ EXTERNAL interface +// +// Uses a number of routines in WINMOR.c + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "CHeaders.h" +#include "tncinfo.h" +#include "bpq32.h" + +int (WINAPI FAR *GetModuleFileNameExPtr)(); + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +KillTNC(struct TNCINFO * TNC); +RestartTNC(struct TNCINFO * TNC); +KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +static VOID ChangeMYC(struct TNCINFO * TNC, char * Call); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + +static char ClassName[]="V4STATUS"; +static char WindowTitle[] = "V4TNC"; +static int RigControlRow = 147; + +#define V4 +#define NARROWMODE 0 +#define WIDEMODE 0 + +#include + + +extern int SemHeldByAPI; + +static RECT Rect; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr == '#') return (TRUE); // comment + + if (*ptr == ';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + else if (_stricmp(ptr, "CM108") == 0) + TNC->PTTMode = PTTCM108; + else if (_stricmp(ptr, "HAMLIB") == 0) + TNC->PTTMode = PTTHAMLIB; + + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + {} // Ignore + else + + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + + + +void ConnecttoWINMORThread(int port); +VOID V4ProcessDataSocketData(int port); +int ConnecttoWINMOR(); +int ProcessReceivedData(struct TNCINFO * TNC); +VOID ReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXBPQPORTS 32 + +static time_t ltime; + +#pragma pack() + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +static VOID ChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + datalen = sprintf(TXMsg, "MYCALL %s\r\n", Call); + send(TNC->TCPSock,TXMsg, datalen, 0); + +// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); +// TNC->StartSent = TRUE; + +// send(TNC->TCPSock, "MYCALL\r\n", 8, 0); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; + int i,winerr; + char txbuff[500]; + char Status[80]; + unsigned int bytes,txlen=0; + char ErrMsg[255]; + int Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if ((TNC->Busy & CDBusy) == 0) + { + // No, so send + + send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[11], strlen(TNC->ConnectCmd)-13); + + sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + + // Probe link + + send(TNC->TCPSock, "BUFFER\r\n", 8, 0); + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); + } + } + if (TNC->FECPending) // Check if FEC Send needed + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + } +/* + if (TNC->UpdateWL2K) + { + TNC->UpdateWL2KTimer--; + + if (TNC->UpdateWL2KTimer == 0) + { + TNC->UpdateWL2KTimer = 32910/2; // Every Hour + if (CheckAppl(TNC, "RMS ")) // Is RMS Available? + SendReporttoWL2K(TNC); + } + } +*/ + if (TNC->RIG) + { + if (TNC->RIG->RigFreq != TNC->LastFreq) + { + char FREQMsg[80]; + int Len; + + TNC->LastFreq = TNC->RIG->RigFreq; + Len = sprintf(FREQMsg, "DISPLAY CF:%1.4f\r\n", TNC->LastFreq + .0015); + send(TNC->TCPSock,FREQMsg, Len, 0); + } + } + + if (TNC->TimeSinceLast++ > 700) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, "V4 TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + TNC->Streams[0].ARQENDSent = FALSE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + +// send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime >9 ) + { + TNC->LastFreq = 0; // so display will be updated + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + if (select((int)TNC->TCPSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (readfs.fd_count == 1) + V4ProcessDataSocketData(port); + + if (writefs.fd_count == 1) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = (unsigned int)buffptr->Len; + + memcpy(txbuff,buffptr->Data, txlen); + bytes=send(TNC->TCPSock,(const char FAR *)&txbuff,txlen,0); + ReleaseBuffer(buffptr); + } + + if (errorfs.fd_count == 1) + { + i=sprintf(ErrMsg, "V4 Data Connection lost for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr = Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "No Connection to V4 TNC\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->BPQtoWINMOR_Q) + return 0; // Socket is blocked - just drop packets till it clears + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen,0); + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + +/* if (TNC->Busy) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } +*/ return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, buff->L2DATA); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(&buff[8], "FEC\r", 4) == 0 || _memicmp(&buff[8], "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; + send(TNC->TCPSock,"MODE FEC\r\n", 10, 0); + strcpy(TNC->WEB_MODE, "FEC"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + return 0; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80] = "ARQCONNECT "; + + memcpy(&Connect[11], &buff->L2DATA[2], txlen); + txlen += 9; + Connect[txlen++] = 0x0a; + Connect[txlen] = 0; + + _strupr(Connect); + + // See if Busy + + if (TNC->Busy & CDBusy) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = 100; // 10 secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + bytes=send(TNC->TCPSock, Connect, txlen, 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &Connect[11], txlen-13); + + sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + } + else + { + buff->L2DATA[txlen++] = 0x0a; + bytes=send(TNC->TCPSock, buff->L2DATA, txlen, 0); + } + } + if (bytes != txlen) + { + + // WINMOR doesn't seem to recover from a blocked write. For now just reset + +// if (bytes == SOCKET_ERROR) +// { + winerr=WSAGetLastError(); + + i=sprintf(ErrMsg, "V4 Write Failed for port %d - error code = %d\n", port, winerr); + WritetoConsole(ErrMsg); + + +// if (winerr != WSAEWOULDBLOCK) +// { + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + + return (0); +// } +// else +// { +// bytes=0; // resent whole packet +// } + +// } + + // Partial Send or WSAEWOULDBLOCK. Save data, and send once busy clears + + + // Get a buffer + +// buffptr=GetBuff(); + +// if (buffptr == 0) +// { + // No buffers, so can only break connection and try again + +// closesocket(TCPSock[MasterPort[port]]); + +// CONNECTED[MasterPort[port]]=FALSE; + +// return (0); +// } + +// buffptr->Len=txlen-bytes; // Bytes still to send + +// memcpy(buffptr+2,&txbuff[bytes],txlen-bytes); + +// C_Q_ADD(&BPQtoWINMOR_Q[MasterPort[port]],buffptr); + +// return (0); + } + + + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + break; + + case 4: // reinit + + return (0); + + case 5: // Close + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return (0); + + case 6: // Scan Stop Interface + + Param = (int)(size_t)buff; + + if (Param == 1) // Request Permission + { + if (!TNC->ConnectPending) + return 0; // OK to Change + +// send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + + return TRUE; + } + + if (Param == 2) // Check Permission + { + if (TNC->ConnectPending) + return -1; // Skip Interval + + return 1; // OK to change + } + + if (Param == 3) // Release Permission + { +// send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + return 0; + } + + if (Param == 4) // Set Wide Mode + { + send(TNC->TCPSock, "BW 1600\r\n", 9, 0); + return 0; + } + + if (Param == 5) // Set Narrow Mode + { + send(TNC->TCPSock, "BW 500\r\n", 8, 0); + return 0; + } + + return 0; + } + return 0; +} + +VOID V4ProcessDataSocketData(int port) +{ + // Info on Data Socket - just packetize and send on + + struct TNCINFO * TNC = TNCInfo[port]; + int InputLen, PacLen = 236, i; + PMSGWITHLEN buffptr; + char * msg; + + TNC->TimeSinceLast = 0; + +loop: + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + InputLen=recv(TNC->TCPDataSock, (char *)&buffptr[2], PacLen, 0); + + if (InputLen == -1) + { + ReleaseBuffer(buffptr); + return; + } + + + //Debugprintf("Winmor: RXD %d bytes", InputLen); + + if (InputLen == 0) + { + // Does this mean closed? + + strcpy(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + ReleaseBuffer(buffptr); + return; + } + + + msg = buffptr->Data; + + // Message should always be received in 17 char chunks. 17th is a status byte + // In ARQ, 6 = "Echo as sent" ack + + if (InputLen != 17) + { + Debugprintf("V4 TNC incorrect RX Len = %d", InputLen); + goto loop; + } + + if (msg[16] == 0x06) + goto loop; + + InputLen = 16; + + for (i = 0; i < 16; i++) + { + if (msg[i] == 0) + break; + + if (msg[i] == 10) + continue; + + if (msg[i] < 0x20 || msg[i] > 0x7e) + msg[i] = '?'; + } + + + msg[InputLen] = 0; + + WritetoTrace(TNC, msg, InputLen); + + // V4 Sends null padded blocks + + InputLen = (int)strlen(buffptr->Data); + + if (msg[InputLen - 1] == 10) // LF + { + // Replace with CRLF + + msg[InputLen-1] = 13; // Add CR + msg[InputLen++] = 10; + } + + buffptr->Len = InputLen; + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + goto loop; +} + + + + +static VOID ReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call + + ChangeMYC(TNC, TNC->NodeCall); + +// send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); + + MySetWindowText(TNC->xIDC_TNCSTATE, "Free"); + strcpy(TNC->WEB_TNCSTATE, "Free"); + + // Start Scanner + + ReleaseOtherPorts(TNC); + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "V4 Status" + "

V4 Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + + + +void * V4ExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->Hardware = H_V4; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; +// PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "DebugLog True\r\n"); + strcat(TempScript, "AUTOID FALSE\r\n"); + strcat(TempScript, "CODEC FALSE\r\n"); + strcat(TempScript, "TIMEOUT 90\r\n"); + strcat(TempScript, "MODE ARQ\r\n"); + strcat(TempScript, "TUNING 100\r\n"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + + +// strcat(TNC->InitScript,"FECRCV True\r\n"); + + sprintf(Msg, "MYCALL %s\r\nCODEC TRUE\r\nMYCALL\r\n", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcat(TNC->InitScript,"PROCESSID\r\n"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 450, 500, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,116,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,116,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,116,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,116,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,138,250,300, TNC->hDlg, NULL, hInstance, NULL); + + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill V4 TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart V4 TNC"); +// AppendMenu(TNC->hPopMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after each Connection"); +// CheckMenuItem(TNC->hPopMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + + MoveWindows(TNC); +#endif + i=sprintf(Msg,"V4 Host %s %d\n", TNC->HostName, htons(TNC->destaddr.sin_port)); + WritetoConsole(Msg); + + strcpy(TNC->WEB_MODE, "ARQ"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + ConnecttoWINMOR(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"Registration", 12) == 0) + { + SendMessage(hwnd, WM_CLOSE, 0, 0); + return TRUE; + } + if (memcmp(wtext,"V4 Sound Card TNC", 17) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + sprintf (wtext, "V4 Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(hwnd, wtext); + // return FALSE; + } + } + + return (TRUE); +} +#endif + +static VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + // Response on WINMOR control channel. Could be a reply to a command, or + // an Async Response + + PMSGWITHLEN buffptr; + char Status[80]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + { + TNC->TimeSinceLast = 0; + } + + Buffer[MsgLen - 2] = 0; // Remove CRLF + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy |= PTTBusy; + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, TRUE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->Busy &= ~PTTBusy; + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->Busy |= CDBusy; + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->Busy &= ~CDBusy; + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 2); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "CONNECTED", 9) == 0) + { + char Call[11]; + char * ptr; + char * ApplPtr = APPLS; + APPLCALLS * APPL; + int App; + char Appl[10]; + + WritetoTrace(TNC, Buffer, MsgLen - 2); + + STREAM->ConnectTime = time(NULL); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incomming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, Call, 0, TRUE); + TNC->Streams[0].ARQENDSent = FALSE; + + if (TNC->RIG) + sprintf(Status, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + else + sprintf(Status, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->CurrentMYC, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); + + buffptr->Len = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG) + sprintf(Status, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(Status, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + UpdateMH(TNC, Call, '+', 'O'); + + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "V4} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 2); + + // Release Session + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "CMD", 3) == 0) + { + return; + } + + if (_memicmp(Buffer, "PENDING", 6) == 0) + return; + +/* + + if (_memicmp(Buffer, "FAULT Not connected!", 20) == 0) + { + // If in response to ARQEND, assume Disconnected was missed + + if (TNC->Streams[0].Disconnecting) + { + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + } + } +*/ + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (TNC->Streams[0].ARQENDSent == FALSE) + { + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + MySetWindowText(TNC->xIDC_TRAFFIC, &Buffer[7]); + strcpy(TNC->WEB_TRAFFIC, &Buffer[7]); + + return; + } + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->PID = atoi(&Buffer[10]); + + // Get the File Name in case we want to restart it. + + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + if (TNC->ProgramPath) + free(TNC->ProgramPath); + + TNC->ProgramPath = _strdup(ExeName); + } + } + + // Set Window Title to reflect BPQ Port Description + +#ifndef LINBPQ + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "CONREQ", 6) == 0) + { + // if to one of our APPLCALLS, change TNC MYCALL + + APPLCALLS * APPL; + char Appl[11]; + char Target[20]; + char * ptr; + int i; + + memcpy(Target, &Buffer[7], 12); + ptr = memchr(Target, ' ', 12); + if (ptr) + *ptr = 0; + + if (strcmp(Target, TNC->NodeCall) == 0) + ChangeMYC(TNC, Target); + else + { + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (strcmp(Appl, Target) == 0) + { + ChangeMYC(TNC, Target); + break; + } + } + } + } + WritetoTrace(TNC, Buffer, MsgLen - 2); + + // Update MH + + ptr = strstr(Buffer, " de "); + if (ptr) + UpdateMH(TNC, ptr + 4, '!', 'O'); + } + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "V4} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + +} + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + char ErrMsg[255]; + + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + if (!TNC->CONNECTING) + { + sprintf(ErrMsg, "V4TNC Connection lost for BPQ Port %d\r\n", TNC->Port); + WritetoConsole(ErrMsg); + } + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return 0; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &TNC->TCPBuffer[TNC->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); + TNC->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = TNC->InputLen - (int)(ptr2 - ptr); + + memcpy(Buffer, TNC->TCPBuffer, MsgLen); + + ProcessResponse(TNC, Buffer, MsgLen); + + memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + return 0; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + { + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + send(TNC->TCPSock,"ABORT\r\n", 7, 0); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ReleaseTNC(TNC); + + ChangeMYC(TNC, TNC->NodeCall); // In case changed to an applcall + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + send(TNC->TCPSock,"MODE ARQ\r\n", 10, 0); + strcpy(TNC->WEB_MODE, "ARQ"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + } +} diff --git a/V4.c b/V4.c new file mode 100644 index 0000000..8592734 --- /dev/null +++ b/V4.c @@ -0,0 +1,1766 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use the V4 TNC as a Port Driver +// +// Uses BPQ EXTERNAL interface +// +// Uses a number of routines in WINMOR.c + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "CHeaders.h" +#include "tncinfo.h" +#include "bpq32.h" + +int (WINAPI FAR *GetModuleFileNameExPtr)(); + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +KillTNC(struct TNCINFO * TNC); +RestartTNC(struct TNCINFO * TNC); +KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +static VOID ChangeMYC(struct TNCINFO * TNC, char * Call); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + +static char ClassName[]="V4STATUS"; +static char WindowTitle[] = "V4TNC"; +static int RigControlRow = 147; + +#define V4 +#define NARROWMODE 0 +#define WIDEMODE 0 + +#include + + +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr == '#') return (TRUE); // comment + + if (*ptr == ';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + {} // Ignore + else + + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + + + +void ConnecttoWINMORThread(int port); +VOID V4ProcessDataSocketData(int port); +int ConnecttoWINMOR(); +int ProcessReceivedData(struct TNCINFO * TNC); +VOID ReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXBPQPORTS 32 + +static time_t ltime; + +#pragma pack() + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +static VOID ChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + datalen = sprintf(TXMsg, "MYCALL %s\r\n", Call); + send(TNC->TCPSock,TXMsg, datalen, 0); + +// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); +// TNC->StartSent = TRUE; + +// send(TNC->TCPSock, "MYCALL\r\n", 8, 0); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; + int i,winerr; + char txbuff[500]; + char Status[80]; + unsigned int bytes,txlen=0; + char ErrMsg[255]; + int Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if ((TNC->Busy & CDBusy) == 0) + { + // No, so send + + send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[11], strlen(TNC->ConnectCmd)-13); + + sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + + // Probe link + + send(TNC->TCPSock, "BUFFER\r\n", 8, 0); + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); + } + } + if (TNC->FECPending) // Check if FEC Send needed + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + } +/* + if (TNC->UpdateWL2K) + { + TNC->UpdateWL2KTimer--; + + if (TNC->UpdateWL2KTimer == 0) + { + TNC->UpdateWL2KTimer = 32910/2; // Every Hour + if (CheckAppl(TNC, "RMS ")) // Is RMS Available? + SendReporttoWL2K(TNC); + } + } +*/ + if (TNC->RIG) + { + if (TNC->RIG->RigFreq != TNC->LastFreq) + { + char FREQMsg[80]; + int Len; + + TNC->LastFreq = TNC->RIG->RigFreq; + Len = sprintf(FREQMsg, "DISPLAY CF:%1.4f\r\n", TNC->LastFreq + .0015); + send(TNC->TCPSock,FREQMsg, Len, 0); + } + } + + if (TNC->TimeSinceLast++ > 700) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, "V4 TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + TNC->Streams[0].ARQENDSent = FALSE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + +// send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime >9 ) + { + TNC->LastFreq = 0; // so display will be updated + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + if (select((int)TNC->TCPSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (readfs.fd_count == 1) + V4ProcessDataSocketData(port); + + if (writefs.fd_count == 1) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = (unsigned int)buffptr->Len; + + memcpy(txbuff,buffptr->Data, txlen); + bytes=send(TNC->TCPSock,(const char FAR *)&txbuff,txlen,0); + ReleaseBuffer(buffptr); + } + + if (errorfs.fd_count == 1) + { + i=sprintf(ErrMsg, "V4 Data Connection lost for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr = Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "No Connection to V4 TNC\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->BPQtoWINMOR_Q) + return 0; // Socket is blocked - just drop packets till it clears + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen,0); + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + +/* if (TNC->Busy) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } +*/ return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, buff->L2DATA); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(&buff[8], "FEC\r", 4) == 0 || _memicmp(&buff[8], "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; + send(TNC->TCPSock,"MODE FEC\r\n", 10, 0); + strcpy(TNC->WEB_MODE, "FEC"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + return 0; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80] = "ARQCONNECT "; + + memcpy(&Connect[11], &buff->L2DATA[2], txlen); + txlen += 9; + Connect[txlen++] = 0x0a; + Connect[txlen] = 0; + + _strupr(Connect); + + // See if Busy + + if (TNC->Busy & CDBusy) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = 100; // 10 secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + bytes=send(TNC->TCPSock, Connect, txlen, 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &Connect[11], txlen-13); + + sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + } + else + { + buff->L2DATA[txlen++] = 0x0a; + bytes=send(TNC->TCPSock, buff->L2DATA, txlen, 0); + } + } + if (bytes != txlen) + { + + // WINMOR doesn't seem to recover from a blocked write. For now just reset + + winerr = WSAGetLastError(); + + sprintf(ErrMsg, "V4 Write Failed for port %d - error code = %d\n", port, winerr); + WritetoConsole(ErrMsg); + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + return (0); + } + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + break; + + case 4: // reinit + + return (0); + + case 5: // Close + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return (0); + + case 6: // Scan Stop Interface + + Param = (int)(size_t)buff; + + if (Param == 1) // Request Permission + { + if (!TNC->ConnectPending) + return 0; // OK to Change + +// send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + + return TRUE; + } + + if (Param == 2) // Check Permission + { + if (TNC->ConnectPending) + return -1; // Skip Interval + + return 1; // OK to change + } + + if (Param == 3) // Release Permission + { +// send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + return 0; + } + + if (Param == 4) // Set Wide Mode + { + send(TNC->TCPSock, "BW 1600\r\n", 9, 0); + return 0; + } + + if (Param == 5) // Set Narrow Mode + { + send(TNC->TCPSock, "BW 500\r\n", 8, 0); + return 0; + } + + return 0; + } + return 0; +} + +VOID V4ProcessDataSocketData(int port) +{ + // Info on Data Socket - just packetize and send on + + struct TNCINFO * TNC = TNCInfo[port]; + int InputLen, PacLen = 236, i; + PMSGWITHLEN buffptr; + char * msg; + + TNC->TimeSinceLast = 0; + +loop: + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + InputLen=recv(TNC->TCPDataSock, (char *)&buffptr[2], PacLen, 0); + + if (InputLen == -1) + { + ReleaseBuffer(buffptr); + return; + } + + + //Debugprintf("Winmor: RXD %d bytes", InputLen); + + if (InputLen == 0) + { + // Does this mean closed? + + strcpy(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + ReleaseBuffer(buffptr); + return; + } + + + msg = buffptr->Data; + + // Message should always be received in 17 char chunks. 17th is a status byte + // In ARQ, 6 = "Echo as sent" ack + + if (InputLen != 17) + { + Debugprintf("V4 TNC incorrect RX Len = %d", InputLen); + goto loop; + } + + if (msg[16] == 0x06) + goto loop; + + InputLen = 16; + + for (i = 0; i < 16; i++) + { + if (msg[i] == 0) + break; + + if (msg[i] == 10) + continue; + + if (msg[i] < 0x20 || msg[i] > 0x7e) + msg[i] = '?'; + } + + + msg[InputLen] = 0; + + WritetoTrace(TNC, msg, InputLen); + + // V4 Sends null padded blocks + + InputLen = (int)strlen(buffptr->Data); + + if (msg[InputLen - 1] == 10) // LF + { + // Replace with CRLF + + msg[InputLen-1] = 13; // Add CR + msg[InputLen++] = 10; + } + + buffptr->Len = InputLen; + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + goto loop; +} + + + + +static VOID ReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call + + ChangeMYC(TNC, TNC->NodeCall); + +// send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); + + MySetWindowText(TNC->xIDC_TNCSTATE, "Free"); + strcpy(TNC->WEB_TNCSTATE, "Free"); + + // Start Scanner + + ReleaseOtherPorts(TNC); + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "V4 Status" + "

V4 Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + + + +void * V4ExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->Hardware = H_V4; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; +// PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "DebugLog True\r\n"); + strcat(TempScript, "AUTOID FALSE\r\n"); + strcat(TempScript, "CODEC FALSE\r\n"); + strcat(TempScript, "TIMEOUT 90\r\n"); + strcat(TempScript, "MODE ARQ\r\n"); + strcat(TempScript, "TUNING 100\r\n"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + + +// strcat(TNC->InitScript,"FECRCV True\r\n"); + + sprintf(Msg, "MYCALL %s\r\nCODEC TRUE\r\nMYCALL\r\n", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcat(TNC->InitScript,"PROCESSID\r\n"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 450, 500, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,116,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,116,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,116,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,116,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,138,250,300, TNC->hDlg, NULL, hInstance, NULL); + + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill V4 TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart V4 TNC"); +// AppendMenu(TNC->hPopMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after each Connection"); +// CheckMenuItem(TNC->hPopMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + + MoveWindows(TNC); +#endif + i=sprintf(Msg,"V4 Host %s %d\n", TNC->HostName, htons(TNC->destaddr.sin_port)); + WritetoConsole(Msg); + + strcpy(TNC->WEB_MODE, "ARQ"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + ConnecttoWINMOR(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"Registration", 12) == 0) + { + SendMessage(hwnd, WM_CLOSE, 0, 0); + return TRUE; + } + if (memcmp(wtext,"V4 Sound Card TNC", 17) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + sprintf (wtext, "V4 Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(hwnd, wtext); + // return FALSE; + } + } + + return (TRUE); +} +#endif + +static VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + // Response on WINMOR control channel. Could be a reply to a command, or + // an Async Response + + PMSGWITHLEN buffptr; + char Status[80]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + { + TNC->TimeSinceLast = 0; + } + + Buffer[MsgLen - 2] = 0; // Remove CRLF + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy |= PTTBusy; + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->Busy &= ~PTTBusy; + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->Busy |= CDBusy; + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->Busy &= ~CDBusy; + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 2); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "CONNECTED", 9) == 0) + { + char Call[11]; + char * ptr; + char * ApplPtr = APPLS; + APPLCALLS * APPL; + int App; + char Appl[10]; + + WritetoTrace(TNC, Buffer, MsgLen - 2); + + STREAM->ConnectTime = time(NULL); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incomming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, Call, 0, TRUE); + TNC->Streams[0].ARQENDSent = FALSE; + + if (TNC->RIG) + sprintf(Status, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + else + sprintf(Status, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->CurrentMYC, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); + + buffptr->Len = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG) + sprintf(Status, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(Status, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + UpdateMH(TNC, Call, '+', 'O'); + + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "V4} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 2); + + // Release Session + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "CMD", 3) == 0) + { + return; + } + + if (_memicmp(Buffer, "PENDING", 6) == 0) + return; + +/* + + if (_memicmp(Buffer, "FAULT Not connected!", 20) == 0) + { + // If in response to ARQEND, assume Disconnected was missed + + if (TNC->Streams[0].Disconnecting) + { + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + } + } +*/ + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (TNC->Streams[0].ARQENDSent == FALSE) + { + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + MySetWindowText(TNC->xIDC_TRAFFIC, &Buffer[7]); + strcpy(TNC->WEB_TRAFFIC, &Buffer[7]); + + return; + } + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->PID = atoi(&Buffer[10]); + + // Get the File Name in case we want to restart it. + + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + if (TNC->ProgramPath) + free(TNC->ProgramPath); + + TNC->ProgramPath = _strdup(ExeName); + } + } + + // Set Window Title to reflect BPQ Port Description + +#ifndef LINBPQ + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "CONREQ", 6) == 0) + { + // if to one of our APPLCALLS, change TNC MYCALL + + APPLCALLS * APPL; + char Appl[11]; + char Target[20]; + char * ptr; + int i; + + memcpy(Target, &Buffer[7], 12); + ptr = memchr(Target, ' ', 12); + if (ptr) + *ptr = 0; + + if (strcmp(Target, TNC->NodeCall) == 0) + ChangeMYC(TNC, Target); + else + { + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (strcmp(Appl, Target) == 0) + { + ChangeMYC(TNC, Target); + break; + } + } + } + } + WritetoTrace(TNC, Buffer, MsgLen - 2); + + // Update MH + + ptr = strstr(Buffer, " de "); + if (ptr) + UpdateMH(TNC, ptr + 4, '!', 'O'); + } + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "V4} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + +} + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + char ErrMsg[255]; + + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + if (!TNC->CONNECTING) + { + sprintf(ErrMsg, "V4TNC Connection lost for BPQ Port %d\r\n", TNC->Port); + WritetoConsole(ErrMsg); + } + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return 0; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &TNC->TCPBuffer[TNC->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); + TNC->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = TNC->InputLen - (int)(ptr2 - ptr); + + memcpy(Buffer, TNC->TCPBuffer, MsgLen); + + ProcessResponse(TNC, Buffer, MsgLen); + + memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + return 0; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + { + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + send(TNC->TCPSock,"ABORT\r\n", 7, 0); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ReleaseTNC(TNC); + + ChangeMYC(TNC, TNC->NodeCall); // In case changed to an applcall + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + send(TNC->TCPSock,"MODE ARQ\r\n", 10, 0); + strcpy(TNC->WEB_MODE, "ARQ"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + } +} diff --git a/VARA.c b/VARA.c new file mode 100644 index 0000000..3066db5 --- /dev/null +++ b/VARA.c @@ -0,0 +1,2539 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use VARA Virtual TNC in a form +// of ax.25 + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + + +#include "CHeaders.h" + +#ifdef WIN32 +#include +#endif + +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 VARASendData(struct TNCINFO * TNC, UCHAR * Buff, int Len); +VOID VARASendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length); +void CountRestarts(struct TNCINFO * TNC); + +#ifndef LINBPQ +BOOL CALLBACK EnumVARAWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="VARASTATUS"; +static char WindowTitle[] = "VARA"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + + +BOOL VARAStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + KillTNC(TNC); + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + +int ConnecttoVARA(int port); + +BOOL VARAStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + ConnecttoVARA(TNC->Port); + TNC->lasttime = time(NULL);; + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + TNC->MaxConReq = 10; // Default + + // Read Initialisation lines + + while (TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + else if (_memicmp(buf, "BW2300", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 50; + } + else if (_memicmp(buf, "BW500", 5) == 0) + { + TNC->ARDOPCurrentMode[0] = 'N'; + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 53; + } + else if (_memicmp(buf, "BW2750", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 54; + } + else if (_memicmp(buf, "FM1200", 6) == 0) + TNC->DefaultMode = TNC->WL2KMode = 51; + else if (_memicmp(buf, "FM9600", 5) == 0) + TNC->DefaultMode = TNC->WL2KMode = 52; + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + + return (TRUE); +} + +void VARAThread(void * portptr); +int ConnecttoVARA(int port); +VOID VARAProcessReceivedData(struct TNCINFO * TNC); +VOID VARAProcessReceivedControl(struct TNCINFO * TNC); +VOID VARAReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes,txlen=0; + size_t Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + + switch (fn) + { + case 8: + + return 0; + + case 7: + + // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // Check session limit timer + + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + VARASendCommand(TNC, "ABORT\r", TRUE); + STREAM->Disconnecting = TRUE; + } + } + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + + VARASendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + TNC->Streams[0].Connecting = FALSE; + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + return 0; + + case 1: // poll + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + VARASendCommand(TNC, "DISCONNECT\r", TRUE); + } + } + + /* + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); +*/ + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + TNC->lasttime = ltime; + ConnecttoVARA(port); + } + } + + // See if any frames for this port + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = (int)buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes = VARASendData(TNC, &txbuff[0], txlen); + STREAM->BytesTXed += bytes; + ReleaseBuffer(buffptr); + } + + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len=36; + memcpy(buffptr->Data,"No Connection to VARA 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 = (int)buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, txbuff, txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + STREAM->PacketsSent++; + + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, buff->L2DATA, txlen); + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0 || _memicmp(buff->L2DATA, "BYE\r", 4) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(buff->L2DATA, "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "VARA} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "VARA} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 0; + } + } + + + if (_memicmp(&buff->L2DATA[0], "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(&buff->L2DATA[0], "BW2300", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + TNC->WL2KMode = 50; + } + + if (_memicmp(&buff->L2DATA[0], "BW500", 5) == 0) + { + TNC->ARDOPCurrentMode[0] = 'N'; + TNC->WL2KMode = 53; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if a Connect Command. If so set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + sprintf(Connect, "CONNECT %s %s\r", TNC->Streams[0].MyCall, &buff->L2DATA[2]); + + // Need to set connecting here as if we delay for busy we may incorrectly process OK response + + TNC->Streams[0].Connecting = TRUE; + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + VARASendCommand(TNC, Connect, TRUE); + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + buff->L2DATA[(txlen++) - 1] = 13; + buff->L2DATA[(txlen) - 1] = 0; + VARASendCommand(TNC, &buff->L2DATA[0], TRUE); + } + } + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { +// GetSemaphore(&Semaphore, 52); +// VARASendCommand(TNC, "CLOSE", FALSE); +// FreeSemaphore(&Semaphore); +// Sleep(100); + } + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + if (TNC->WeStartedTNC) + KillTNC(TNC); + + return 0; + + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on VARA"); + return 1; // OK to change + } + + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + if (!TNC->ConnectPending) + return 0; // OK to Change + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->VARAMode != TNC->ARDOPCurrentMode[0]) + { + // Mode changed + + if (TNC->ARDOPCurrentMode[0] == 'S') + { + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + } + + if (Scan->VARAMode == 'W') // Set Wide Mode + { + VARASendCommand(TNC, "BW2300\r", TRUE); + TNC->WL2KMode = 50; + } + if (Scan->VARAMode == 'T') // Set Wide Mode + { + VARASendCommand(TNC, "BW2750\r", TRUE); + TNC->WL2KMode = 54; + } + else if (Scan->VARAMode == 'N') // Set Narrow Mode + { + VARASendCommand(TNC, "BW500\r", TRUE); + TNC->WL2KMode = 53; + } + else if (Scan->VARAMode == 'S') // Skip + { + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + } + + TNC->ARDOPCurrentMode[0] = Scan->VARAMode; + } + return 0; + } + return 0; +} + +void CountRestarts(struct TNCINFO * TNC) +{ + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); +} +/* +char WebProcTemplate[] = "" + "\r\n" + "" + "%s" + "" + "%s" + "
"; +*/ +char WebProcTemplate[] = "" + "\r\n" + "\r\n" + "%s\r\n" + "

%s

" + "" + "\r\n" + "" + "Abort Session" + "Kill TNC" + "Kill and Restart TNC" + ""; + +char sliderBit[] = " TX Offset %d" + "\r\n" + "\r\n"; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "VARA Status", "VARA Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
S/N%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID VARASuspendPort(struct TNCINFO * TNC) +{ + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); +} + +VOID VARAReleasePort(struct TNCINFO * TNC) +{ + VARASendCommand(TNC, "LISTEN ON\r", TRUE); +} + + +void * VARAExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + int AuxCount = 0; + char Appl[11]; + char * TempScript; + int line; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + // + // Will be called once for each VARA port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(8192); + + if (TNC->ProgramPath) + TNC->WeStartedTNC = 1; + + TNC->Hardware = H_VARA; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + 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 + + PortEntry->PORTCONTROL.UICAPABLE = FALSE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = VARASuspendPort; + TNC->ReleasePortProc = VARAReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = VARAStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = VARAStopPort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + +// strcat(TempScript, "ROBUST False\r"); + + // Set MYCALL(S) + + if (TNC->LISTENCALLS) + { + sprintf(Msg, "MYCALL %s", TNC->LISTENCALLS); + } + else + { + sprintf(Msg, "MYCALL %s", TNC->NodeCall); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + // *ptr++ = ' '; + *ptr = 0; + } + strcat(Msg, " "); + strcat(Msg, Appl); + AuxCount++; + if (AuxCount == 4) // Max 5 in MYCALL + break; + } + } + } + + strcat(Msg, "\r"); + + strcat(TempScript, Msg); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + + strcat(TNC->InitScript,"LISTEN ON\r"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + // if mode hasn't been set explicitly or via WL2KREPORT set to HF Wide mode (BW2300) + + if (TNC->DefaultMode == 0) + { + if (TNC->WL2K && TNC->WL2K->mode >= 50 && TNC->WL2K->mode <= 53) // A VARA Mode + TNC->DefaultMode = TNC->WL2KMode = TNC->WL2K->mode; + else + TNC->DefaultMode = TNC->WL2KMode = 50; // Default to 2300 + } + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + line = 6; + + if (TNC->TXFreq) + { + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 500, 450, ForcedClose); + + InitCommonControls(); // loads common control's DLL + + CreateWindowEx(0, "STATIC", "TX Tune", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNE = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNEVAL = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 320,line,30,20, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->xIDC_TXTUNE, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG(-200, 200)); // min. & max. positions + + line += 22; + } + else + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,386,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,line,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,520,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,line,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,144,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "S/N", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,line,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,line,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,line,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + Consoleprintf("VARA Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoVARA(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +int ConnecttoVARA(int port) +{ + if (TNCInfo[port]->CONNECTING || TNCInfo[port]->PortRecord->PORTCONTROL.PortStopped) + return 0; + + _beginthread(VARAThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID VARAThread(void * portptr) +{ + // Opens sockets and looks for data on control and data sockets. + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1; + char * ptr2; + PMSGWITHLEN buffptr; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + + if (TNCInfo[port]->PortRecord->PORTCONTROL.PortStopped) + { + TNC->CONNECTING = FALSE; + return; + } + + +// printf("Starting VARA Thread\n"); + +// if on Windows and Localhost see if TNC is running + +#ifdef WIN32 + + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + + if (TNC->PID == 0) + goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + goto TNCRunning; + } + +#endif + +TNCNotRunning: + + // Not running or can't check, restart if we have a path + + if (TNC->ProgramPath) + { + Consoleprintf("Trying to (re)start TNC %s", TNC->ProgramPath); + + if (RestartTNC(TNC)) + CountRestarts(TNC); + + Sleep(TNC->AutoStartDelay); + } + +TNCRunning: + + if (TNC->Alerted == FALSE) + { + sprintf(TNC->WEB_COMMSSTATE, "Connecting to TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for VARA socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPSock); +// closesocket(TNC->TCPDataSock); + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for VARA socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for VARA Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + +// printf("Trying to connect to VARA TNC\n"); + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // Connected successful + + goto VConnected; + + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for VARA socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + +// printf("VARA Connect failed\n"); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + +VConnected: + + // Connect Data Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for VARA Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // VARA needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + + while (ptr1 && ptr1[0]) + { + unsigned char c; + + ptr2 = strchr(ptr1, 13); + + if (ptr2) + { + c = *(ptr2 + 1); // Save next char + *(ptr2 + 1) = 0; // Terminate string + } + VARASendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(1 + ptr2++) = c; // Put char back + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to VARA TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to VARA TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title + EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("VARA Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + VARAProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + VARAProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "VARA Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + break; + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + sprintf(Msg, "VARA Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + break; + } + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + continue; + + sprintf(Msg, "VARA No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + +// GetSemaphore(&Semaphore, 52); +// VARASendCommand(TNC, "CODEC FALSE", FALSE); +// FreeSemaphore(&Semaphore); + + Sleep(100); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + +// if (TNC->PID && TNC->WeStartedTNC) +// { +// KillTNC(TNC); +// + break; + } + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + sprintf(Msg, "VARA Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + + +VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 1] = 0; // Remove CR + + TNC->TimeSinceLast = 0; + + if (_memicmp(Buffer, "PTT ON", 6) == 0) + { +// Debugprintf("PTT On"); + + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + return; + } + + if (_memicmp(Buffer, "PTT OFF", 6) == 0) + { +// Debugprintf("PTT Off"); + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + + return; + } + + if (_memicmp(Buffer, "SN ", 3) == 0) + { + strcpy(TNC->WEB_PROTOSTATE, &Buffer[3]); + MySetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + TNC->SNR = atof(&Buffer[3]); + return; + } + + if (_stricmp(Buffer, "BUSY ON") == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_stricmp(Buffer, "BUSY OFF") == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + Debugprintf(Buffer); +// WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0) + { + TNC->ConnectPending = FALSE; + Debugprintf(Buffer); + + // If a callsign is present it is the calling station - add to MH + + if (TNC->SeenCancelPending == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + TNC->SeenCancelPending = 1; + } + + if (Buffer[13] == ' ') + UpdateMH(TNC, &Buffer[14], '!', 'I'); + + return; + } + + TNC->SeenCancelPending = 0; + + if (strcmp(Buffer, "OK") == 0) + { + // Need to discard response to LISTEN OFF after attach + + if (TNC->DiscardNextOK) + { + TNC->DiscardNextOK = 0; + return; + } + + if (TNC->Streams[0].Connecting == TRUE) + return; // Discard response or it will mess up connect scripts + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + Debugprintf(Buffer); + + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %s", + STREAM->BytesTXed, STREAM->BytesRXed, &Buffer[7]); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + + strcpy(TNC->WEB_MODE, ""); + + if (strstr(Buffer, "2300")) + { + Speed = 50; + strcpy(TNC->WEB_MODE, "2300"); + } + else if (strstr(Buffer, "NARROW")) + { + Speed = 51; + strcpy(TNC->WEB_MODE, "NARROW"); + } + else if (strstr(Buffer, "WIDE")) + { + Speed = 52; + strcpy(TNC->WEB_MODE, "WIDE"); + } + else if (strstr(Buffer, "500")) + { + Speed = 53; + strcpy(TNC->WEB_MODE, "500"); + } + else if (strstr(Buffer, "2750")) + { + Speed = 54; + strcpy(TNC->WEB_MODE, "2750"); + } + + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Target Call + + ptr = strchr(&Buffer[10], ' '); + + if (ptr) + { + memcpy(TNC->TargetCall, ++ptr, 10); + strlop(TNC->TargetCall, ' '); + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (Speed) + SESS->Mode = Speed; + else + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("VARA Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("VARA Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + int txlen = (int)buffptr->Len; + VARASendData(TNC, buffptr->Data, txlen); + ReleaseBuffer(buffptr); + } + + VARASendData(TNC, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", TNC->TargetCall); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, TNC->TargetCall, '+', 'O'); + return; + } + } + + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + Debugprintf(Buffer); + + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "VARA} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->RestartAfterFailure) + { + if (TNC->ProgramPath) + KillTNC(TNC); + } + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session3 + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + VARAReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + + if (_memicmp(Buffer, "IAMALIVE", 8) == 0) + { +// strcat(Buffer, "\r\n"); +// WritetoTrace(TNC, Buffer, strlen(Buffer)); + return; + } + +// Debugprintf(Buffer); + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); +// return; + } + + if (_memicmp(Buffer, "REGISTERED", 9) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "MISSING SOUNDCARD", 17) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + // Others should be responses to commands + + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "VARA} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); +} + +VOID VARAProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen; + + InputLen = recv(TNC->TCPDataSock, TNC->ARDOPDataBuffer, 8192, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + +// closesocket(TNC->TCPSock); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + + + return; + } + + TNC->DataInputLen += InputLen; + + VARAProcessDataPacket(TNC, TNC->ARDOPDataBuffer, TNC->DataInputLen); + TNC->DataInputLen=0; + return; +} + + + +VOID VARAProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + // I don't think it likely we will get packets this long, but be aware... + + // We can get pretty big ones in the faster + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR + { + // Usual Case - single meg in buffer + + VARAProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + MsgLen = TNC->InputLen - (int)(ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + VARAProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + return; +} + + + +VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + PMSGWITHLEN buffptr; + + TNC->TimeSinceLast = 0; + + STREAM->BytesRXed += Length; + + Data[Length] = 0; + Debugprintf("VARA: RXD %d bytes", Length); + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed, STREAM->BytesRXed,STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + // May need to fragment + + while (Length) + { + int Fraglen = Length; + + if (Length > PACLEN) + Fraglen = PACLEN; + + Length -= Fraglen; + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + memcpy(buffptr->Data, Data, Fraglen); + + WritetoTrace(TNC, Data, Fraglen); + + Data += Fraglen; + + buffptr->Len = Fraglen; + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return; +} + +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + VARASendCommand(TNC, "DISCONNECT\r", TRUE); +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + char Abort[] = "ABORT\r"; + + VARASendCommand(TNC, Abort, TRUE); + WritetoTrace(TNC, Abort, 5); +} + +static VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + VARAReleaseTNC(TNC); + TNC->ARDOPCurrentMode[0] = 0; // Force Mode select on next scan change + + // Also reset mode in case incoming call has changed it + + if (TNC->DefaultMode == 50) + VARASendCommand(TNC, "BW2300\r", TRUE); + else if (TNC->DefaultMode == 53) + VARASendCommand(TNC, "BW500\r", TRUE); + else if (TNC->DefaultMode == 54) + VARASendCommand(TNC, "BW2750\r", TRUE); +} + + +VOID VARASendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue) +{ + int SentLen; + + if (Buff[0] == 0) // Terminal Keepalive? + return; + + if (memcmp(Buff, "LISTEN O", 8) == 0) + TNC->DiscardNextOK = TRUE; // Responding to LISTEN messes up forwarding + + if (TNC->CONNECTED == 0) + return; + + if(TNC->TCPSock) + { + SentLen = send(TNC->TCPSock, Buff, (int)strlen(Buff), 0); + + if (SentLen != strlen(Buff)) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "VARA Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTED = FALSE; + return; + } + } + return; +} + +int VARASendData(struct TNCINFO * TNC, UCHAR * Buff, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int bytes=send(TNC->TCPDataSock,(const char FAR *)Buff, Len, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, Buff, Len); + return bytes; +} + +#ifndef LINBPQ + +BOOL CALLBACK EnumVARAWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[128]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + int n; + + n = GetWindowText(hwnd, wtext, 127); + + if (memcmp(wtext,"VARA", 4) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + char msg[512]; + char ID[64] = ""; + int i = 29; + + memcpy(ID, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION, 30); + + while (ID[i] == ' ') + ID[i--] = 0; + + wtext[n] = 0; + sprintf (msg, "BPQ %s - %s", ID, wtext); + SetWindowText(hwnd, msg); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID VARAReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + +// ARDOPChangeMYC(TNC, TNC->NodeCall); + + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command(-1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, TXMsg); + + ReleaseOtherPorts(TNC); +} + + + + + + diff --git a/Versions.h b/Versions.h new file mode 100644 index 0000000..a91446f --- /dev/null +++ b/Versions.h @@ -0,0 +1,125 @@ + +#ifdef Kernel + +#define Vers 5,2,9,2 +#define Verstring "5.2.9.2\0" +#define Datestring "September 2012" +#define VerComments "G8BPQ Packet Switch V5.2.9.2\0" +#define VerCopyright "Copyright © 2001-2012 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" + +#endif + +#define KVers 6,0,23,18 +#define KVerstring "6.0.23.18\0" + +#ifdef CKernel + +#define Vers KVers +#define Verstring KVerstring +#define Datestring "August 2022" +#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring +#define VerCopyright "Copyright © 2001-2022 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" +#define VerProduct "BPQ32" + +#endif + +#ifdef TermTCP + +#define Vers 1,0,16,1 +#define Verstring "1.0.16.1\0" +#define VerComments "Internet Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2022 John Wiseman G8BPQ\0" +#define VerDesc "Simple TCP Terminal Program for G8BPQ Switch\0" +#define VerProduct "BPQTermTCP" + +#endif + +#ifdef BPQTerm + +#define Vers 2,2,5,1 +#define Verstring "2.2.5.1\0" +#define VerComments "Simple Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2022 John Wiseman G8BPQ\0" +#define VerDesc "Simple Terminal Program for G8BPQ Switch\0" +#define VerProduct "BPQTerminal" + +#endif + +#ifdef BPQTermMDI + +#define Vers 2,2,0,3 +#define Verstring "2.2.0.3\0" +#define VerComments "MDI Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2022 John Wiseman G8BPQ\0" +#define VerDesc "MDI Terminal Program for G8BPQ Switch\0" + +#endif + +#ifdef MAIL + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "Mail server for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2022 John Wiseman G8BPQ\0" +#define VerDesc "Mail server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQMail" + +#endif + +#ifdef HOSTMODES + +#define Vers 1,1,8,1 +#define Verstring "1.1.8.1\0" +//#define SPECIALVERSION "Test 3" +#define VerComments "Host Modes Emulator for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2019 John Wiseman G8BPQ\0" +#define VerDesc "Host Modes Emulator for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQHostModes" + +#endif + + +#ifdef UIUTIL + +#define Vers 0,1,3,1 +#define Verstring "0.1.3.1\0" +#define VerComments "Beacon Utility for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2019 John Wiseman G8BPQ\0" +#define VerDesc "Beacon Utility for G8BPQ Switch\0" +#define VerProduct "BPQUIUtil" + +#endif + +#ifdef AUTH + +#define Vers 0,1,0,0 +#define Verstring "0.1.0.0\0" +#define VerComments "Password Generation Utility for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2022 John Wiseman G8BPQ\0" +#define VerDesc "Password Generation Utility for G8BPQ Switch\0" + +#endif + +#ifdef APRS + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "APRS Client for G8BPQ Switch\0" +#define VerCopyright "Copyright © 2012-2022 John Wiseman G8BPQ\0" +#define VerDesc "APRS Client for G8BPQ Switch\0" +#define VerProduct "BPQAPRS" + +#endif + +#ifdef CHAT + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "Chat server for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2022 John Wiseman G8BPQ\0" +#define VerDesc "Chat server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQChat" + +#endif diff --git a/WINMOR.c b/WINMOR.c new file mode 100644 index 0000000..25ba700 --- /dev/null +++ b/WINMOR.c @@ -0,0 +1,3125 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use 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 + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int 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 char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + +// There seem to be timing issues when calling SendMessage from multiple threads. +// Queue and process in main thread + +UINT * WINMORTraceQ; +UINT * SetWindowTextQ; + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) +{ + int index = 0; + UCHAR * ptr1 = Msg, * ptr2; + UCHAR Line[1000]; + int LineLen, i; + UCHAR Save; + int SaveLen = Len; + if (Len < 0) + return; + + Save = Msg[Len]; + Msg[Len] = 0; + +#ifndef LINBPQ + index=SendMessage(TNC->hMonitor, LB_SETCURSEL, -1, 0); +#endif + +lineloop: + + if (Len > 0) + { + // copy text to control a line at a time + + ptr2 = memchr(ptr1, 13, Len); + + if (ptr2) + { + ptr2++; + LineLen = (int)(ptr2 - ptr1); + Len -= LineLen; + memcpy(Line, ptr1, LineLen); + memcpy(&Line[LineLen - 1], "", 4); + LineLen += 3; + + if ((*ptr2) == 10) + { + memcpy(&Line[LineLen], "", 4); + LineLen += 4; + ptr2++; + Len --; + } + + Line[LineLen] = 0; + + // If line contains any data above 7f, assume binary and dont display + + for (i = 0; i < LineLen; i++) + { + if (Line[i] > 126 || Line[i] < 32) + goto Skip; + } + + // We now also pass to Monitor Window + + if (strlen(Line) < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + strlen(Line); + Monframe.DEST[0] = 1; // Plain Text Monitor + strcpy(&Monframe.DEST[1], Line); + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Line); +#endif + // Write to Web Buffer + + strcat(TNC->WebBuffer, Line); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + Skip: + ptr1 = ptr2; + + goto lineloop; + + } + + // Process incomplete line + + for (i = 0; i < Len; i++) + { + if (ptr1[i] > 126 || ptr1[i] < 32) + break; + } + + if (i == Len) + { + if (Len < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + Len; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], ptr1, Len); + Monframe.DEST[1 + Len] = 0; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); +#endif + strcat(TNC->WebBuffer, ptr1); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + } + } + +#ifdef LINBPQ +#else + + if (index > 1200) + do + index=index=SendMessage(TNC->hMonitor, LB_DELETESTRING, 0, 0); + while (index > 1000); + + if (index > -1) + index=SendMessage(TNC->hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); +#endif + Msg[SaveLen] = Save; + +} + +VOID MySetWindowTextWithSem(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD(&SetWindowTextQ, buffptr); + } + +#endif +} + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); + +struct SEM SetWindTextSem = {0, 0, 0, 0}; + +VOID MySetWindowText(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + GetSemaphore(&SetWindTextSem, 61); + buffptr = zalloc(400); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD_NP(&SetWindowTextQ, buffptr); + } + + FreeSemaphore(&SetWindTextSem); +#endif +} + +VOID SetWindowTextSupport() +{ + PMSGWITHLEN Buffer; + + while (SetWindowTextQ) + { + GetSemaphore(&SetWindTextSem, 61); + Buffer = Q_REM_NP(&SetWindowTextQ); + SetWindowText((HWND)Buffer->Len, Buffer->Data); + FreeSemaphore(&SetWindTextSem); + free(Buffer); + } +} + + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len) +{ + // It seems writing from multiple threads can cause problems in Windows + // Queue and process in main thread + +#ifdef LINBPQ + WritetoTraceSupport(TNC, Msg, Len); +} +#else + UINT * buffptr; + BOOL Sem = FALSE; + + if (Len < 0) + return; + + // Get semaphore if it isn't set + + if (InterlockedExchange(&Semaphore.Flag, 1) == 0) + { + Sem = TRUE; + Semaphore.Gets++; + } + + buffptr = GetBuff(); + + if (buffptr) + { + if (Len > 340) + Len = 340; + + buffptr[1] = (UINT)TNC; + buffptr[2] = (UINT)Len; + memcpy(&buffptr[3], Msg, Len + 1); + + C_Q_ADD(&WINMORTraceQ, buffptr); + } + + if (Sem) + FreeSemaphore(&Semaphore); + +} +#endif + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else if (standardParams(TNC, buf) == FALSE) + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + + + +void WINMORThread(void * portptr); +VOID ProcessDataSocketData(int port); +int ConnecttoWINMOR(); +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); + + +#define MAXBPQPORTS 32 + +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->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->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(-1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + ProcessDataSocketData(port); + + if (FD_ISSET(TNC->TCPDataSock, &writefs)) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = buffptr->Len; + memcpy(txbuff,buffptr->Data,txlen); + bytes=send(TNC->TCPSock, (const char FAR *)&txbuff, (int)txlen, 0); + ReleaseBuffer(buffptr); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + i=sprintf(ErrMsg, "WINMOR Data Connection lost for BPQ Port %d\r\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); // Data goes to +7, but we have an extra byte + datalen = buffptr->Len; + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(buffptr->Data, "No Connection to WINMOR Virtual TNC\r", 36); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes = send(TNC->TCPDataSock, txbuff, (int)txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, txbuff, (int)txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + STREAM->PacketsSent++; + + if (STREAM->PacketsSent == 3) + { + if (TNC->Robust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + else + send(TNC->TCPSock, "ROBUST FALSE\r\n", 14, 0); + } + + bytes = send(TNC->TCPDataSock,buff->L2DATA, (int)txlen, 0); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], (int)txlen); + + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + + if (TNC->BusyFlags) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &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 WINMOR"); + 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(-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<33; 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); + } +} + +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<33; 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) +{ + 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; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->Hardware = H_WINMOR; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + 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(-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(-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(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) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; // Avoid zero divide + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + + 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 +#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[80]; + 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]; + + 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); + + while (KillOldTNC(TNC->ProgramPath) && n++ < 100) + { + Sleep(100); + } + + + if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0 ,NULL ,NULL, &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/WPRoutines.c b/WPRoutines.c new file mode 100644 index 0000000..97c8828 --- /dev/null +++ b/WPRoutines.c @@ -0,0 +1,1636 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without Fvoideven the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +int CurrentWPIndex; +char CurrentWPCall[10]; + +time_t LASTWPSendTime; + + +VOID DoWPUpdate(WPRec *WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate); +VOID Do_Save_WPRec(HWND hDlg); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); + +WPRec * AllocateWPRecord() +{ + WPRec * WP = zalloc(sizeof (WPRec)); + + GetSemaphore(&AllocSemaphore, 0); + + WPRecPtr=realloc(WPRecPtr,(++NumberofWPrecs+1) * sizeof(void *)); + WPRecPtr[NumberofWPrecs]= WP; + + FreeSemaphore(&AllocSemaphore); + + return WP; +} + +extern config_t cfg; + +VOID GetWPDatabase() +{ + WPRec WPRec; + FILE * Handle; + int ReadLen; + WPRecP WP; + char CfgName[MAX_PATH]; + long long val; + config_t wpcfg; + config_setting_t * group, * wpgroup; + int i = 1; + struct stat STAT; + + // If WP info is in main config file, use it + + group = config_lookup (&cfg, "WP"); + + if (group) + { + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Record[1024]; + char * ptr, * ptr2; + unsigned int n; + + sprintf(Key, "R%d", i++); + + GetStringValue(group, Key, Record); + + if (Record[0] == 0) // End of List + return; + + memset(&WPRec, 0, sizeof(WPRec)); + + WP = &WPRec; + + ptr = Record; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->callsign[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->name[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->Type = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->changed = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->seen = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->first_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->secnd_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) WP->last_modif = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) + { + WP->last_seen = atol(ptr); + + // Check Call + + for (n = 1; n < strlen(WP->callsign); n++) // skip first which may also be digit + { + if (isdigit(WP->callsign[n])) + { + // Has a digit. Check Last is not digit + + if (isalpha(WP->callsign[strlen(WP->callsign) - 1])) + { + WP = LookupWP(WPRec.callsign); + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + goto WPOK; + } + } + } + Debugprintf("Bad WP Call %s", WP->callsign); + } +WPOK:; + } + return; + } + + // If text format exists use it + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + if (stat(CfgName, &STAT) == -1) + goto tryOld; + + config_init(&wpcfg); + + if (!config_read_file(&wpcfg, CfgName)) + { + char Msg[256]; + sprintf(Msg, "Config File %s Line %d - %s\n", CfgName, + config_error_line(&wpcfg), config_error_text(&wpcfg)); + + printf("%s", Msg); + config_destroy(&wpcfg); + goto tryOld; + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Temp[128]; + + sprintf(Key, "R%d", i++); + + wpgroup = config_lookup(&wpcfg, Key); + + if (wpgroup == NULL) // End of List + { + config_destroy(&wpcfg); + return; + } + + memset(&WPRec, 0, sizeof(WPRec)); + + GetStringValue(wpgroup, "c", WPRec.callsign); + GetStringValue(wpgroup, "n", WPRec.name); + + WPRec.Type = GetIntValue(wpgroup, "T"); + WPRec.changed = GetIntValue(wpgroup, "ch"); + WPRec.seen = GetIntValue(wpgroup, "s"); + + GetStringValue(wpgroup, "h", WPRec.first_homebbs); + GetStringValue(wpgroup, "sh", WPRec.secnd_homebbs); + GetStringValue(wpgroup, "z", WPRec.first_zip); + GetStringValue(wpgroup, "sz", WPRec.secnd_zip); + + GetStringValue(wpgroup, "q", Temp); + Temp[30] = 0; + strcpy(WPRec.first_qth, Temp); + + GetStringValue(wpgroup, "sq", Temp); + Temp[30] = 0; + strcpy(WPRec.secnd_qth, Temp); + + val = GetIntValue(wpgroup, "m"); + WPRec.last_modif = val; + val = GetIntValue(wpgroup, "ls"); + WPRec.last_seen = val; + + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + continue; + + if (_stricmp(WPRec.callsign, "RMS") == 0) + continue; + + if (_stricmp(WPRec.callsign, "SYSTEM") == 0) + continue; + + if (_stricmp(WPRec.callsign, "SWITCH") == 0) + continue; + + if (_stricmp(WPRec.callsign, "SYSOP") == 0) + continue; + + if (_memicmp(WPRec.callsign, "SMTP", 4) == 0) + continue; + + if (_memicmp(WPRec.callsign, "SMTP:", 5) == 0) + continue; + + if (_stricmp(WPRec.callsign, "AMPR") == 0) + continue; + + if (_stricmp(WPRec.callsign, "FILE") == 0) + continue; + + if (_memicmp(WPRec.callsign, "MCAST", 5) == 0) + continue; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + } + +tryOld: + + Handle = fopen(WPDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memset(WPRecPtr[0], 0, sizeof(WPRec)); + NumberofWPrecs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&WPRec, 0, sizeof(WPRec)); + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memcpy(WPRecPtr[0], &WPRec, sizeof(WPRec)); + + NumberofWPrecs = 0; + +Next: + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == sizeof(WPRec)) + { + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + goto Next; + + if (_stricmp(WPRec.callsign, "RMS") == 0) + goto Next; + + if (_stricmp(WPRec.callsign, "SYSTEM") == 0) + goto Next; + + if (_stricmp(WPRec.callsign, "SWITCH") == 0) + goto Next; + + if (_stricmp(WPRec.callsign, "SYSOP") == 0) + goto Next; + + if (_memicmp(WPRec.callsign, "SMTP", 4) == 0) + goto Next; + + if (_memicmp(WPRec.callsign, "SMTP:", 5) == 0) + goto Next; + + if (_stricmp(WPRec.callsign, "AMPR") == 0) + goto Next; + + if (_stricmp(WPRec.callsign, "FILE") == 0) + goto Next; + + if (_memicmp(WPRec.callsign, "MCAST", 5) == 0) + goto Next; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + goto Next; + } + + fclose(Handle); + SaveWPDatabase(); +} + +VOID CopyWPDatabase() +{ + char Backup[MAX_PATH]; + char Orig[MAX_PATH]; + + return; + + strcpy(Backup, WPDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(WPDatabasePath, Backup, FALSE); + + strcpy(Backup, WPDatabasePath); + strlop(Backup, '.'); + strcat(Backup, ".cfg.bak"); + + strcpy(Orig, WPDatabasePath); + strlop(Orig, '.'); + strcat(Orig, ".cfg"); + CopyFile(Orig, Backup, FALSE); +} + +VOID SaveWPDatabase() +{ +// SaveConfig(ConfigName); // WP config is now in main config file + + int i; + config_setting_t *root, *group; + config_t cfg; + char Key[16]; + WPRec * WP; + char CfgName[MAX_PATH]; + long long val; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + for (i = 0; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + sprintf(Key, "R%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + SaveStringValue(group, "c", &WP->callsign[0]); + SaveStringValue(group, "n", &WP->name[0]); + SaveIntValue(group, "T", WP->Type); + SaveIntValue(group, "ch", WP->changed); + SaveIntValue(group, "s", WP->seen); + SaveStringValue(group, "h", &WP->first_homebbs[0]); + SaveStringValue(group, "sh", &WP->secnd_homebbs[0]); + SaveStringValue(group, "z", &WP->first_zip[0]); + SaveStringValue(group, "sz", &WP->secnd_zip[0]); + SaveStringValue(group, "q", &WP->first_qth[0]); + SaveStringValue(group, "sq", &WP->secnd_qth[0]); + val = WP->last_modif; + SaveInt64Value(group, "m", val); + val = WP->last_seen; + SaveInt64Value(group, "ls", val); + } + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + Debugprintf("Saving WP Database to %s\n", CfgName); + + config_write_file(&cfg, CfgName); + config_destroy(&cfg); + +} + +WPRec * LookupWP(char * Call) +{ + WPRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (_stricmp(ptr->callsign, Call) == 0) return ptr; + } + + return NULL; +} + +char * FormatWPDate(time_t Datim) +{ + struct tm *tm; + static char Date[]="xx-xxx-xx "; + + tm = gmtime(&Datim); + + if (tm) + { + if (tm->tm_year >= 100) + sprintf_s(Date, sizeof(Date), "%02d-%3s-%02d", + tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100); + else + sprintf_s(Date, sizeof(Date), ""); + } + return Date; +} + +#ifndef LINBPQ + +int Do_WP_Sel_Changed(HWND hDlg) +{ + // Update WP display with newly selected rec + + WPRec * WP; + int Sel = SendDlgItemMessage(hDlg, IDC_WP, CB_GETCURSEL, 0, 0); + char Type[] = " "; + + if (Sel == -1) + SendDlgItemMessage(hDlg, IDC_WP, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + else + SendDlgItemMessage(hDlg, IDC_WP, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + + for (CurrentWPIndex = 1; CurrentWPIndex <= NumberofWPrecs; CurrentWPIndex++) + { + WP = WPRecPtr[CurrentWPIndex]; + + if (_stricmp(WP->callsign, CurrentWPCall) == 0) + { + + SetDlgItemText(hDlg, IDC_WPNAME, WP->name); + SetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs); + SetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs); + SetDlgItemText(hDlg, IDC_QTH1, WP->first_qth); + SetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth); + SetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip); + SetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip); + Type[0] = WP->Type; + SetDlgItemText(hDlg, IDC_TYPE, Type); + SetDlgItemInt(hDlg, IDC_CHANGED, WP->changed, FALSE); + SetDlgItemInt(hDlg, IDC_SEEN, WP->seen, FALSE); + + SetDlgItemText(hDlg, IDC_LASTSEEN, FormatWPDate(WP->last_seen)); + SetDlgItemText(hDlg, IDC_LASTMODIFIED, FormatWPDate(WP->last_modif)); + + return 0; + } + } + + CurrentWPIndex = -1; + + return 0; +} + +INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + +VOID Do_Save_WPRec(HWND hDlg) +{ + WPRec * WP; + char Type[] = " "; + BOOL OK1; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); + GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); + GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs, 41); + GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); + GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); + GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); + GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); + WP->last_modif = time(NULL); + WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); + + WP->Type = 'U'; + WP->changed = 1; + + SaveWPDatabase(); + + sprintf(InfoBoxText, "WP information saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + +VOID Do_Delete_WPRec(HWND hDlg) +{ + WPRec * WP; + int n; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to delete"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not deleted"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + for (n = CurrentWPIndex; n < NumberofWPrecs; n++) + { + WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries + } + + NumberofWPrecs--; + + SendDlgItemMessage(hDlg, IDC_WP, CB_RESETCONTENT, 0, 0); + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + + sprintf(InfoBoxText, "WP record for %s deleted", WP->callsign); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + free(WP); + + SaveWPDatabase(); + + return; + +} + +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, n; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDC_WP: + + // Msg Selection Changed + + Do_WP_Sel_Changed(hDlg); + + return TRUE; + + case IDC_SAVEWP: + + Do_Save_WPRec(hDlg); + return TRUE; + + case IDC_DELETEWP: + + Do_Delete_WPRec(hDlg); + return TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} +#endif + +VOID GetWPBBSInfo(char * Rline) +{ + // Update WP with /I records for each R: Line + + // R:111206/1636Z 29130@N9PMO.#SEWI.WI.USA.NOAM [Racine, WI] FBB7.00i + + struct tm rtime; + time_t RLineTime; + int Age; + + WPRec * WP; + char ATBBS[200]; + char Call[200]; + char QTH[200] = ""; + int RLen; + + char * ptr1; + char * ptr2; + + + memset(&rtime, 0, sizeof(struct tm)); + + if (Rline[10] == '/') + { + // Dodgy 4 char year + + sscanf(&Rline[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (Rline[8] == '/') + { + sscanf(&Rline[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + if ((RLineTime = mktime(&rtime)) != (time_t)-1 ) + { + Age = (time(NULL) - RLineTime)/86400; + + if ( Age < -1) + return; // in the future + + if (Age > BidLifetime || Age > MaxAge) + return; // Too old + } + + ptr1 = strchr(Rline, '@'); + ptr2 = strchr(Rline, '\r'); + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + + if (ptr2 == NULL) + return; // No CR on end + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + strcpy(Call, ATBBS); + strlop(Call, '.'); + + ptr2 = memchr(ptr1, '[', RLen); + + if (ptr2) + { + ptr1= memchr(ptr2, ']', RLen); + if (ptr1) + memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); + } + + if (_stricmp(Call, "RMS") == 0) + return; + + if (_stricmp(Call, "SYSTEM") == 0) + return; + + if (_stricmp(Call, "SWITCH") == 0) + return; + + if (_stricmp(Call, "SYSOP") == 0) + return; + + if (_memicmp(Call, "SMTP", 4) == 0) + return; + + if (_memicmp(Call, "SMTP:", 5) == 0) + return; + + if (_stricmp(Call, "AMPR") == 0) + return; + + if (_stricmp(Call, "FILE") == 0) + return; + + if (_memicmp(Call, "MCAST", 5) == 0) + return; + + WP = LookupWP(Call); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + if (QTH[0]) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + } + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'I'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime || WP->Type != 'I') + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + if (QTH[0] && strcmp(WP->secnd_qth , QTH) != 0) + { + strcpy(WP->secnd_qth, QTH); + WP->last_modif = RLineTime; + } + + return; +} + + + + +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) +{ + /* The /G suffix denotes that the information in this line has been gathered by examining + the header of a message to GUESS at which BBS the sender is registered. The HomeBBS of the User + is assumed to be the BBS shown in the first R: header line. The date associated with this + information is the date shown on this R: header line. + */ + + // R:930101/0000 1530@KA6FUB.#NOCAL.CA.USA.NOAM + + // R:930101/0000 @:KA6FUB.#NOCAL.CA.USA.NOAM #:1530 + + // The FirstRLine pointer points to the message, so shouldnt be changed + + WPRec * WP; + char ATBBS[200]; + int RLen; + + char * ptr1 = strchr(FirstRLine, '@'); + char * ptr2 = strchr(FirstRLine, '\r'); + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + if (strlen(ATBBS) > 40) + ATBBS[40] = 0; + + WP = LookupWP(From); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, From); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'G'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime) + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + return; +} + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FirstRLine) +{ + char * ptr1 = MailBuffer; + char * ptr2; + WPRec * WP; + char WPLine[200]; + int WPLen; + + ptr1[Size] = 0; + + ptr1 = FirstRLine; + + if (ptr1 == NULL) + return; + + while (*ptr1) + { + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == 0) // No CR in buffer + return; + + WPLen = ptr2 - ptr1; + + if ((memcmp(ptr1, "On ", 3) == 0) && (WPLen < 200)) + { + char * Date; + char * Call; + char * AT; + char * HA; + char * zip; + char * ZIP; + char * Name; + char * QTH = NULL; + char * Context; + char seps[] = " \r"; + + // Make copy of string, as strtok messes with it + + memcpy(WPLine, ptr1, WPLen); + WPLine[WPLen] = 0; + + Date = strtok_s(WPLine+3, seps, &Context); + Call = strtok_s(NULL, seps, &Context); + AT = strtok_s(NULL, seps, &Context); + HA = strtok_s(NULL, seps, &Context); + zip = strtok_s(NULL, seps, &Context); + ZIP = strtok_s(NULL, seps, &Context); + Name = strtok_s(NULL, seps, &Context); + QTH = strtok_s(NULL, "\r", &Context); // QTH may have spaces + + if (Date == 0 || Call == 0 || AT == 0 || ZIP == 0 || Name == 0 || QTH == 0) + return; + + if (strlen(HA) > 40) + return; + if (strlen(ZIP) > 8) + return; + if (strlen(Name) > 12) + return; + if (strlen(QTH) > 30) + return; + + if (AT[0] == '@' && (QTH)) + { + struct tm rtime; + time_t WPDate; + char Type; + char * TypeString; + + if (memcmp(Name, "?", 2) == 0) Name = NULL; + if (memcmp(ZIP, "?", 2) == 0) ZIP = NULL; + if (memcmp(QTH, "?", 2) == 0) QTH = NULL; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(Date, "%02d%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday); + + rtime.tm_year += 100; + rtime.tm_mon--; + +/* +This process freshens the database, following receipt of the new or changed information detailed above. + +The update subroutine will first look for an entry in the database for the callsign which matches the received information. +If it does not exist then a completely new record will be created in the database and the information be used to fill what +fields it can, in both the active and the temporary components. The date will be then changed to the one associated with the +update information. + +If the record does already exist, then the unknown fields of both the temporary and active fields will be filled in, and +those fields already known in the temporary part will be replaced by the new information if the date new information is +younger than that already on file. The date will then be +adjusted such that it is consistent with the updated information. + +If the new information is of the /U category, then the current fields will be replaced by the new information in both +the primary and secondary (Active and Temporary) parts of the record, as this information has been input directly from +the user. If the information was of another category then only the secondary (Temporary) part of the record will be +updated, so the Active or primary record will remain unchanged at this time. + +If a field is changed, a flag giving the update request type is then validated. If the /U flag is already validated, +it will not be replaced. This flag will be used in case the WP update messages are validated. +*/ + if ((WPDate = mktime(&rtime)) != (time_t)-1 ) + { + WPDate -= (time_t)_MYTIMEZONE; + TypeString = strlop(Call, '/'); + + if (strlen(Call) < 3 || strlen(Call) > 9) + return; + + if (TypeString) + Type = TypeString[0]; + else + Type = 'G'; + + if (strchr(Call, ':')) + break; + + if (_stricmp(Call, "RMS") == 0) + break; + + if (_stricmp(Call, "SWITCH") == 0) + break; + + if (_stricmp(Call, "SYSTEM") == 0) + break; + + if (_stricmp(Call, "SYSOP") == 0) + break; + + if (_memicmp(Call, "SMTP", 4) == 0) + break; + + if (_memicmp(Call, "SMTP:", 5) == 0) + break; + + if (_stricmp(Call, "AMPR") == 0) + break; + + if (_stricmp(Call, "FILE") == 0) + break; + + if (_memicmp(Call, "MCAST", 5) == 0) + break; + + WP = LookupWP(Call); + + if (WP) + { + // Found, so update + + DoWPUpdate(WP, Type, Name, HA, QTH, ZIP, WPDate); + } + else + { + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + if (Name) strcpy(WP->name, Name); + strcpy(WP->first_homebbs, HA); + strcpy(WP->secnd_homebbs, HA); + + if (QTH) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH);; + } + if (ZIP) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + } + + WP->Type = Type; + WP->last_modif = WPDate; + WP->last_seen = WPDate; + WP->changed = TRUE; + WP->seen++; + } + } + } + } + + ptr1 = ++ptr2; + if (*ptr1 == '\n') + ptr1++; + } + + SaveWPDatabase(); + + return; +} + +VOID DoWPUpdate(WPRec * WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate) +{ + // First Update any unknown field + + if(Name) + if (WP->name == NULL) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE;} + + if (QTH) + { + if (WP->first_qth == NULL) {strcpy(WP->first_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_qth == NULL) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + } + if (ZIP) + { + if (WP->first_zip == NULL) {strcpy(WP->first_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_zip == NULL) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + } + + WP->last_seen = WPDate; + WP->seen++; + + // Now Update Temp Fields if update is newer than original + + if (WP->last_modif >= WPDate) + return; + + if (Type == 'U') // Definitive Update + { + if (strcmp(WP->first_homebbs , HA) != 0) + { + strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->changed = TRUE; + } + + if (Name) + { + if (strcmp(WP->name , Name) != 0) + { + strcpy(WP->name, Name); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (QTH) + { + if (strcmp(WP->first_qth , QTH) != 0) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (ZIP) + { + if (strcmp(WP->first_zip , ZIP) != 0) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + WP->Type = Type; + + return; + } + + // Non-Definitive - only update second copy + + if (strcmp(WP->secnd_homebbs , HA) != 0) {strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->Type = Type;} + + if (Name) + if (strcmp(WP->name , Name) != 0) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->Type = Type;} + + if (QTH) + if (strcmp(WP->secnd_qth , QTH) != 0) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->Type = Type;} + + if (ZIP) + if (strcmp(WP->secnd_zip , ZIP) != 0) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->Type = Type;} + + return; +} + +VOID UpdateWPWithUserInfo(struct UserInfo * user) +{ + WPRec * WP = LookupWP(user->Call); + + if (strchr(user->Call, ':')) + return; + + if (_stricmp(user->Call, "RMS") == 0) + return; + + if (_stricmp(user->Call, "SYSTEM") == 0) + return; + + if (_stricmp(user->Call, "SWITCH") == 0) + return; + + if (_stricmp(user->Call, "SYSOP") == 0) + return; + + if (_memicmp(user->Call, "SMTP", 4) == 0) + return; + + if (_memicmp(user->Call, "SMTP:", 5) == 0) + return; + + if (_stricmp(user->Call, "AMPR") == 0) + return; + + if (_stricmp(user->Call, "FILE") == 0) + return; + + if (_stricmp(user->Call, "SYNC") == 0) + return; + + if (_memicmp(user->Call, "MCAST", 5) == 0) + return; + + if (_stricmp(user->Call, user->Name) == 0) + return; + + if (!WP) + { + WP = AllocateWPRecord(); + strcpy(WP->callsign, user->Call); + } + + // Update Record + + if (user->HomeBBS[0]) + { + strcpy(WP->first_homebbs, user->HomeBBS); + strcpy(WP->secnd_homebbs, user->HomeBBS); + } + + if (user->Address[0]) + { + char Temp[127]; + strcpy(Temp, user->Address); + Temp[30] = 0; + + strcpy(WP->first_qth, Temp); + strcpy(WP->secnd_qth, Temp); + } + + if (user->ZIP[0]) + { + strcpy(WP->first_zip, user->ZIP); + strcpy(WP->secnd_zip, user->ZIP ); + } + + if (user->Name[0]) + strcpy(WP->name, user->Name); + + WP->last_modif = WP->last_seen = time(NULL); + + WP->changed = TRUE; + WP->Type = 'U'; + + SaveWPDatabase(); + +} + +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Param) +{ + // Process the I call command + + WPRec * ptr = NULL; + int i; + char ATBBS[100]; + char * HA; + char * Rest; + + _strupr(Param); + Type = toupper(Type); + + switch (Type) + { + case 0: + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (wildcardcompare(ptr->callsign, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, + ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + return; + + case'@': // AT BBS + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + strlop(ATBBS,'.'); + + if (wildcardcompare(ATBBS, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + case'H': // Hierarchic Element + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + + Rest = strlop(ATBBS,'.'); + + if (Rest == 0) + continue; + + HA = strtok_s(Rest, ".", &Rest); + + while (HA) + { + if (wildcardcompare(HA, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + + HA = strtok_s(NULL, ".", &Rest); + } + } + return; + + case'Z': // ZIP + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (ptr->first_zip[0] == 0) + continue; + + if (wildcardcompare(ptr->first_zip, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + return; + } + nodeprintf(conn, "Invalid I command option %c\r", Type); +} +/* +On 111120 N4ZKF/I @ N4ZKF.#NFL.FL.USA.NOAM zip 32118 Dave 32955 +On 111120 F6IQF/I @ F6IQF.FRPA.FRA.EU zip ? ? f6iqf.dyndns.org +On 111121 W9JUN/I @ W9JUN.W9JUN.#SEIN.IN.US.NOAM zip 47250 Don NORTH VERNON, IN +On 111120 KR8ZY/U @ KR8ZY zip ? john ? +On 111120 N0DEC/G @ N0ARY.#NCA.CA.USA.NOAM zip ? ? ? + +From: N0JAL +To: WP +Type/Status: B$ +Date/Time: 22-Nov 10:15Z +Bid: 95F7N0JAL +Title: WP Update + +R:111122/1500Z 35946@KD6PGI.OR.USA.NOAM BPQ1.0.4 +R:111122/1020 16295@K7ZS.OR.USA.NOAM +R:111122/1015 38391@N0JAL.OR.USA.NOAM + +On 111121 N0JAL @ N0JAL.OR.USA.NOAM zip ? Sai Damascus, Oregon CN85sj + +*/ + +VOID UpdateWP() +{ + // If 2nd copy of info has been unchanged for 30 days, copy to active fields + + WPRec * ptr = NULL; + int i; + char * via = NULL; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + int WriteLen=0; + char HDest[61] = "WP"; + char WPMsgType = 'P'; + time_t NOW = time(NULL); + time_t UpdateLimit = NOW - (86400 * 30); // 30 days + LASTWPSendTime = NOW - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + // DO we have a new field, and if so is it different? + + if ((ptr->secnd_homebbs[0] && _stricmp(ptr->first_homebbs, ptr->secnd_homebbs)) + || (ptr->secnd_qth[0] && _stricmp(ptr->first_qth, ptr->secnd_qth)) + || (ptr->secnd_zip[0] && _stricmp(ptr->first_zip, ptr->secnd_zip))) + { + // Have new data + + if (ptr->last_modif < UpdateLimit) + { + // Stable for 30 days + + if (ptr->secnd_homebbs[0]) + strcpy(ptr->first_homebbs, ptr->secnd_homebbs); + if (ptr->secnd_qth[0]) + strcpy(ptr->first_qth, ptr->secnd_qth); + if (ptr->secnd_zip[0]) + strcpy(ptr->first_zip, ptr->secnd_zip); + + ptr->last_modif = NOW; + + } + } + } +} + +int CreateWPMessage() +{ + // Include all messages with Type of U whach have changed since LASTWPSendTime + + WPRec * ptr = NULL; + int i; + struct tm *tm; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + BIDRec * BIDRec; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + char ** To = SendWPAddrs; + + LASTWPSendTime = time(NULL) - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + +// if (ptr->last_modif > LASTWPSendTime && ptr->Type == 'U' && ptr->first_homebbs[0]) + if (ptr->changed && ptr->last_modif > LASTWPSendTime && ptr->first_homebbs[0]) + { + tm = gmtime(&ptr->last_modif); + MsgLen += sprintf(Buffptr, "On %02d%02d%02d %s/%c @ %s zip %s %s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, + ptr->callsign, ptr->Type, ptr->first_homebbs, + (ptr->first_zip[0]) ? ptr->first_zip : "?", + (ptr->name[0]) ? ptr->name : "?", + (ptr->first_qth[0]) ? ptr->first_qth : "?"); + + Buffptr = &MailBuffer[MsgLen]; + + ptr->changed = FALSE; + + if (MsgLen > 100000) + break; + } + } + + if (MsgLen == 0) + return TRUE; + + while(To[0]) + { + char TO[256]; + char * VIA; + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + Msg->length = MsgLen; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, BBSName); + + strcpy(TO, To[0]); + + VIA = strlop(TO, '@'); + + if (VIA) + { + if (strlen(VIA) > 40) + VIA[40] = 0; + strcpy(Msg->via, VIA); + } + strcpy(Msg->to, TO); + + strcpy(Msg->title, "WP Update"); + + Msg->type = (SendWPType) ? 'P' : 'B'; + Msg->status = 'N'; + + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + + To++; + } + + SaveMessageDatabase(); + SaveBIDDatabase(); + + return TRUE; +} + +VOID CreateWPReport() +{ + int i; + char Line[200]; + int len; + char File[100]; + FILE * hFile; + WPRec * WP = NULL; + + sprintf_s(File, sizeof(File), "%s/wp.txt", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + return; + +// len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); +// len = sprintf(Line, " In Out Rxed Sent Rxed Sent In Out\r\n\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); + + + for (i=1; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + + len = sprintf(Line, "%-7s,%c,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s\r\n", + WP->callsign, WP->Type, WP->first_homebbs, WP->first_qth, WP->first_zip, + WP->secnd_homebbs, WP->secnd_qth, WP->secnd_zip, WP->name, WP->changed, + FormatWPDate(WP->last_modif), + FormatWPDate(WP->last_seen)); + + fwrite(Line, 1, len, hFile); + } + fclose(hFile); +} + + + + + + diff --git a/WebMail.c b/WebMail.c new file mode 100644 index 0000000..aeddc40 --- /dev/null +++ b/WebMail.c @@ -0,0 +1,5963 @@ +/* +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(char * Title, char * UTF8Title); +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); + +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) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + Debugprintf("Recurse %s/%s/%s", FormSet, DirName, entry->d_name); + continue; + + } + // see if initial html + +// if (stristr(entry->d_name, "initial.html")) + { + // 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); + return 0; + } + + 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[128]; + 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 (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); + + ConvertTitletoUTF8(EncodedTitle, UTF8Title); + + 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, 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[128]; + 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 + + ConvertTitletoUTF8(Msg->title, UTF8Title); + + // 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); + } + } + } + + 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); + } + } + } + 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) + { + // With Windows it is simple - convert using current codepage + // I think the only reliable way is to convert to unicode and back + + size_t origlen = msgLen + 1; + + UCHAR * BufferB = malloc(2 * origlen); + +#ifdef WIN32 + + WCHAR * BufferW = malloc(2 * origlen); + int wlen; + int len = (int)origlen; + + wlen = MultiByteToWideChar(CP_ACP, 0, MsgBytes, len, BufferW, (int)(origlen * 2)); + len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, (int)(origlen * 2), NULL, NULL); + + free(Save); + Save = MsgBytes = BufferB; + free(BufferW); + msgLen = len - 1; // exclude NULL + +#else + int left = 2 * msgLen; + int len = msgLen; + UCHAR * BufferBP = BufferB; + iconv_t * icu = NULL; + + if (icu == NULL) + icu = iconv_open("UTF-8", "CP1252"); + + iconv(icu, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu, &MsgBytes, &len, (char ** __restrict__)&BufferBP, &left); + + free(Save); + Save = MsgBytes = BufferB; + msgLen = strlen(MsgBytes); + +#endif + + } + + + // 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); + } + } + } + } + else + { + ptr += sprintf(ptr, "File for Message %d not found\r", Number); + } + + if (DisplayHTML && stristr(Message, "html>")) + 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; + + memset(WebMail, 0, sizeof(WebMailInfo)); + + WebMail->Reply = SaveReply; + WebMail->RLen = SaveRlen; + + return; +} + + +void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen) +{ + char * URLParams = strlop(Key, '&'); + int ReplyLen; + char Appl = 'M'; + + // Webmail doesn't use the normal Mail Key. + + // webscript.js doesn't need a key + + if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0) + { + if (jsTemplate) + free(jsTemplate); + + jsTemplate = GetTemplateFromFile(2, "webscript.js"); + + ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate); + *RLen = ReplyLen; + return; + } + + // Neither do js or file downloads + + if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) + { + int FileSize; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + size_t ReadLen; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * FN = &NodeURL[16]; + char * fileBit = FN; + char * ext; + + UndoTransparency(FN); + ext = strchr(FN, '.'); + + sprintf(MsgFile, "%s/%s", BPQDirectory, FN); + + while (strchr(fileBit, '/')) + fileBit = strlop(fileBit, '/'); + + if (stat(MsgFile, &STAT) == -1) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + FormatTime2(FileTimeString, STAT.st_ctime); + FormatTime2(TimeString, time(NULL)); + + if (_stricmp(ext, ".htm") == 0 || _stricmp(ext, ".html") == 0 || _stricmp(ext, ".css") == 0 || _stricmp(ext, ".js") == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: text/css\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n%s", FileSize, TimeString, FileTimeString, MsgBytes); + } + else + { + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"%s\"\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n%s", FileSize, fileBit, TimeString, FileTimeString, MsgBytes); + + } + free (MsgBytes); + return; + } + + Session = NULL; + + if (Key && Key[0]) + Session = FindWMSession(Key); + + if (Session == NULL) + { + // Lost Session + + if (LOCAL) + { + Session = AllocateWebMailSession(); + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page unless Signon request + + if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0) + { + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/WebMail/Signon") == 0) + { + char * msg = strstr(input, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Msg[128]; + int n; + + if (msg) + { + struct UserInfo * User; + + if (strstr(msg, "Cancel=Cancel")) + { + *RLen = sprintf(Reply, ""); + return; + } + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + + Key = Session->Key; + + user = strtok_s(&msg[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + Session->User = User = LookupCall(user); + + if (User) + { + // Check Password + + if (password[0] && strcmp(User->pass, password) == 0) + { + // send Message Index + + Session->WebMailLastUsed = time(NULL); + Session->WebMailSkip = 0; + Session->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; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAll") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMB") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "B"); + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMP") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "P"); + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMT") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "T"); + Session->WebMailMine = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMMine") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = TRUE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + + if (_stricmp(NodeURL, "/WebMail/WMSame") == 0) + { + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + 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; + } + + *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; + } + + *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

" + "

", popup); + + *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 [] = + "" + "

", popup); + + *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 (EnableUI) + SendMsgUI(Msg); + + user = LookupCall(Msg->to); + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + HDest = HDestRest; + } + + *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt); + + SaveMessageDatabase(); + SaveBIDDatabase(); + FreeWebMailFields(WebMail); + free(HDestCopy); + + return; +} + + + + + + +// RMS Express Forms Support + +char * GetHTMLViewerTemplate(char * FN) +{ + int i, j, k, l; + + // Seach list of forms for base file (without .html) + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + for (j = 0; j < Dir->FormCount; j++) + { + if (strcmp(FN, Dir->Forms[j]->FileName) == 0) + { + return CheckFile(Dir, FN); + } + } + + if (Dir->DirCount) + { + for (l = 0; l < Dir->DirCount; l++) + { + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + { + if (strcmp(FN, Dir->Dirs[l]->Forms[k]->FileName) == 0) + { + return CheckFile(Dir, Dir->Dirs[l]->Forms[k]->FileName); + } + } + } + } + } + + 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; + } + + 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]; + + sprintf(popup, "%s

", popup); + + *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("