diff --git a/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base b/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base new file mode 100644 index 0000000..caa0cc8 --- /dev/null +++ b/.svn/pristine/0e/0e76009e1f1f816128476fe53b8ac9e78d519dea.svn-base @@ -0,0 +1,6764 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ +// +// 409l Oct 2001 Fix l3timeout for KISS +// +// 409m Oct 2001 Fix Crossband Digi +// +// 409n May 2002 Change error handling on load ext DLL + +// 409p March 2005 Allow Multidigit COM Ports (kiss.c) + +// 409r August 2005 Treat NULL string in Registry as use current directory +// Allow shutdown to close BPQ Applications + +// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 + +// 409t January 2006 +// +// Add API for Perl "GetPerlMsg" +// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine +// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" +// Add New API "GetBPQDirectory" - Returns location of config file +// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command +// Rename BPQNODES to BPQNODES.dat +// New API "GetAttachedProcesses" - returns number of processes connected. +// Warn if user trys to close Console Window. +// Add Debug entries to record Process Attach/Detach +// Fix recovery following closure of first process + +// 409t Beta 2 February 2006 +// +// Add API Entry "GetPortNumber" +// +// 409u February 2006 +// +// Fix crash if allocate/deallocate called with stream=0 +// Add API to ch +// Display config file path +// Fix saving of Locked Node flag +// Added SAVENODES SYSOP command +// +// 409u 2 March 2006 +// +// Fix SetupBPQDirectory +// Add CopyBPQDirectory (for Basic Programs) +// +// 409u 3 March 2006 +// +// Release streams on DLL unload + +// 409v October 2006 +// +// Support Minimize to Tray for all BPQ progams +// Implement L4 application callsigns + +// 410 November 2006 +// +// Modified to compile with C++ 2005 Express Edition +// Make MCOM MTX MMASK local variables +// +// 410a January 2007 +// +// Add program name to Attach-Detach messages +// Attempt to detect processes which have died +// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt +// Add BCALL - origin call for Beacons +// Fix KISS ACKMODE ACK processing +// + +// 410b November 2007 +// +// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary + +// 410c December 2007 + +// Fix problem with NT introduced in V410a +// Display location of DLL on Console + +// 410d January 2008 + +// Fix crash in DLL Init caused by long path to program +// Invoke Appl2 alias on C command (if enabled) +// Allow C command to be disabled +// Remove debug trap in GETRAWFRAME +// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem +// Move Port statup code out of DLLInit (mainly for perl) +// Changes to allow Load/Unload of bpq32.dll by appl +// CloseBPQ32 API added +// Ext Driver Close routes called +// Changes to release Mutex + +// 410e May 2008 + +// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) +// Fix VCOM Driver (RX Len was 1 byte too long) +// Fix possible crash on L4CODE if L4DACK received out of sequence +// Add basic IP decoding + +// 410f October 2008 + +// Add IP Gateway +// Add Multiport DIGI capability +// Add GetPortDescription API +// Fix potential hangs if RNR lost +// Fix problem if External driver failes to load +// Put pushad/popad round _INITIALISEPORTS (main.asm) +// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) +// Ensure Route Qual is updated if Port Qual changed +// Add Reload Option, plus menu items for DUMP and SAVENODES + +// 410g December 2008 + +// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) +// Fix changed init of BPQDirectory (accidentally changed) +// Fix Checks for lost processes (accidentally deleted) +// Support HDLC Cards on W2K and above +// Delete Tray List entries for crashed processes +// Add Option to NODES command to sort by Callsign +// Add options to save or clear BPQNODES before Reconfig. +// Fix Reconfig in Win98 +// Monitor buffering tweaks +// Fix Init for large (>64k) tables +// Fix Nodes count in Stats + +// 410h January 2009 + +// Add Start Minimized Option +// Changes to KISS for WIn98 Virtual COM +// Open \\.\com instead of //./COM +// Extra Dignostics + +// 410i Febuary 2009 + +// Revert KISS Changes +// Save Window positions + +// 410j June 2009 + +// Fix tidying of window List when program crashed +// Add Max Nodes to Stats +// Don't update APPLnALIAS with received NODES info +// Fix MH display in other timezones +// Fix Possible crash when processing NETROM type Zero frames (eg NRR) +// Basic INP3 Stuff +// Add extra diagnostics to Lost Process detection +// Process Netrom Record Route frames. + +// 410k June 2009 + +// Fix calculation of %retries in extended ROUTES display +// Fix corruption of ROUTES table + +// 410l October 2009 + +// Add GetVersionString API call. +// Add GetPortTableEntry API call +// Keep links to neighbouring nodes open + +// Build 2 + +// Fix PE in NOROUTETODEST (missing POP EBX) + +// 410m November 2009 + +// Changes for PACTOR and WINMOR to support the ATTACH command +// Enable INP3 if configured on a route. +// Fix count of nodes in Stats Display +// Overwrite the worst quality unused route if a call is received from a node not in your +// table when the table is full + +// Build 5 + +// Rig Control Interface +// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) + +// Build 6 + +// Fix reading INP3 Flag from BPQNODES + +// Build 7 + +// Add MAXHOPS and MAXRTT config options + +// Build 8 + +// Fix INP3 deletion of Application Nodes. +// Fix GETCALLSIGN for Pactor Sessions +// Add N Call* to display all SSID's of a call +// Fix flow control on Pactor sessions. + +// Build 9 + +// HDLC Support for XP +// Add AUTH routines + +// Build 10 + +// Fix handling commands split over more that one packet. + +// Build 11 + +// Attach cmd changes for winmor disconnecting state +// Option Interlock Winmor/Pactor ports + +// Build 12 + +// Add APPLS export for winmor +// Handle commands ending CR LF + +// Build 13 + +// Incorporate Rig Control in Kernel + +// Build 14 + +// Fix config reload for Rig COntrol + +// 410n March 2010 + +// Implement C P via PACTOR/WINMOR (for Airmail) + +// Build 2 + +// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR +// Fix resetting IDLE Timer on Pactor/WINMOR sessions +// Send L4 KEEPLI messages based on IDLETIME + +// 410o July 2010 + +// Read bpqcfg.txt instead of .bin +// Support 32 bit MMASK (Allowing 32 Ports) +// Support 32 bit _APPLMASK (Allowing 32 Applications) +// Allow more commands +// Allow longer command aliases +// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR +// Clear RIGControl RTS and DTR on close + +// 410o Build 2 August 2010 + +// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) +// Fix Kenwood Rig Control when more than one message received at once. +// Save minimzed state of Rigcontrol Window + +// 410o Build 3 August 2010 + +// Fix reporting of set errors in scan to a random session + +// 410o Build 4 August 2010 + +// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK +// Fix validation of TRANSDELAY + +// 410o Build 5 August 2010 + +// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) +// Add WINMOR and SCS Pactor mode control option to RigControl +// Extend INFOMSG to 2000 bytes +// Improve Scan freq change lock (check both SCS and WINMOR Ports) + +// 410o Build 6 September 2010 + +// Incorporate IPGateway in main code. +// Fix GetSessionInfo for Pactor/Winmor Ports +// Add Antenna Selection to RigControl +// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) + +// 410o Build 7 September 2010 + +// Move rigconrtol display to driver windows +// Move rigcontrol config to driver config. +// Allow driver and IPGateway config info in bpq32.cfg +// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll +// Add option to reread IP Gateway config. +// Fix Reinit after process with timer closes (error in TellSessions). + +// 410p Build 2 October 2010 + +// Move KAM and SCS drivers to bpq32.dll + +// 410p Build 3 October 2010 + +// Support more than one axip port. + +// 410p Build 4 October 2010 + +// Dynamically load psapi.dll (for 98/ME) + +// 410p Build 5 October 2010 + +// Incorporate TelnetServer +// Fix AXIP ReRead Config +// Report AXIP accept() fails to syslog, not a popup. + +// 410p Build 6 October 2010 + +// Includes HAL support +// Changes to Pactor Drivers disconnect code +// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param +// Config now checks for duplicate port definitions +// Add Node Map reporting +// Fix WINMOR deferred disconnect. +// Report Pactor PORTCALL to WL2K instead of RMS Applcall + +// 410p Build 7 October 2010 + +// Add In/Out flag to Map reporting, and report centre, not dial +// Write Telnet log to BPQ Directory +// Add Port to AXIP resolver display +// Send Reports to update.g8bpq.net:81 +// Add support for FT100 to Rigcontrol +// Add timeout to Rigcontrol PTT +// Add Save Registry Command + +// 410p Build 8 November 2010 + +// Add NOKEEPALIVES Port Param +// Renumbered for release + +// 410p Build 9 November 2010 + +// Get Bandwith for map report from WL2K Report Command +// Fix freq display for FT100 (was KHz, not MHz) +// Don't try to change SCS mode whilst initialising +// Allow reporting of Lat/Lon as well as Locator +// Fix Telnet Log Name +// Fix starting with Minimized windows when Minimizetotray isn't set +// Extra Program Error trapping in SessionControl +// Fix reporting same freq with different bandwidths at different times. +// Code changes to support SCS Robust Packet Mode. +// Add FT2000 to Rigcontrol +// Only Send CTEXT to connects to Node (not to connects to an Application Call) + +// Released as Build 10 + +// 410p Build 11 January 2011 + +// Fix MH Update for SCS Outgoing Calls +// Add Direct CMS Access to TelnetServer +// Restructure DISCONNECT processing to run in Timer owning process + +// 410p Build 12 January 2011 + +// Add option for Hardware PTT to use a different com port from the scan port +// Add CAT PTT for Yaesu 897 (and maybe others) +// Fix RMS Packet ports busy after restart +// Fix CMS Telnet with MAXSESSIONS > 10 + +// 410p Build 13 January 2011 + +// Fix loss of buffers in TelnetServer +// Add CMS logging. +// Add non - Promiscuous mode option for BPQETHER + +// 410p Build 14 January 2011 + +// Add support for BPQTermTCP +// Allow more that one FBBPORT +// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs +// Add session length to CMS Telnet logging. +// Return Secure Session Flag from GetConnectionInfo +// Show Uptime as dd/hh/mm + +// 4.10.16.17 March 2011 + +// Add "Close all programs" command +// Add BPQ Program Directory registry key +// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) +// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries +// Add support for SCS Tracker HF Modes +// Fix WL2K Reporting +// Report Version to WL2K +// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) + + +// Above released as 5.0.0.1 + +// 5.2.0.1 + +// Add caching of CMS Server IP addresses +// Initialise TNC State on Pactor Dialogs +// Add Shortened (6 digit) AUTH mode. +// Update MH with all frames (not just I/UI) +// Add IPV6 Support for TelnetServer and AXIP +// Fix TNC OK Test for Tracker +// Fix crash in CMS mode if terminal disconnects while tcp commect in progress +// Add WL2K reporting for Robust Packet +// Add option to suppress WL2K reporting for specific frequencies +// Fix Timeband processing for Rig Control +// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access +// New Driver for V4 TNC + +// 5.2.1.3 October 2011 + +// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) +// Improved program error logging +// WL2K reporting changed to new format agreed with Lee Inman + +// 5.2.3.1 January 2012 + +// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. +// Fix reporting of Tracker freqs to WL2K. +// Fix Tracker monitoring setup (sending M UISC) +// Fix possible call/application routing error on RP +// Changes for P4Dragon +// Include APRS Digi/IGate +// Tracker monitoring now includes DIGIS +// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers +// Include driver for UZ7HO soundcard modem. +// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal +// No longer supports separate config files, or BPQTELNETSERVER.exe +// Improved flow control for Telnet CMS Sessions +// Fix handling Config file without a newline after last line +// Add non - Promiscuous mode option for BPQETHER +// Change Console Window to a Dialog Box. +// Fix possible corruption and loss of buffers in Tracker drivers +// Add Beacon After Session option to Tracker and UZ7HO Drivers +// Rewrite RigControl and add "Reread Config Command" +// Support User Mode VCOM Driver for VKISS ports + +// 5.2.4.1 January 2012 + +// Remove CR from Telnet User and Password Prompts +// Add Rigcontrol to UZ7HO driver +// Fix corruption of Free Buffer Count by Rigcontol +// Fix WINMOR and V4 PTT +// Add MultiPSK Driver +// Add SendBeacon export for BPQAPRS +// Add SendChatReport function +// Fix check on length of Port Config ID String with trailing spaces +// Fix interlock when Port Number <> Port Slot +// Add NETROMCALL for L3 Activity +// Add support for APRS Application +// Fix Telnet with FBBPORT and no TCPPORT +// Add Reread APRS Config +// Fix switching to Pactor after scanning in normal packet mode (PTC) + +// 5.2.5.1 February 2012 + +// Stop reading Password file. +// Add extra MPSK commands +// Fix MPSK Transparency +// Make LOCATOR command compulsory +// Add MobileBeaconInterval APRS param +// Send Course and Speed when APRS is using GPS +// Fix Robust Packet reporting in PTC driver +// Fix corruption of some MIC-E APRS packets + +// 5.2.6.1 February 2012 + +// Convert to MDI presentation of BPQ32.dll windows +// Send APRS Status packets +// Send QUIT not EXIT in PTC Init +// Implement new WL2K reporting format and include traffic reporting info in CMS signon +// New WL2KREPORT format +// Prevent loops when APPL alias refers to itself +// Add RigControl for Flex radios and ICOM IC-M710 Marine radio + +// 5.2.7.1 + +// Fix opening more thn one console window on Win98 +// Change method of configuring multiple timelots on WL2K reporting +// Add option to update WK2K Sysop Database +// Add Web server +// Add UIONLY port option + +// 5.2.7.2 + +// Fix handling TelnetServer packets over 500 bytes in normal mode + +// 5.2.7.3 + +// Fix Igate handling packets from UIView + +// 5.2.7.4 + +// Prototype Baycom driver. + +// 5.2.7.5 + +// Set WK2K group ref to MARS (3) if using a MARS service code + +// 5.2.7.7 + +// Check for programs calling CloseBPQ32 when holding semaphore +// Try/Except round Status Timer Processing + +// 5.2.7.8 + +// More Try/Except round Timer Processing + +// 5.2.7.9 + +// Enable RX in Baycom, and remove test loopback in tx + +// 5.2.7.10 + +// Try/Except round ProcessHTTPMessage + +// 5.2.7.11 + +// BAYCOM tweaks + +// 5.2.7.13 + +// Release semaphore after program error in Timer Processing +// Check fro valid dest in REFRESHROUTE + + +// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( + +// Version 5.2.9.1 Sept 2012 + +// Fix using KISS ports with COMn > 16 +// Add "KISS over UDP" driver for PI as a TNC concentrator + +// Version 6.0.1.1 + +// Convert to C for linux portability +// Try to speed up kiss polling + +// Version 6.0.2.1 + +// Fix operation on Win98 +// Fix callsign error with AGWtoBPQ +// Fix PTT problem with WINMOR +// Fix Reread telnet config +// Add Secure CMS signon +// Fix error in cashing addresses of CMS servers +// Fix Port Number when using Send Raw. +// Fix PE in KISS driver if invalid subchannel received +// Fix Orignal address of beacons +// Speed up Telnet port monitoring. +// Add TNC Emulators +// Add CountFramesQueuedOnStream API +// Limit number of frames that can be queued on a session. +// Add XDIGI feature +// Add Winmor Robust Mode switching for compatibility with new Winmor TNC +// Move most APRS code from BPQAPRS to here +// Stop corruption caused by overlong KISS frames + +// Version 6.0.3.1 + +// Add starting/killing WINMOR TNC on remote host +// Fix Program Error when APRS Item or Object name is same as call of reporting station +// Dont digi a frame that we have already digi'ed +// Add ChangeSessionIdleTime API +// Add WK2KSYSOP Command +// Add IDLETIME Command +// Fix Errors in RELAYAPPL processing +// Fix PE cauaed by invalid Rigcontrol Line + +// Version 6.0.4.1 + +// Add frequency dependent autoconnect appls for SCS Pactor +// Fix DED Monitoring of I and UI with no data +// Include AGWPE Emulator (from AGWtoBPQ) +// accept DEL (Hex 7F) as backspace in Telnet +// Fix re-running resolver on re-read AXIP config +// Speed up processing, mainly for Telnet Sessions +// Fix APRS init on restart of bpq32.exe +// Change to 2 stop bits +// Fix scrolling of WINMOR trace window +// Fix Crash when ueing DED TNC Emulator +// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions +// Allow HOST applications even when CMS option is disabled +// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) + +// Version 6.0.5.1 January 2014 + +// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) +// Add "Clear" option to MH command +// Add "Connect to RMS Relay" Option +// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control +// Fix routing of first call in Robust Packet +// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) +// Add RTS>CAT PTT option for Sound Card rigs +// Add Clear Nodes Option (NODE DEL ALL) +// SCS Pactor can set differeant APPLCALLS when scanning. +// Fix possible Scan hangup after a manual requency change with SCS Pactor +// Accept Scan entry of W0 to disable WINMOR on that frequency +// Fix corruption of NETROMCALL by SIMPLE config command +// Enforce Pactor Levels +// Add Telnet outward connect +// Add Relay/Trimode Emulation +// Fix V4 Driver +// Add PTT Mux +// Add Locked ARP Entries (via bpq32.cfg) +// Fix IDLETIME node command +// Fix STAY param on connect +// Add STAY option to Attach and Application Commands +// Fix crash on copying a large AXIP MH Window +// Fix possible crash when bpq32.exe dies +// Fix DIGIPORT for UI frames + +// Version 6.0.6.1 April 2014 + +// FLDigi Interface +// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. +// Validate INP3 messages to try to prevent crash +// Fix possible crash if an overlarge KISS frame is received +// Fix error in AXR command +// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line +// Add CBELL to TNC21 emulator +// Add sent objects and third party messages to APRS Dup List +// Incorporate UIUtil +// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 +// Improvements to FLDIGI interlocking +// Fix TNC State Display for Tracker +// Cache CMS Addresses on LinBPQ +// Fix count error on DED Driver when handling 256 byte packets +// Add basic SNMP interface for MRTG +// Fix memory loss from getaddrinfo +// Process "BUSY" response from Tracker +// Handle serial port writes that don't accept all the data +// Trap Error 10038 and try to reopen socket +// Fix crash if overlong command line received + +// Version 6.0.7.1 Aptil 2014 +// Fix RigContol with no frequencies for Kenwood and Yaesu +// Add busy check to FLDIGI connects + +// Version 6.0.8.1 August 2014 + +// Use HKEY_CURRENT_USER on all OS versions +// Fix crash when APRS symbol is a space. +// Fixes for FT847 CAT +// Fix display of 3rd byte of FRMR +// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver +// Fix possible memory corruption in WINMOR driver +// Fix FT2000 Modes +// Use new WL2K reporting system (Web API Based) +// APRS Server now cycles through hosts if DNS returns more than one +// BPQ32 can now start and stop FLDIGI +// Fix loss of AXIP Resolver when running more than one AXIP port + +// Version 6.0.9.1 November 2014 + +// Fix setting NOKEEPALIVE flag on route created from incoming L3 message +// Ignore NODES from locked route with quality 0 +// Fix seting source port in AXIP +// Fix Dual Stack (IPV4/V6) on Linux. +// Fix RELAYSOCK if IPv6 is enabled. +// Add support for FT1000 +// Fix hang when APRS Messaging packet received on RF +// Attempt to normalize Node qualies when stations use widely differing Route qualities +// Add NODES VIA command to display nodes reachable via a specified neighbour +// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) +// Fix buffering large messages in Telnet Host API +// Fix occasional crash in terminal part line processing +// Add "NoFallback" command to Telnet server to disable "fallback to Relay" +// Improved support for APPLCALL scanning with Pactor +// MAXBUFFS config statement is no longer needed. +// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails +// Implement LISTEN and CQ commands +// FLDIGI driver can now start FLDIGI on a remote system. +// Add IGNOREUNLOCKEDROUTES parameter +// Fix error if too many Telnet server connections + +// Version 6.0.10.1 Feb 2015 + +// Fix crash if corrupt HTML request received. +// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. +// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins +// Add INP3ONLY flag. +// Fix program error if enter UNPROTO without a destination path +// Show client IP address on HTTP sessions in Telnet Server +// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set +// Add FT990 RigControl support, fix FT1000MP support. +// Support ARMV5 processors +// Changes to support LinBPQ APRS Client +// Add IC7410 to supported Soundcard rigs +// Add CAT PTT to NMEA type (for ICOM Marine Radios_ +// Fix ACKMODE +// Add KISS over TCP +// Support ACKMode on VKISS +// Improved reporting of configuration file format errors +// Experimental driver to support ARQ sessions using UI frames + +// Version 6.0.11.1 September 2015 + +// Fixes for IPGateway configuration and Virtual Circuit Mode +// Separate Portmapper from IPGateway +// Add PING Command +// Add ARDOP Driver +// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) +// Add "VeryOldMode" for KAM Version 5.02 +// Add KISS over TCP Slave Mode. +// Support Pactor and Packet on P4Dragon on one port +// Add "Remote Staton Quality" to Web ROUTES display +// Add Virtual Host option for IPGateway NET44 Encap +// Add NAT for local hosts to IPGateway +// Fix setting filter from RADIO command for IC7410 +// Add Memory Channel Scanning for ICOM Radios +// Try to reopen Rig Control port if it fails (could be unplugged USB) +// Fix restoring position of Monitor Window +// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) +// Support APRS beacons in RP mode on Dragon// +// Change Virtual MAC address on IPGateway to include last octet of IP Address +// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode +// Fix sending I frames before L2 session is up +// Fix Flow control on Telnet outbound sessions. +// Fix reporting of unterminatred comments in config +// Add option for RigControl to not change mode on FT100/FT990/FT1000 +// Add "Attach and Connect" for Telnet ports + +// Version 6.0.12.1 November 2015 + +// Fix logging of IP addresses for connects to FBBPORT +// Allow lower case user and passwords in Telnet "Attach and Connect" +// Fix possible hang in KISS over TCP Slave mode +// Fix duplicating LinBPQ process if running ARDOP fails +// Allow lower case command aliases and increase alias length to 48 +// Fix saving long IP frames pending ARP resolution +// Fix dropping last entry from a RIP44 message. +// Fix displaying Digis in MH list +// Add port name to Monitor config screen port list +// Fix APRS command display filter and add port filter +// Support port names in BPQTermTCP Monitor config +// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog +// Buffer Web Mgmt Edit Config output +// Add WebMail Support +// Fix not closing APRS Send WX file. +// Add RUN option to APRS Config to start APRS Client +// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 +// Close and reopen ARDOP connection if nothing received for 90 secs +// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) +// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode + +// 6.0.13.1 + +// Allow /ex to exit UNPROTO mode +// Support ARQBW commands. +// Support IC735 +// Fix sending ARDOP beacons after a busy holdoff +// Enable BPQDED driver to beacon via non-ax.25 ports. +// Fix channel number in UZ7HO monitoring +// Add SATGate mode to APRSIS Code. +// Fix crash caused by overlong user name in telnet logon +// Add option to log L4 connects +// Add AUTOADDQuiet mode to AXIP. +// Add EXCLUDE processing +// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux +// Convert calls in MAP entries to upper case. +// Support Linux COM Port names for APRS GPS +// Fix using NETROM serial protocol on ASYNC Port +// Fix setting MYLEVEL by scanner after manual level change. +// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic +// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command +// Add Multicast RX interface to FLDIGI Driver +// Fix processing application aliases to a connect command. +// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it +// Save backups of bpq32.cfg when editing with Web interface and report old and new length +// Add DD command to SCS Pactor, and use it for forced disconnect. +// Add ARDOP mode select to scan config +// ARDOP changes for ARDOP V 0.5+ +// Flip SSID bits on UZ7HO downlink connects + + +// Version 6.0.14.1 + +// Fix Socket leak in ARDOP and FLDIGI drivers. +// Add option to change CMS Server hostname +// ARDOP Changes for 0.8.0+ +// Discard Terminal Keepalive message (two nulls) in ARDOP command hander +// Allow parameters to be passed to ARDOP TNC when starting it +// Fix Web update of Beacon params +// Retry connects to KISS ports after failure +// Add support for ARDOP Serial Interface Native mode. +// Fix gating APRS-IS Messages to RF +// Fix Beacons when PORTNUM used +// Make sure old monitor flag is cleared for TermTCP sessions +// Add CI-V antenna control for IC746 +// Don't allow ARDOP beacons when connected +// Add support for ARDOP Serial over I2C +// Fix possble crash when using manual RADIO messages +// Save out of sequence L2 frames for possible reuse after retry +// Add KISS command to send KISS control frame to TNC +// Stop removing unused digis from packets sent to APRS-IS + +// Processing of ARDOP PING and PINGACK responses +// Handle changed encoding of WL2K update responses. +// Allow anonymous logon to telnet +// Don't use APPL= for RP Calls in Dragon Single mode. +// Add basic messaging page to APRS Web Server +// Add debug log option to SCSTracker and TrkMulti Driver +// Support REBOOT command on LinBPQ +// Allow LISTEN command on all ports that support ax.25 monitoring + +// Version 6.0.15.1 Feb 2018 + +// partial support for ax.25 V2.2 +// Add MHU and MHL commands and MH filter option +// Fix scan interlock with ARDOP +// Add Input source seiect for IC7300 +// Remove % transparency from web terminal signon message +// Fix L4 Connects In count on stats +// Fix crash caused by corrupt CMSInfo.txt +// Add Input peaks display to ARDOP status window +// Add options to show time in local and distances in KM on APRS Web pages +// Add VARA support +// Fix WINMOR Busy left set when port Suspended +// Add ARDOP-Packet Support +// Add Antenna Switching for TS 480 +// Fix possible crash in Web Terminal +// Support different Code Pages on Console sessions +// Use new Winlink API interface (api.winlink.org) +// Support USB/ACC switching on TS590SG +// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. +// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. +// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs +// Fix timing out ARDOP PENDING Lock +// Support mixed case WINLINK Passwords +// Add TUNE and POWER Rigcontol Commands for some radios +// ADD LOCALTIME and DISPKM options to APRS Digi/Igate + +// 6.0.16.1 March 2018 + +// Fix Setting data mode and filter for IC7300 radios +// Add VARA to WL2KREPORT +// Add trace to SCS Tracker status window +// Fix possible hang in IPGATEWAY +// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. +// Fix sending CTEXT on WINMOR sessions + +// 6.0.17.1 November 2018 + +// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA +// Add Abort Connection to WINMOR and VARA Interfaces +// Reinstate accidentally removed CMS Access logging +// Fix MH CLEAR +// Fix corruption of NODE table if NODES received from station with null alias +// Fix loss of buffer if session closed with something in PARTCMDBUFFER +// Fix Spurious GUARD ZONE CORRUPT message in IP Code. +// Remove "reread bpq32.cfg and reconfigure" menu options +// Add support for PTT using CM108 based soundcard interfaces +// Datestamp Telnet log files and delete old Telnet and CMSAcces logs + +// 6.0.18.1 January 2019 + +// Fix validation of NODES broadcasts +// Fix HIDENODES +// Check for failure to reread config on axip reconfigure +// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port +// Send Beacons from BCALL or PORTCALL if configured +// Fix possible corruption of last entry in MH display +// Ensure RTS/DTR is down when opening PTT Port +// Remove RECONFIG command +// Preparations for 64 bit version + +// 6.0.19 Sept 2019 +// Fix UZ7HO interlock +// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) +// Add option to save and restore MH lists and SAVEMH command +// Add Frequency (if known) to UZ7HO MH lists +// Add Gateway option to Telnet for PAT +// Try to fix SCS Tracker recovery +// Ensure RTS/DTR is down on CAT port if using that line for PTT +// Experimental APRS Messaging in Kernel +// Add Rigcontrol on remote PC's using WinmorControl +// ADD VARAFM and VARAFM96 WL2KREPORT modes +// Fix WL2K sysop update for new Winlink API +// Fix APRS when using PORTNUM higher than the number of ports +// Add Serial Port Type +// Add option to linbpq to log APRS-IS messages. +// Send WL2K Session Reports +// Drop Tunneled Packets from 44.192 - 44.255 +// Log incoming Telnet Connects +// Add IPV4: and IPV6: overrides on AXIP Resolver. +// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) +// Add RADIO FREQ command to display current frequency + +// 6.0.20 April 2020 + +// Trap and reject YAPP file transfer request. +// Fix possible overrun of TCP to Node Buffer +// Fix possible crash if APRS WX file doesn't have a terminating newline +// Change communication with BPQAPRS.exe to restore old message popup behaviour +// Preparation for 64 bit version +// Improve flow control on SCS Dragon +// Fragment messages from network links to L2 links with smaller paclen +// Change WL2K report rate to once every two hours +// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator +// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) +// Add links to Ports Web Manangement Page to open individual Driver windows +// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers +// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. +// Don't try to send WL2K Traffic report if Internet is down +// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server +// ADD AGWAPPL config command to set application number. AGWMASK is still supported +// Register Node Alias with UZ7HO Driver +// Register calls when UZ7HO TNC Restarts and at intervals afterwards +// Fix crash when no IOADDR or COMPORT in async port definition +// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 +// Only apply BBSFLAG=NOBBS to APPPLICATION 1 +// Add RIGREONFIG command +// fix APRS RECONFIG on LinBPQ +// Fix Web Terminal scroll to end problem on some browsers +// Add PTT_SETS_INPUT option for IC7600 +// Add TELRECONFIG command to reread users or whole config +// Enforce PACLEN on UZ7HO ports +// Fix PACLEN on Command Output. +// Retry axip resolver if it fails at startup +// Fix AGWAPI connect via digis +// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers +// Limit APRS OBJECT length to 80 chars +// UZ7HO disconnect incoming call if no free streams +// Improve response to REJ (no F) followed by RR (F). +// Try to prevent more than MAXFRAME frames outstanding when transmitting +// Allow more than one instance of APRS on Linux +// Stop APRS digi by originating station +// Send driver window trace to main monitor system +// Improve handling of IPOLL messages +// Fix setting end of address bit on dest call on connects to listening sessions +// Set default BBS and CHAT application number and number of streams on LinBPQ +// Support #include in bpq32.cfg processing + +// Version 6.0.21 14 December 2020 + +// Fix occasional missing newlines in some node command reponses +// More 64 bit fixes +// Add option to stop setting PDUPLEX param in SCSPACTOR +// Try to fix buffer loss +// Remove extra space from APRS position reports +// Suppress VARA IAMALIVE messages +// Add display and control of QtSoundModem modems +// Only send "No CMS connection available" message if fallbacktorelay is set. +// Add HAMLIB backend and emulator support to RIGCONTROL +// Ensure all beacons are sent even with very short beacon intervals +// Add VARA500 WL2K Reporting Mode +// Fix problem with prpcessing frame collector +// Temporarily disable L2 and L4 collectors till I can find problem +// Fix possible problem with interactive RADIO commands not giving a response, +// Incease maximum length of NODE command responses to handle maximum length INFO message, +// Allow WL2KREPORT in CONFIG section of UZ7HO port config. +// Fix program error in processing hamlib frame +// Save RestartAfterFailure option for VARA +// Check callsign has a winlink account before sending WL2KREPORT messages +// Add Bandwidth control to VARA scanning +// Renable L2 collector +// Fix TNCPORT reconnect on Linux +// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases +// Add option to suppress sending call to application in Telnet HOST API +// Add FT991A support to RigControl +// Use background.jpg for Edit Config page +// Send OK response to SCS Pactor commands starting with # +// Resend ICOM PTT OFF command after 30 seconds +// Add WXCall to APRS config +// Fixes for AEAPactor +// Allow PTTMUX to use real or com0com com ports +// Fix monitoring with AGW Emulator +// Derive approx position from packets on APRS ports with a valid 6 char location +// Fix corruption of APRS message lists if the station table fills up. +// Don't accept empty username or password on Relay sessions. +// Fix occasional empty Nodes broadcasts +// Add Digis to UZ7HO Port MH list +// Add PERMITTEDAPPLS port param +// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. +// Fix handling AX/IP (proto 93) frames +// Fix possible corruption sending APRS messages +// Allow Telnet connections to be made using Connect command as well as Attach then Connect +// Fix Cancel Sysop Signin +// Save axip resolver info and restore on restart +// Add Transparent mode to Telnet Server HOST API +// Fix Tracker driver if WL2KREPRRT is in main config section +// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. +// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon +// Fix crash in Web terminal when processing very long lines + +// Version 6.0.22.1 August 2021 + +// Fix bug in KAM TNCEMULATOR +// Add WinRPR Driver (DED over TCP) +// Fix handling of VARA config commands FM1200 and FM9600 +// Improve Web Termanal Line folding +// Add StartTNC to WinRPR driver +// Add support for VARA2750 Mode +// Add support for VARA connects via a VARA Digipeater +// Add digis to SCSTracker and WinRPR MHeard +// Separate RIGCONTROL config from PORT config and add RigControl window +// Fix crash when a Windows HID device doesn't have a product_string +// Changes to VARA TNC connection and restart process +// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. +// Fix saving part lines in adif log and Winlink Session reporting +// Add port specific CTEXT +// Add FRMR monitoring to UZ7HO driver +// Add audio input switching for IC7610 +// Include Rigcontrol Support for IC-F8101E +// Process any response to KISS command +// Fix NODE ADD command +// Add noUpdate flag to AXIP MAP +// Fix clearing NOFALLBACK flag in Telnet Server +// Allow connects to RMS Relay running on another host +// Allow use of Power setting in Rigcontol scan lines for Kenwood radios +// Prevent problems caused by using "CMS" as a Node Alias +// Include standard APRS Station pages in code +// Fix VALIDCALLS processing in HF drivers +// Send Netrom Link reports to Node Map +// Add REALTELNET mode to Telnet Outward Connect +// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST +// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection +// Fix long (> 60 seconds) scan intervals +// Improved debugging of stuck semaphores +// Fix potential securiby bug in BPQ Web server +// Send Chat Updates to chatupdate.g8bpq.net port 81 +// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY +// Add experimental Mode reporting +// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY +// Add UPNP Support + +// Version 6.0.23.1 June 2022 + +// Add option to control which applcalls are enabled in VARA +// Add support for rtl_udp to Rig Control +// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal +// Allow setting css styles for Web Terminal +// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows +// More flexible RigControl for split frequency operation, eg for QO100 +// Increase stack size for ProcessHTMLMessage (.11) +// Fix HTML Content-Type on images (.12) +// Add AIS and ADSB Support (.13) +// Compress web pages (.14) +// Change minidump routine and close after program error (.15) +// Add RMS Relay SYNC Mode (.17) +// Changes for compatibility with Winlink Hybrid +// Add Rigcontrol CMD feature to Yaesu code (21) +// More diagnostic code +// Trap potential buffer overrun in ax/tcp code +// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail +// Add FLRIG as backend for RigControl (.24) +// Fix bug in compressing some management web pages +// Fix bugs in AGW Emulator (.25) +// Add more PTT_Sets_Freq options for split frequency working (.26) +// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) +// Fix Telnet negotiation and backspace processing (.29) +// Fix VARA Mode change when scanning (.30) +// Add Web Mgmt Log Display (.33) +// Fix crash when connecting to RELAY when CMS=0 (.36) +// Send OK to user for manual freq changes with hamlib or flrig +// Fix Rigcontrol leaving port disabled when using an empty timeband +// Fix processing of backspace in Telnet character processing (.40) +// Increase max size of connect script +// Fix HAMLIB Slave Thread control +// Add processing of VARA mode responses and display of VARA Mode (41) +// Fix crash when VARA session aborted on LinBPQ (43) +// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) +// Include APRS Map web page +// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) +// Add Basic DRATS interface (46) +// Fix MYCALLS on VARA (49) +// Add FreeData driver (51) +// Add additonal Rigcontrol options for QO100 (51) +// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) +// Fix sending large compressed web messages (52) +// Fix freq display when using flrig or hamlib backends to rigcontrol +// Change VARA Driver to send ABORT when Session Time limit expires +// Add Chat Log to Web Logs display +// Fix possible buffer loss in RigControl +// Allow hosts on local lan to be treated as secure +// Improve validation of data sent to Winlink SessionAdd API call +// Add support for FreeDATA modem. +// Add GetLOC API Call +// Change Leaflet link in aprs map. +// Add Connect Log (64) +// Fix crash when Resolve CMS Servers returns ipv6 addresses +// Fix Reporting P4 sessions to Winlink (68) +// Add support for FreeBSD (68) +// Fix Rigcontrol PTCPORT (69) +// Set TNC Emulator sessions as secure (72) +// Fix not always detecting loss of FLRIG (73) +// Add ? and * wildcards to NODES command (74) +// Add Port RADIO config parameter (74) + +// Version 6.0.24.1 August 2023 + +// Apply NODES command wildcard to alias as well a call (2) +// Add STOPPORT/STARTPORT to VARA Driver (2) +// Add bandwidth setting to FLRIG interface. (2) +// Fix N VIA (3) +// Fix NODE ADD and NODE DEL (4) +// Improvements to FLRIG Rigcontrol backend (6, 7) +// Fix UZ7HO Window Title Update +// Reject L2 calls with a blank from call (8) +// Update WinRPR Window header with BPQ Port Description (8) +// Fix error in blank call code (9) +// Change web buttons to white on black when pressed (10) +// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) +// Add RADIO PTT command for testing PTT (11) +// Fix using APPLCALLs on SCSTracker RP call (12) +// Add Rigcntol Web Page (13) +// Fix scan bandwidth change with ARDOPOFDM (13) +// Fix setting Min Pactor Level in SCSPactor (13) +// Fix length of commands sent via CMD_TO_APPL flag (14) +// Add filter by quality option to N display (15) +// Fix VARA Mode reporting to WL2K (16) +// Add FLRIG POWER and TUNE commands (18) +// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) +// FLDIGI improvements (19) +// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) +// Fix processing C command if first port driver is SCSPACTROR (20) +// Fix crash in UZ7HO driver if bad raw frame received (21) +// Fix using FLARQ chat mode with FLDIGI ddriover (22) +// Fix to KISSHF driver (23) +// Fix for application buffer loss (24) +// Add Web Sockets auto-refresh option for Webmail index page (25) +// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) +// Add SmartID for bridged frames - Send ID only if packets sent recently (26) +// Add option to save and restore received APRS messages (27) +// Add mechanism to run a user program on certain events (27) +// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) +// Add Node Help command (28) +// Add APRS Igate RXOnly option (29) +// Fix RMC message handling with prefixes other than GP (29) +// Add GPSD support for APRS (30) +// Attempt to fix Tracker/WinRPR reconnect code (30) +// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) +// Fix interactive commands in tracker driver (33) +// Fix SESSIONTIMELIMIT processing +// Add STOPPORT/STARTPORT for UZ7HO driver +// Fix processing of extended QtSM 'g' frame (36) +// Allow setting just freq on Yaseu rigs (37) +// Enable KISSHF driver on Linux (40) +// Allow AISHOST and ADSBHOST to be a name as well as an address (41) +// Fix Interlock of incoming UZ7HO connections (41) +// Disable VARA Actions menu if not sysop (41) +// Fix Port CTEXT on UZ7HO B C or D channels (42) +// Fix repeated trigger of SessionTimeLimit (43) +// Fix posible memory corruption in UpateMH (44) +// Add PHG to APRS beacons (45) +// Dont send DM to stations in exclude list(45) +// Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) +// Switch APRS Map to my Tiles (52) +// Fix using ; in UNPROTO Mode messages (52) +// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) +// Fix TNC Emulator Monitoring (53) +// Fix attach and connect on Telnet port bug introduced in .55 (56) +// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) +// Fix stack size in beginthread for MAC (58) +// Add NETROM over VARA (60) +// Add Disconnect Script (64) +// Add node commands to set UZ7HO modem mode and freq (64) +// Trap empty NODECALL or NETROMCALL(65) +// Trap NODES messages with empty From Call (65) +// Add RigControl for SDRConsole (66) +// Fix FLRig crash (66) +// Fix VARA disconnect handling (67) +// Support 64 ports (69) +// Fix Node commands for setting UZ7HO Modem (70) +// Fix processing SABM on an existing session (71) +// Extend KISS Node command to send more than one parameter byte (72) +// Add G7TAJ's code to record activity of HF ports for stats display (72) +// Add option to send KISS command to TNC on startup (73) +// Fix Bug in DED Emulator Monitor code (74) +// Add Filters to DED Monitor code (75) +// Detect loss of DED application (76) +// Fix connects to Application Alias with UZ7HO Driver (76) +// Fix Interlock of ports on same UZ7HO modem. (76) +// Add extended Ports command (77) +// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) +// Fix Web Terminal (80) +// Trap ENCRYPTION message from VARA (81) +// Fix processing of the Winlink API /account/exists response (82) +// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set + +// Version 6.0.25.? + +// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers +// Add Chat PACLEN config (5) +// Fix NC to Application Call (6) +// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) +// Get Beacon config from config file on Windows (9) +// fix processing DED TNC Emulator M command with space between M and params (10) +// Fix sending UI frames on SCSPACTOR (11) +// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) +// Add SDRAngel rig control (11) +// Add option to specify config and data directories on linbpq (12) +// Allow zero resptime (send RR immediately) (13) +// Make sure CMD bit is set on UI frames +// Add setting Modem Flags in QtSM AGW mode +// If FT847 om PTC Port send a "Cat On" command (17) +// Fix some 63 port bugs in RigCOntrol (17) +// Fix 63 port bug in Bridging (18) +// Add FTDX10 Rigcontrol (19) +// Fix 64 bit bug in displaying INP3 Messages (20) +// Improve restart of WinRPR TNC on remote host (21) +// Fix some Rigcontrol issues with empty timebands (22) +// Fix 64 bit bug in processing INP3 Messages (22) +// First pass at api (24) +// Send OK in response to Rigcontrol CMD (24) +// Disable CTS check in WriteComBlock (26) +// Improvments to reporting to M0LTE Map (26) +// IPGateway fix from github user isavitsky (27) +// Fix possible crash in SCSPactor PTCPORT code (29) +// Add NodeAPI call sendLinks and remove get from other calls (32) +// Improve validation of Web Beacon Config (33) +// Support SNMP via host ip stack as well as IPGateway (34) +// Switch APRS Map to OSM tile servers (36) +// Fix potential buffer overflow in Telnet login (36) +// Allow longer serial device names (37) +// Fix ICF8101 Mode setting (37) +// Kill link if we are getting repeated RR(F) after timeout +// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) +// Change default of SECURETELNET to 1 (41) +// Add optional ATTACH time limit for ARDOP (42) +// Fix buffer overflow risk in HTTP Terminal(42) +// Fix KISSHF Interlock (43) +// Support other than channel A on HFKISS (43) +// Support additional port info reporting for M0LTE Map (44) +// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) +// Add ARDOP UI Packets to MH (45) +// Add support for Qtsm Mgmt Interface (45) +// NodeAPI improvements (46) +// Add MQTT Interface (46) +// Fix buffer leak in ARDOP code(46) +// Fix possible crash if MQTT not in use (47) +// Add optional ATTACH time limit for VARA (48) +// API format fixes (48) +// AGWAPI Add protection against accidental connects from a non-agw application (50) +// Save MH and NODES every hour (51) +// Fix handling long unix device names (now max 250 bytes) (52) +// Fix error reporting in api update (53) +// Coding changes to remove some compiler warnings (53, 54) +// Add MQTT reporting of Mail Events (54) +// Fix beaconong on KISSHF ports (55) +// Fix MailAPI msgs endpoint +// Attempt to fix NC going to wrong application. (57) +// Improve ARDOP end of session code (58) +// Run M0LTE Map reporting in a separate thread (59/60) +// Add RHP support for WhatsPac (59) +// Add timestamps to LIS monitor (60) +// Fix problem with L4 frames being delivered out of sequence (60) +// Add Compression of Netrom connections (62) +// Improve handling of Locked Routes (62) +// Add L4 RESET (Paula G8PZT's extension to NETROM) +// Fix problem using SENDRAW from BPQMail (63) +// Fix compatibility with latest ardopcf (64) +// Fix bug in RHP socket timeout code (65) +// Fix L4 RTT (66) +// Fix RigConrol with Chanxx but no other settings (66) +// Add option to compress L2 frames (67) +// Sort Routes displays (67) +// Fix Ardop session premature close (70) +// Add timestamps to log entries in Web Driver windows (70) +// Generate stack backtrace if SIGSEGV or SIGABRT occur (Linux) (70) +// Remove some debug logging from L2 code (70) +// Fix compiling LinBPQ with nomqtt option (70) +// Improve handling of binary data in RHP interface (70) +// Fix sending KISS commands to multiport or multidropped TNCs (70) +// Add MHUV and MHLV commands (Verbose listing with timestamps in clock time) (70) + +#define CKernel + +#include "Versions.h" + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" +#include "AsmStrucs.h" + +#include "SHELLAPI.H" +#include "kernelresource.h" + +#include +#include +#include "BPQTermMDI.h" + +#include "GetVersion.h" + +#define DllImport __declspec( dllimport ) + +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) +void _CheckGuardZone(char * File, int Line); + +#define CHECKLOADED 0 +#define SETAPPLFLAGS 1 +#define SENDBPQFRAME 2 +#define GETBPQFRAME 3 +#define GETSTREAMSTATUS 4 +#define CLEARSTREAMSTATUS 5 +#define BPQCONDIS 6 +#define GETBUFFERSTATUS 7 +#define GETCONNECTIONINFO 8 +#define BPQRETURN 9 // GETCALLS +//#define RAWTX 10 //IE KISS TYPE DATA +#define GETRAWFRAME 11 +#define UPDATESWITCH 12 +#define BPQALLOC 13 +//#define SENDNETFRAME 14 +#define GETTIME 15 + +extern short NUMBEROFPORTS; +extern long PORTENTRYLEN; +extern long LINKTABLELEN; +extern struct PORTCONTROL * PORTTABLE; +extern void * FREE_Q; +extern UINT APPL_Q; // Queue of frames for APRS Appl + +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR NEXTID; +extern DWORD MAXCIRCUITS; +extern DWORD L4DEFAULTWINDOW; +extern DWORD L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; + +extern struct WL2KInfo * WL2KReports; + +extern int NUMBEROFTNCPORTS; + + +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * TelnetExtInit(EXTPORTDATA * PortEntry); +//void * SoundModemExtInit(EXTPORTDATA * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * UIARQExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * SIXPACKExtInit(EXTPORTDATA * PortEntry); + +extern char * ConfigBuffer; // Config Area +VOID REMOVENODE(dest_list * DEST); +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); +VOID GetUIConfig(); +VOID ADIFWriteFreqList(); +void SaveAIS(); +void initAIS(); +void initADSB(); + +extern BOOL ADIFLogEnabled; + +int CloseOnError = 0; + +char UIClassName[]="UIMAINWINDOW"; // the main window class name + +HWND UIhWnd; + +extern char AUTOSAVE; +extern char AUTOSAVEMH; + +extern char MYNODECALL; // 10 chars,not null terminated + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +extern char * CONFIGFILENAME; + +DllExport BPQVECSTRUC * BPQHOSTVECPTR; + +extern int DATABASESTART; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + +extern double LatFromLOC; +extern double LonFromLOC; + + +extern int BPQHOSTAPI(); +extern int INITIALISEPORTS(); +extern int TIMERINTERRUPT(); +extern int MONDECODE(); +extern int BPQMONOPTIONS(); +extern char PWTEXT[]; +extern char PWLen; + +extern int FINDFREEDESTINATION(); +extern int RAWTX(); +extern int RELBUFF(); +extern int SENDNETFRAME(); +extern char MYCALL[]; // 7 chars, ax.25 format + +extern HWND hIPResWnd; +extern BOOL IPMinimized; + +extern int NODESINPROGRESS; +extern VOID * CURRENTNODE; + + +BOOL Start(); + +VOID SaveWindowPos(int port); +VOID SaveAXIPWindowPos(int port); +VOID SetupRTFHddr(); +DllExport VOID APIENTRY CreateNewTrayIcon(); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int DoMonData(int Stream); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +CloseHostSessions(); +SaveHostSessions(); +VOID SaveBPQ32Windows(); +VOID CloseDriverWindow(int port); +VOID CheckWL2KReportTimer(); +VOID SetApplPorts(); +VOID WriteMiniDump(); +VOID FindLostBuffers(); +BOOL InitializeTNCEmulator(); +VOID TNCTimer(); +char * strlop(char * buf, char delim); + +DllExport int APIENTRY Get_APPLMASK(int Stream); +DllExport int APIENTRY GetStreamPID(int Stream); +DllExport int APIENTRY GetApplFlags(int Stream); +DllExport int APIENTRY GetApplNum(int Stream); +DllExport BOOL APIENTRY GetAllocationState(int Stream); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY RXCount(int Stream); +DllExport int APIENTRY TXCount(int Stream); +DllExport int APIENTRY MONCount(int Stream); +DllExport int APIENTRY GetCallsign(int stream, char * callsign); +DllExport VOID APIENTRY RelBuff(VOID * Msg); +void SaveMH(); +void DRATSPoll(); + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +VOID SetWindowTextSupport(); +int WritetoConsoleSupport(char * buff); +VOID PMClose(); +VOID MySetWindowText(HWND hWnd, char * Msg); +BOOL CreateMonitorWindow(char * MonSize); +VOID FormatTime3(char * Time, time_t cTime); + +char EXCEPTMSG[80] = ""; + +char SIGNONMSG[128] = ""; +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index + + +char WL2KCall[10]; +char WL2KLoc[7]; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + +extern UCHAR ConfigDirectory[260]; + +extern uint64_t timeLoadedMS; + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +DllExport int APIENTRY CloseBPQ32(); +DllExport char * APIENTRY GetLOC(); +DllExport int APIENTRY SessionControl(int stream, int command, int param); + +int DoRefreshWebMailIndex(); + +BOOL APIENTRY Init_IP(); +BOOL APIENTRY Poll_IP(); + +BOOL APIENTRY Init_PM(); +BOOL APIENTRY Poll_PM(); + +BOOL APIENTRY Init_APRS(); +BOOL APIENTRY Poll_APRS(); +VOID HTTPTimer(); + +BOOL APIENTRY Rig_Init(); +BOOL APIENTRY Rig_Close(); +BOOL Rig_Poll(); + +VOID IPClose(); +VOID APRSClose(); +VOID CloseTNCEmulator(); + +VOID Poll_AGW(); +void RHPPoll(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +int * Flag = (int *)&Flag; // for Dump Analysis +int MAJORVERSION=4; +int MINORVERSION=9; + +struct SEM Semaphore = {0, 0, 0, 0}; +struct SEM APISemaphore = {0, 0, 0, 0}; +int SemHeldByAPI = 0; +int LastSemGets = 0; +UINT Sem_eax = 0; +UINT Sem_ebx = 0; +UINT Sem_ecx = 0; +UINT Sem_edx = 0; +UINT Sem_esi = 0; +UINT Sem_edi = 0; + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; +//DllExport long MONDECODEPTR = (long)&MONDECODE; + +extern UCHAR BPQDirectory[]; +extern UCHAR LogDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +static char BPQWinMsg[] = "BPQWindowMessage"; + +static char ClassName[] = "BPQMAINWINDOW"; + +HKEY REGTREE = HKEY_CURRENT_USER; +char REGTREETEXT[100] = "HKEY_CURRENT_USER"; + +UINT BPQMsg=0; + +#define MAXLINELEN 120 +#define MAXSCREENLEN 50 + +#define BGCOLOUR RGB(236,233,216) + +HBRUSH bgBrush = NULL; + +//int LINELEN=120; +//int SCREENLEN=50; + +//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; + +//int lineno=0; +//int col=0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +HANDLE OpenConfigFile(char * file); + +VOID SetupBPQDirectory(); +VOID SendLocation(); + +//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); + +#define TRAY_ICON_ID 1 // ID number for the Notify Icon +#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window + +NOTIFYICONDATA niData; + +int SetupConsoleWindow(); + +BOOL StartMinimized=FALSE; +BOOL MinimizetoTray=TRUE; + +BOOL StatusMinimized = FALSE; +BOOL ConsoleMinimized = FALSE; + +HMENU trayMenu=0; + +HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; + +BOOL FrameMaximized = FALSE; + +BOOL IGateEnabled = TRUE; +extern int ISDelayTimer; // Time before trying to reopen APRS-IS link +extern int ISPort; + +UINT * WINMORTraceQ = NULL; +UINT * SetWindowTextQ = NULL; + +static RECT Rect = {100,100,400,400}; // Console Window Position +RECT FRect = {100,100,800,600}; // Frame +static RECT StatusRect = {100,100,850,500}; // Status Window + +DllExport int APIENTRY DumpSystem(); +DllExport int APIENTRY SaveNodes (); +DllExport int APIENTRY ClearNodes (); +DllExport int APIENTRY SetupTrayIcon(); + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) + +VOID * _Q_REM(VOID *Q, char * File, int Line); + +UINT ReleaseBuffer(UINT *BUFF); + + +VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +DllExport int APIENTRY DeallocateStream(int stream); + +int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); + +int FirstEntry = 1; +BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close +BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe + +BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. + // Used to Kill surplus BPQ32.exe processes + +DWORD Our_PID; // Our Process ID - local variable + +void * InitDone = 0; +int FirstInitDone = 0; +int PerlReinit = 0; +UINT_PTR TimerHandle = 0; +UINT_PTR SessHandle = 0; + +BOOL EventsEnabled = 0; + +unsigned int TimerInst = 0xffffffff; + +HANDLE hInstance = 0; + +int AttachedProcesses = 0; +int AttachingProcess = 0; +HINSTANCE hIPModule = 0; +HINSTANCE hRigModule = 0; + +BOOL ReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL CloseAllNeeded = FALSE; +BOOL NeedWebMailRefresh = FALSE; + +int AttachedPIDList[100] = {0}; + +HWND hWndArray[100] = {0}; +int PIDArray[100] = {0}; +char PopupText[30][100] = {""}; + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MTX; // Top bit indicates use local time +uint64_t MMASK; +UCHAR MUIONLY; + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +char pgm[256]; // Uninitialised so per process + +HANDLE Mutex; + +BOOL PartLine = FALSE; +int pindex = 0; +DWORD * WritetoConsoleQ; + + +LARGE_INTEGER lpFrequency = {0}; +LARGE_INTEGER lastRunTime; +LARGE_INTEGER currentTime; + +int ticksPerMillisec; +int interval; + + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + + +TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; +TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; + + +BOOL ProcessConfig(); +VOID FreeConfig(); + +DllExport int APIENTRY WritetoConsole(char * buff); + +BOOLEAN CheckifBPQ32isLoaded(); +BOOLEAN StartBPQ32(); +DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); +BOOL LoadIPDriver(); +BOOL Send_IP(VOID * Block, DWORD len); +VOID CheckforLostProcesses(); +BOOL LoadRigDriver(); +VOID SaveConfig(); +VOID CreateRegBackup(); +VOID ResolveUpdateThread(); +VOID OpenReportingSockets(); +DllExport VOID APIENTRY CloseAllPrograms(); +DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); +int upnpClose(); + +BOOL IPActive = FALSE; +extern BOOL IPRequired; +BOOL PMActive = FALSE; +extern BOOL PMRequired; +BOOL RigRequired = TRUE; +BOOL RigActive = FALSE; +BOOL APRSActive = FALSE; +BOOL AGWActive = FALSE; +BOOL needAIS = FALSE; +int needADSB = 0; + +extern int AGWPort; + +Tell_Sessions(); + + +typedef int (WINAPI FAR *FARPROCX)(); + +FARPROCX CreateToolHelp32SnapShotPtr; +FARPROCX Process32Firstptr; +FARPROCX Process32Nextptr; + +void LoadToolHelperRoutines() +{ + HINSTANCE ExtDriver=0; + int err; + char msg[100]; + + ExtDriver=LoadLibrary("kernel32.dll"); + + if (ExtDriver == NULL) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); + OutputDebugString(msg); + return; + } + + CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); + Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); + Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); + + if (CreateToolHelp32SnapShotPtr == 0) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); + OutputDebugString(msg); + return; + } +} + +BOOL GetProcess(int ProcessID, char * Program) +{ + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + int p; + + if (CreateToolHelp32SnapShotPtr==0) + { + return (TRUE); // Routine not available + } + // Take a snapshot of all processes in the system. + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + if (ProcessID==pe32.th32ProcessID) + { + // if running on 98, program contains the full path - remove it + + for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) + { + if (pe32.szExeFile[p]=='\\') + { + break; + } + } + p++; + + sprintf(Program,"%s", &pe32.szExeFile[p]); + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + + sprintf(Program,"PID %d Not Found", ProcessID); + CloseHandle( hProcessSnap ); + return(FALSE); +} + +BOOL IsProcess(int ProcessID) +{ + // Check that Process exists + + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available + + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return(TRUE); // Don't know, so assume ok + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return(TRUE); // Don't know, so assume ok + } + + do + { + if (ProcessID==pe32.th32ProcessID) + { + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return(FALSE); +} + +#include "DbgHelp.h" + +VOID MonitorThread(int x) +{ + // Thread to detect killed processes. Runs in process owning timer. + + // Obviously can't detect loss of timer owning thread! + + do + { + if (Semaphore.Gets == LastSemGets && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + // Write a minidump + + WriteMiniDump(); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); + CheckforLostProcesses(); + + } while (TRUE); +} + +VOID CheckforLostProcesses() +{ + UCHAR buff[100]; + char Log[80]; + int i, n, ProcessID; + + for (n=0; n < AttachedProcesses; n++) + { + ProcessID=AttachedPIDList[n]; + + if (!IsProcess(ProcessID)) + { + // Process has died - Treat as a detach + + sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); + OutputDebugString(Log); + + // Remove Tray Icon Entry + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + // If process had the semaphore, release it + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) + { + DeallocateStream(i); + } + } + + if (TimerInst == ProcessID) + { + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; +// Tell_Sessions(); + OutputDebugString("BPQ32 Process was running timer \n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + + } + + // Remove this entry from PID List + + for (i=n; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + AttachedProcesses--; + + sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); + OutputDebugString(buff); + } + } +} +VOID MonitorTimerThread(int x) +{ + // Thread to detect killed timer process. Runs in all other BPQ32 processes. + + do { + + Sleep(60000); + + if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) + { + // Timer owning Process has died - Force a new timer to be created + // New timer thread will detect lost process and tidy up + + Debugprintf("BPQ32 Process %d with Timer died", TimerInst); + + // If process was holding the semaphore, release it + + if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + +// KillTimer(NULL,TimerHandle); +// TimerHandle=0; +// TimerInst=0xffffffff; +// Tell_Sessions(); + + CheckforLostProcesses(); // Normally only done in timer thread, which is now dead + + // Timer can only run in BPQ32.exe + + TimerInst=0xffffffff; // So we dont keep doing it + TimerHandle = 0; // So new process attaches + + if (Closing == FALSE && AttachingProcess == FALSE) + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + +// if (MinimizetoTray) +// Shell_NotifyIcon(NIM_DELETE,&niData); + } + + } while (TRUE); +} + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); + +VOID TimerProcX(); + +VOID CALLBACK TimerProc( + HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT idEvent, // timer identifier + DWORD dwTime) // current system time +{ + KillTimer(NULL,TimerHandle); + TimerProcX(); + TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); +} +VOID TimerProcX() +{ + struct _EXCEPTION_POINTERS exinfo; + + // + // Get semaphore before proceeeding + // + + GetSemaphore(&Semaphore, 2); + + // Get time since last run + + QueryPerformanceCounter(¤tTime); + + interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; + lastRunTime.QuadPart = currentTime.QuadPart; + + //Debugprintf("%d", interval); + + // Process WINMORTraceQ + + while (WINMORTraceQ) + { + UINT * Buffer = Q_REM(&WINMORTraceQ); + struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; + int Len = Buffer[2]; + char * Msg = (char *)&Buffer[3]; + + WritetoTraceSupport(TNC, Msg, Len); + RelBuff(Buffer); + } + + if (SetWindowTextQ) + SetWindowTextSupport(); + + while (WritetoConsoleQ) + { + UINT * Buffer = Q_REM(&WritetoConsoleQ); + WritetoConsoleSupport((char *)&Buffer[2]); + RelBuff(Buffer); + } + + strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); + + __try + { + + if (trayMenu == NULL) + SetupTrayIcon(); + + // See if reconfigure requested + + if (CloseAllNeeded) + { + CloseAllNeeded = FALSE; + CloseAllPrograms(); + } + + if (ReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + WSADATA WsaData; // receives data from WSAStartup + RECT cRect; + + ReconfigFlag = FALSE; + + SetupBPQDirectory(); + + WritetoConsole("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + GetWindowRect(FrameWnd, &FRect); + + SaveWindowPos(70); // Rigcontrol + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + WSACleanup(); + + WL2KReports = NULL; + + Sleep(2000); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); // Restart Ports + + SetApplPorts(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + + PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + // Free the APRS Appl Q + + APPL_Q = 0; + + OpenReportingSockets(); + + WritetoConsole("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 0); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 0); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + } + + + if (RigReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + RigReconfigFlag = FALSE; + CloseDriverWindow(70); + Rig_Close(); + Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close + RigActive = Rig_Init(); + + WritetoConsole("Rigcontrol Reconfiguration Complete\n"); + } + } + + if (APRSReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + WritetoConsole("APRS Reconfiguration Complete\n"); + } + } + + } + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + strcpy(EXCEPTMSG, "Timer Processing"); + + __try + { + if (IPActive) Poll_IP(); + if (PMActive) Poll_PM(); + if (RigActive) Rig_Poll(); + + if (NeedWebMailRefresh) + DoRefreshWebMailIndex(); + + CheckGuardZone(); + + if (APRSActive) + { + Poll_APRS(); + CheckGuardZone(); + } + + CheckWL2KReportTimer(); + + CheckGuardZone(); + + TIMERINTERRUPT(); + + CheckGuardZone(); + + FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "HTTP Timer Processing"); + + HTTPTimer(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + CheckGuardZone(); + + return; +} + +HANDLE NPHandle; + +int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; +int (WINAPI FAR *EnumProcessesPtr)() = NULL; + +FirstInit() +{ + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + + // First Time Ports and Timer init + + // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain + + // Call wsastartup - most systems need winsock, and duplicate statups could be a problem + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver=LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + timeLoadedMS = GetTickCount(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + WritetoConsole("\n"); + WritetoConsole("Port Initialisation Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (APRSActive) + { + hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, + 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); + + CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, + 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGate State - Disconnected", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "GPS Off", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); + } + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + _beginthread(MonitorThread,0,0); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser + + if (ADIFLogEnabled) + ADIFWriteFreqList(); + + OutputDebugString("BPQ32 Port Initialisation Complete\n"); + + if (needAIS) + initAIS(); + + if (needADSB) + initADSB(); + + return 0; +} + +Check_Timer() +{ + if (Closing) + return 0; + + if (Semaphore.Flag) + return 0; + + if (InitDone == (void *)-1) + { + GetSemaphore(&Semaphore, 3); + Sleep(15000); + FreeSemaphore(&Semaphore); + exit (0); + } + + if (FirstInitDone == 0) + { + GetSemaphore(&Semaphore, 3); + + if (_stricmp(pgm, "bpq32.exe") == 0) + { + FirstInit(); + FreeSemaphore(&Semaphore); + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + FirstInitDone=1; // Only init in BPQ32.exe + return 0; + } + else + { + FreeSemaphore(&Semaphore); + return 0; + } + } + + if (TimerHandle == 0 && FirstInitDone == 1) + { + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + // Only attach timer to bpq32.exe + + if (_stricmp(pgm, "bpq32.exe") != 0) + { + return 0; + } + + GetSemaphore(&Semaphore, 3); + OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); + + if (!ProcessConfig()) + { + ShowWindow(hConsWnd, SW_RESTORE); + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); + + exit (0); + } + + GetVersionInfo("bpq32.dll"); + + SetupConsoleWindow(); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + Consoleprintf("Reinitialising..."); + + SetupBPQDirectory(); + + Sleep(1000); // Allow time for sockets to close + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver = LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + Start(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + NODESINPROGRESS = 0; + CURRENTNODE = 0; + + SetApplPorts(); + + WritetoConsole("\n\nPort Reinitialisation Complete\n"); + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + RigActive = Rig_Init(); + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + FreeConfig(); + + _beginthread(MonitorThread,0,0); + + ReportTimer = 0; + + OpenReportingSockets(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + return (1); + } + + return (0); +} + +DllExport INT APIENTRY CheckTimer() +{ + return Check_Timer(); +} + +Tell_Sessions() +{ + // + // Post a message to all listening sessions, so they call the + // API, and cause a new timer to be allocated + // + HWND hWnd; + int i; + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) + { + hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; + PostMessage(hWnd, BPQMsg,i, 1); + PostMessage(hWnd, BPQMsg,i, 2); + } + } + return (0); +} + +BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) +{ + DWORD n; + char buf[350]; + + int i; + unsigned int ProcessID; + + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + + switch( ul_reason_being_called ) + { + case DLL_PROCESS_ATTACH: + + if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(LINKTABLE) != LINK_TABLE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); + return 0; + } + if (sizeof(struct ROUTE) != ROUTE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + GetSemaphore(&Semaphore, 4); + + BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; + + LoadToolHelperRoutines(); + + Our_PID = GetCurrentProcessId(); + + QueryPerformanceFrequency(&lpFrequency); + + ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; + + lastRunTime.QuadPart = lpFrequency.QuadPart; + + GetProcess(Our_PID, pgm); + + if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 1; + } + + if (_stricmp(pgm,"BPQ32.exe") == 0) + BPQ32_EXE = TRUE; + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQMail.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = TRUE; + + if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end + { + FirstEntry = 0; + if (BPQ32_EXE) + CloseLast = FALSE; + } + else + { + if (BPQ32_EXE && AttachingProcess == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); + return 0; + } + } + + if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) + { + MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQUIUtil.exe") == 0) + { + MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + { + MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + AuthorisedProgram = TRUE; + + if (InitDone == 0) + { +// #pragma warning(push) +// #pragma warning(disable : 4996) + +// if (_winver < 0x0600) +// #pragma warning(pop) +// { +// // Below Vista +// +// REGTREE = HKEY_LOCAL_MACHINE; +// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); +// } + + hInstance=hInst; + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); + FreeSemaphore(&Semaphore); + + if (i != IDOK) return (0); + + CloseHandle(Mutex); + } + + if (!BPQ32_EXE) + { + if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed + { + // Wasn't Loaded, so we have started it, and should let it init system + + goto SkipInit; + } + } + + GetVersionInfo("bpq32.dll"); + + sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", + TextVerstring, Datestring, VerCopyright); + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); + + SetupConsoleWindow(); + SetupBPQDirectory(); + + if (!ProcessConfig()) + { + StartMinimized = FALSE; + MinimizetoTray = FALSE; + ShowWindow(FrameWnd, SW_MAXIMIZE); + ShowWindow(hConsWnd, SW_MAXIMIZE); + ShowWindow(StatusWnd, SW_HIDE); + + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); + + return (0); + } + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + if (Start() !=0) + { + Sleep(3000); + FreeSemaphore(&Semaphore); + return (0); + } + else + { + SetApplPorts(); + + GetUIConfig(); + + InitDone = &InitDone; + BPQMsg = RegisterWindowMessage(BPQWinMsg); +// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); +// TimerInst=GetCurrentProcessId(); + +/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + +*/ + Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// CreatePipe(&H1,&H2,NULL,1000); + +// GetLastError(); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + +// GetLastError(); + +/* + // + // Read SYSOP password + // + + if (PWTEXT[0] == 0) + { + handle = OpenConfigFile("PASSWORD.BPQ"); + + if (handle == INVALID_HANDLE_VALUE) + { + WritetoConsole("Can't open PASSWORD.BPQ\n"); + PWLen=0; + PWTEXT[0]=0; + } + else + { + ReadFile(handle,PWTEXT,78,&n,NULL); + CloseHandle(handle); + } + } +*/ + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + PWLen=i; + + } + } + else + { + if (InitDone != &InitDone) + { + MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + } + + // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread +SkipInit: + + _beginthread(MonitorTimerThread,0,0); + + FreeSemaphore(&Semaphore); + + AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); + + if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + // Set up local variables + + MCOM=1; + MTX=1; + MMASK=0xffffffffffffffff; + +// if (StartMinimized) +// if (MinimizetoTray) +// ShowWindow(FrameWnd, SW_HIDE); +// else +// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); +// else +// ShowWindow(FrameWnd, SW_RESTORE); + + return 1; + + case DLL_THREAD_ATTACH: + + return 1; + + case DLL_THREAD_DETACH: + + return 1; + + case DLL_PROCESS_DETACH: + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = FALSE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = FALSE; + + ProcessID=GetCurrentProcessId(); + + Debugprintf("BPQ32 Process %d Detaching", ProcessID); + + // Release any streams that the app has failed to release + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) + { + // If connected, disconnect + + SessionControl(i, 2, 0); + DeallocateStream(i); + } + } + + // Remove any Tray Icon Entries + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + char Log[80]; + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + if (Mutex) CloseHandle(Mutex); + + // Remove our entry from PID List + + for (i=0; i< AttachedProcesses; i++) + if (AttachedPIDList[i] == ProcessID) + break; + + for (; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + + AttachedProcesses--; + + if (TimerInst == ProcessID) + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + OutputDebugString("BPQ32 Process with Timer closing\n"); + + // Call Port Close Routines + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + WSACleanup(); + WSAGetLastError(); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + if (hConsWnd) DestroyWindow(hConsWnd); + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + else + { + // Not Timer Process + + if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left + { + Debugprintf("Only BPQ32.exe running - close it"); + CloseAllNeeded = TRUE; + } + } + + if (AttachedProcesses < 2) + { + if (AUTOSAVE) + SaveNodes(); + if (AUTOSAVEMH) + SaveMH(); + + if (needAIS) + SaveAIS(); + } + if (AttachedProcesses == 0) + { + Closing = TRUE; + KillTimer(NULL,TimerHandle); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + // Unload External Drivers + + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) + FreeLibrary(PORTVEC->DLLhandle); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + } + } + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + return 1; + } + return 1; +} + +DllExport int APIENTRY CloseBPQ32() +{ + // Unload External Drivers + + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + int ProcessID = GetCurrentProcessId(); + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + if (TimerInst == ProcessID) + { + OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + CloseTNCEmulator(); + WSACleanup(); + + if (hConsWnd) DestroyWindow(hConsWnd); + + Debugprintf("AttachedProcesses %d ", AttachedProcesses); + + if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + + return 0; +} + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); + +VOID SetupBPQDirectory() +{ + HKEY hKey = 0; + HKEY hKeyIn = 0; + HKEY hKeyOut = 0; + int disp; + int retCode,Type,Vallen=MAX_PATH,i; + char msg[512]; + char ValfromReg[MAX_PATH] = ""; + char DLLName[256]="Not Known"; + char LogDir[256]; + char Time[64]; + +/* +•NT4 was/is '4' +•Win 95 is 4.00.950 +•Win 98 is 4.10.1998 +•Win 98 SE is 4.10.2222 +•Win ME is 4.90.3000 +•2000 is NT 5.0.2195 +•XP is actually 5.1 +•Vista is 6.0 +•Win7 is 6.1 + + i = _osver; / Build + i = _winmajor; + i = _winminor; +*/ +/* +#pragma warning(push) +#pragma warning(disable : 4996) + +if (_winver < 0x0600) +#pragma warning(pop) + { + // Below Vista + + REGTREE = HKEY_LOCAL_MACHINE; + strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); + ValfromReg[0] = 0; + } + else +*/ + { + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); + } + else + { + // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_READ, + &hKeyIn); + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); + + // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); + + if (retCode != ERROR_SUCCESS) + if (hKeyIn) + CopyReg(hKeyIn, hKeyOut); + + RegCloseKey(hKeyIn); + RegCloseKey(hKeyOut); + } + } + + GetModuleFileName(hInstance,DLLName,256); + + BPQDirectory[0]=0; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + // Try "BPQ Directory" + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + + if (ValfromReg[0] == 0) + { + // BPQ Directory absent or = "" - try "Config File Location" + + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey,"Config File Location",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + } + + if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + + ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); + + // Also get "BPQ Program Directory" + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); + + // And Log Directory + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); + + RegCloseKey(hKey); + } + + strcpy(ConfigDirectory, BPQDirectory); + + if (LogDirectory[0] == 0) + strcpy(LogDirectory, BPQDirectory); + + if (BPQProgramDirectory[0] == 0) + strcpy(BPQProgramDirectory, BPQDirectory); + + sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); + WritetoConsole(msg); + OutputDebugString(msg); + FormatTime3(Time, time(NULL)); + sprintf(msg,"Loaded %s\n", Time); + WritetoConsole(msg); + OutputDebugString(msg); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#if _MSC_VER >= 1400 + +#define _winmajor 6 +#define _winminor 0 + +#endif + + i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); + +#pragma warning(pop) + + WritetoConsole(msg); + OutputDebugString(msg); + + i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); + WritetoConsole(&msg[6]); + msg[i-1]=0; + OutputDebugString(msg); + + // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, + // so will write the wrong tree on ) + + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); + } + else + { + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); + + RegCloseKey(hKey); + } + + // Make sure Logs Directory exists + + sprintf(LogDir, "%s/Logs", LogDirectory); + + CreateDirectory(LogDir, NULL); + + return; +} + +HANDLE OpenConfigFile(char *fn) +{ + HANDLE handle; + UCHAR Value[MAX_PATH]; + FILETIME LastWriteTime; + SYSTEMTIME Time; + char Msg[256]; + + + // If no directory, use current + if (BPQDirectory[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,fn); + } + + handle = CreateFile(Value, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + GetFileTime(handle, NULL, NULL, &LastWriteTime); + FileTimeToSystemTime(&LastWriteTime, &Time); + + sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, + Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); + + OutputDebugString(Msg); + + return(handle); +} + +#ifdef _WIN64 +int BPQHOSTAPI() +{ + return 0; +} +#endif + + +DllExport int APIENTRY GETBPQAPI() +{ + return (int)BPQHOSTAPI; +} + +//DllExport UINT APIENTRY GETMONDECODE() +//{ +// return (UINT)MONDECODE; +//} + + +DllExport INT APIENTRY BPQAPI(int Fn, char * params) +{ + +/* +; +; BPQ HOST MODE SUPPORT CODE +; +; 22/11/95 +; +; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED +; (OS2 VERSION HAD UPSET KANT VERISON +; +; +*/ + + +/* + + BPQHOSTPORT: +; +; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS +; +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH='B',AL='P',BH='Q',BL=' ' +; DH = major version number and DL = minor version number. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:SI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:DI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:DI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:SI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:DI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:SI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 13 Allocate/deallocate stream +; If AL=0, return first free stream +; If AL>0, CL=1, Allocate stream. If aleady allocated, +; return CX nonzero, else allocate, and return CX=0 +; If AL>0, CL=2, Release stream +; +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer + + +*/ + + + switch(Fn) + { + + case CHECKLOADED: + + params[0]=MAJORVERSION; + params[1]=MINORVERSION; + params[2]=QCOUNT; + + return (1); + } + return 0; +} + +DllExport int APIENTRY InitSwitch() +{ + return (0); +} + +/*DllExport int APIENTRY SwitchTimer() +{ + GetSemaphore((&Semaphore); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + return (0); +} +*/ +DllExport int APIENTRY GetFreeBuffs() +{ +// Returns number of free buffers +// (BPQHOST function 7 (part)). + return (QCOUNT); +} + +DllExport UCHAR * APIENTRY GetNodeCall() +{ + return (&MYNODECALL); +} + + +DllExport UCHAR * APIENTRY GetNodeAlias() +{ + return (&MYALIASTEXT[0]); +} + +DllExport UCHAR * APIENTRY GetBBSCall() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); +} + + +DllExport UCHAR * APIENTRY GetBBSAlias() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); +} + +DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) +{ + if (Appl < 1 || Appl > NumberofAppls ) return; + + strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); +} + +BOOL UpdateNodesForApp(int Appl); + +DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + +DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + + +DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) +{ + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + +BOOL UpdateNodesForApp(int Appl) +{ + int App=Appl-1; + int DestLen = sizeof (struct DEST_LIST); + int n = MAXDESTS; + + struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; + APPLCALLS * APPL=&APPLCALLTABLE[App]; + + if (DEST == NULL) + { + // No dest at the moment. If we have valid call and Qual, create an entry + + if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + + GetSemaphore(&Semaphore, 5); + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + break; + } + + if (n == 0) + { + // no dests + + FreeSemaphore(&Semaphore); + return FALSE; + } + + NUMBEROFNODES++; + APPL->NODEPOINTER = DEST; + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + + return TRUE; + } + + // We have a destination. If Quality is zero, remove it, else update it + + if (APPLCALLTABLE[App].APPLQUAL == 0) + { + GetSemaphore(&Semaphore, 6); + + REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + + APPL->NODEPOINTER=NULL; + + FreeSemaphore(&Semaphore); + return FALSE; + + } + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + GetSemaphore(&Semaphore, 7); + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + return TRUE; + +} + + +DllExport UCHAR * APIENTRY GetSignOnMsg() +{ + return (&SIGNONMSG[0]); +} + + +DllExport HKEY APIENTRY GetRegistryKey() +{ + return REGTREE; +} + +DllExport char * APIENTRY GetRegistryKeyText() +{ + return REGTREETEXT;; +} + +DllExport UCHAR * APIENTRY GetBPQDirectory() +{ + while (BPQDirectory[0] == 0) + { + Debugprintf("BPQ Directory not set up - waiting"); + Sleep(1000); + } + return (&BPQDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetProgramDirectory() +{ + return (&BPQProgramDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetLogDirectory() +{ + return (&LogDirectory[0]); +} + +// Version for Visual Basic + +DllExport char * APIENTRY CopyBPQDirectory(char * dir) +{ + return (strcpy(dir,BPQDirectory)); +} + +DllExport int APIENTRY GetMsgPerl(int stream, char * msg) +{ + int len,count; + + GetMsg(stream, msg, &len, &count ); + + return len; +} + +int Rig_Command(int Session, char * Command); + +BOOL Rig_CommandInt(int Session, char * Command) +{ + return Rig_Command(Session, Command); +} + +DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) +{ + BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; + return (0); +} + +#define L4USER 0 + +BPQVECSTRUC * PORTVEC ; + +VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + HINSTANCE ExtDriver=0; + char msg[128]; + int err=0; + HKEY hKey=0; + UCHAR Value[MAX_PATH]; + + // If no directory, use current + + if (BPQDirectory[0] == 0) + { + strcpy(Value,PORTVEC->PORT_DLL_NAME); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,PORTVEC->PORT_DLL_NAME); + } + + // Several Drivers are now built into bpq32.dll + + _strupr(Value); + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + +// if (strstr(Value, "SOUNDMODEM")) +// return SoundModemExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "UIARQ")) + return UIARQExtInit; + +// if (strstr(Value, "BAYCOM")) +// return (UINT) BaycomExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + if (strstr(Value, "6PACK")) + return SIXPACKExtInit; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[100]; + char Addr1[100]; + char Addr2[100]; + char City[100]; + char State[100]; + char Country[100]; + char PostCode[100]; + char Email[100]; + char Website[100]; + char Phone[100]; + char Data[100]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"SysopName\":\"%s\"," + "\"StreetAddress1\":\"%s\"," + "\"StreetAddress2\":\"%s\"," + "\"City\":\"%s\"," + "\"State\":\"%s\"," + "\"Country\":\"%s\"," + "\"PostalCode\":\"%s\"," + "\"Email\":\"%s\"," + "\"Phones\":\"%s\"," + "\"Website\":\"%s\"," + "\"Comments\":\"%s\"", + + WL2KCall, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len += sprintf(&RegLine[len], "\\\r\n", RegLine); + strcat(RegLine, "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len += sprintf(&RegLine[len], "%02x,", Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len += sprintf(&RegLine[len], "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len += sprintf(&RegLine[len], "%02x,", Value[k]); + if (len > 76) + { + len += sprintf(&RegLine[len], "\\\r\n"); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len += sprintf(&RegLine[len], "00,"); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(70); // Rigcontrol + + + if (hIPResWnd) + SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); + + SaveHostSessions(); +} + +DllExport BOOL APIENTRY CheckIfOwner() +{ + // + // Returns TRUE if current process is root process + // that loaded the DLL + // + + if (TimerInst == GetCurrentProcessId()) + + return (TRUE); + else + return (FALSE); +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +int GetListeningPortsPID(int Port) +{ + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + DWORD n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + + if (TcpTable == NULL) + return 0; + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } + return 0; // Not found +} + +DllExport char * APIENTRY GetLOC() +{ + return LOC; +} + +DllExport void APIENTRY GetLatLon(double * lat, double * lon) +{ + *lat = LatFromLOC; + *lon = LonFromLOC; + return; +} + + +// UZ7HO Dll PTT interface + +// 1 ext_PTT_info +// 2 ext_PTT_settings +// 3 ext_PTT_OFF +// 4 ext_PTT_ON +// 5 ext_PTT_close +// 6 ext_PTT_open + +extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +int WINAPI ext_PTT_info() +{ + return 0; +} + +int WINAPI ext_PTT_settings() +{ + return 0; +} + +int WINAPI ext_PTT_OFF(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +int WINAPI ext_PTT_ON(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 1, 0); + + return 0; +} +int WINAPI ext_PTT_close() +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +DllExport INT WINAPI ext_PTT_open() +{ + return 1; +} + +char * stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup(ch1); + chN2 = _strdup(ch2); + + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + chNdx = chN2; + + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + + chNdx = strstr(chN1, chN2); + + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + + free (chN1); + free (chN2); + return chRet; +} + diff --git a/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base b/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base new file mode 100644 index 0000000..83b3f07 --- /dev/null +++ b/.svn/pristine/15/151b9ce04ab29bd13f02a3d71b7d4be4b771493b.svn-base @@ -0,0 +1,3137 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use WINMOR as a Port Driver +// +// Uses BPQ EXTERNAL interface +// + + +// Version 1.0 January 2009 - Initial Version +// + +// March 22 2010 + +// Send FAULTS to Monitor Window +// Force PROTOCOL = WINMOR/PACTOR (to simplifiy Config) + +// July 2010 +// Support up to 32 BPQ Ports +// Support up to 32 Applications + +// Version 1.2.1.2 August 2010 + +// Save Minimized State +// Handle new "BLOCKED by Busy channel" message from TNC + +// Version 1.2.1.4 August 2010 + +// Add Scan control of BW setting +// Reset TNC if stuck in Disconnecting +// Add option to send reports to WL2K +// Disconnect if appl not available + +// Version 1.2.1.5 August 2010 + +// Updates to WL2K Reporting +// Send Watchdog polls every minute and restart if no response. +// Don't connect if channel is busy (unless specifically overridden) + +// Version 1.2.1.6 September 2010 + +// Add option to kill and restart TNC after each transfer +// Fix PTT operation after Node reconfig + +// Version 1.2.2.1 September 2010 + +// Add option to get config from bpq32.cfg +// Merge with BPQ32.dll + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + +#ifdef WIN32 +#include +#endif + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int standardParams(struct TNCINFO * TNC, char * buf); + +static char ClassName[]="WINMORSTATUS"; +static char WindowTitle[] = "WINMOR"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + +// There seem to be timing issues when calling SendMessage from multiple threads. +// Queue and process in main thread + +UINT * WINMORTraceQ; +UINT * SetWindowTextQ; + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) +{ + int index = 0; + UCHAR * ptr1 = Msg, * ptr2; + UCHAR Line[1000]; + int LineLen, i; + UCHAR Save; + int SaveLen = Len; + char Time[16]; + time_t T; + struct tm * tm; + + if (Len < 0) + return; + + Save = Msg[Len]; + Msg[Len] = 0; + +#ifndef LINBPQ + index=SendMessage(TNC->hMonitor, LB_SETCURSEL, -1, 0); +#endif + +lineloop: + + if (Len > 0) + { + // copy text to control a line at a time + + ptr2 = memchr(ptr1, 13, Len); + + if (ptr2) + { + ptr2++; + LineLen = (int)(ptr2 - ptr1); + Len -= LineLen; + memcpy(Line, ptr1, LineLen); + memcpy(&Line[LineLen - 1], "", 4); + LineLen += 3; + + if ((*ptr2) == 10) + { + memcpy(&Line[LineLen], "", 4); + LineLen += 4; + ptr2++; + Len --; + } + + Line[LineLen] = 0; + + // If line contains any data above 7f, assume binary and dont display + + for (i = 0; i < LineLen; i++) + { + if (Line[i] > 126 || Line[i] < 32) + goto Skip; + } + + // We now also pass to Monitor Window + + if (strlen(Line) < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + strlen(Line); + Monframe.DEST[0] = 1; // Plain Text Monitor + strcpy(&Monframe.DEST[1], Line); + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Line); +#endif + // Write to Web Buffer + + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + + strcat(TNC->WebBuffer, Time); + strcat(TNC->WebBuffer, Line); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved + Skip: + ptr1 = ptr2; + + goto lineloop; + + } + + // Process incomplete line + + for (i = 0; i < Len; i++) + { + if (ptr1[i] > 126 || ptr1[i] < 32) + break; + } + + if (i == Len) + { + if (Len < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + Len; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], ptr1, Len); + Monframe.DEST[1 + Len] = 0; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); +#endif + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + strcat(TNC->WebBuffer, Time); + + strcat(TNC->WebBuffer, ptr1); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved + } + } + +#ifdef LINBPQ +#else + + if (index > 1200) + do + index=index=SendMessage(TNC->hMonitor, LB_DELETESTRING, 0, 0); + while (index > 1000); + + if (index > -1) + index=SendMessage(TNC->hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); +#endif + Msg[SaveLen] = Save; + +} + +VOID MySetWindowTextWithSem(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD(&SetWindowTextQ, buffptr); + } + +#endif +} + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); + +struct SEM SetWindTextSem = {0, 0, 0, 0}; + +VOID MySetWindowText(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + GetSemaphore(&SetWindTextSem, 61); + buffptr = zalloc(400); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD_NP(&SetWindowTextQ, buffptr); + } + + FreeSemaphore(&SetWindTextSem); +#endif +} + +VOID SetWindowTextSupport() +{ + PMSGWITHLEN Buffer; + + while (SetWindowTextQ) + { + GetSemaphore(&SetWindTextSem, 61); + Buffer = Q_REM_NP(&SetWindowTextQ); + SetWindowText((HWND)Buffer->Len, Buffer->Data); + FreeSemaphore(&SetWindTextSem); + free(Buffer); + } +} + + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len) +{ + // It seems writing from multiple threads can cause problems in Windows + // Queue and process in main thread + +#ifdef LINBPQ + WritetoTraceSupport(TNC, Msg, Len); +} +#else + UINT * buffptr; + BOOL Sem = FALSE; + + if (Len < 0) + return; + + // Get semaphore if it isn't set + + if (InterlockedExchange(&Semaphore.Flag, 1) == 0) + { + Sem = TRUE; + Semaphore.Gets++; + } + + buffptr = GetBuff(); + + if (buffptr) + { + if (Len > 340) + Len = 340; + + buffptr[1] = (UINT)TNC; + buffptr[2] = (UINT)Len; + memcpy(&buffptr[3], Msg, Len + 1); + + C_Q_ADD(&WINMORTraceQ, buffptr); + } + + if (Sem) + FreeSemaphore(&Semaphore); + +} +#endif + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else if (standardParams(TNC, buf) == FALSE) + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + + + +void WINMORThread(void * portptr); +VOID ProcessDataSocketData(int port); +int ConnecttoWINMOR(int port); +static int ProcessReceivedData(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + + + +VOID ChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + datalen = sprintf(TXMsg, "MYC %s\r\n", Call); + send(TNC->TCPSock,TXMsg, datalen, 0); + +// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); +// TNC->StartSent = TRUE; + + send(TNC->TCPSock, "MYC\r\n", 5, 0); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int i,winerr; + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes; + size_t txlen = 0; + char ErrMsg[255]; + size_t Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + fd_set readfs; + fd_set writefs; + fd_set errorfs; + struct timeval timeout; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 1: // poll + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); + STREAM->Disconnecting = TRUE; + } + } + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->ConnectCmd && TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + + send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->ConnectCmd = 0; + + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + if (TNC->HeartBeat > 600 && TNC->hWnd) + { + char wtext[100]; + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(TNC->hWnd, wtext); + } + + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + send(TNC->TCPSock, "MODE\r\n", 6, 0); + else + { + if (time(NULL) - TNC->WinmorRestartCodecTimer > 900) // 15 mins + { + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + send(TNC->TCPSock, "STATE\r\n", 7, 0); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); + } + } + if (TNC->FECPending) // Check if FEC Send needed + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + send(TNC->TCPSock, "DISCONNECT\r\n", 12, 0); + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath && TNC->CONNECTED) + { + if (strstr(TNC->ProgramPath, "WINMOR TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + SetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + SetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + ProcessDataSocketData(port); + + if (FD_ISSET(TNC->TCPDataSock, &writefs)) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = buffptr->Len; + memcpy(txbuff,buffptr->Data,txlen); + bytes=send(TNC->TCPSock, (const char FAR *)&txbuff, (int)txlen, 0); + ReleaseBuffer(buffptr); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + i=sprintf(ErrMsg, "WINMOR Data Connection lost for BPQ Port %d\r\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); // Data goes to +7, but we have an extra byte + datalen = buffptr->Len; + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(buffptr->Data, "No Connection to WINMOR Virtual TNC\r", 36); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes = send(TNC->TCPDataSock, txbuff, (int)txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, txbuff, (int)txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + STREAM->PacketsSent++; + + if (STREAM->PacketsSent == 3) + { + if (TNC->Robust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + else + send(TNC->TCPSock, "ROBUST FALSE\r\n", 14, 0); + } + + bytes = send(TNC->TCPDataSock,buff->L2DATA, (int)txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], (int)txlen); + + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + + if (TNC->BusyFlags) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &cmd); + + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) + { + if (buff->L2DATA[9] != 13) + { + // Limit connects + + int tries = atoi(&buff->L2DATA[10]); + int len; + + if (tries > 10) tries = 10; + len = sprintf(&buff->L2DATA[0], "MAXCONREQ %d\r\nMAXCONREQ\r\n", tries); + + send(TNC->TCPSock, &buff->L2DATA[0], len, 0); + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if ((_memicmp(buff->L2DATA, "BW 500", 6) == 0) || (_memicmp(buff->L2DATA, "BW 1600", 7) == 0)) + { + // Generate a local response + + PMSGWITHLEN buffptr = GetBuff(); + + if (_memicmp(buff->L2DATA, "BW 500", 6) == 0) + TNC->WL2KMode = 21; + else + TNC->WL2KMode = 22; + + if (buffptr) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + TNC->WinmorCurrentMode = 0; // So scanner will set next value + } + + if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(buff->L2DATA, "ROBUST", 6) == 0) + { + if (_memicmp(&buff->L2DATA[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + else + TNC->Robust = FALSE; + } + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(buff->L2DATA, "FEC\r", 4) == 0 || _memicmp(buff->L2DATA, "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; + send(TNC->TCPSock,"FECRCV TRUE\r\nFECRCV\r\n", 21, 0); + + if (_memicmp(buff->L2DATA, "FEC 1600", 8) == 0) + TNC->FEC1600 = TRUE; + else + TNC->FEC1600 = FALSE; + + return 0; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80] = "CONNECT "; + + memcpy(&Connect[8], &buff->L2DATA[2], txlen); + txlen += 6; + Connect[txlen++] = 0x0a; + Connect[txlen] = 0; + + _strupr(Connect); + + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + bytes = send(TNC->TCPSock, Connect, (int)txlen, 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &Connect[8], txlen-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + buff->L2DATA[txlen++] = 0x0a; + bytes = send(TNC->TCPSock, &buff->L2DATA[0], (int)txlen, 0); + } + } + if (bytes != txlen) + { + + // WINMOR doesn't seem to recover from a blocked write. For now just reset + + winerr = WSAGetLastError(); + sprintf(ErrMsg, "WINMOR Write Failed for port %d - error code = %d\r\n", port, winerr); + WritetoConsole(ErrMsg); + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + return (0); + } + + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + break; + + case 4: // reinit + + return (0); + + case 5: // Close + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return (0); + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on FLDIGI"); + return 1; // OK to change + } + + if (!TNC->TCPSock) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending) + return TRUE; // Not OK to Change + + if (TNC->CONNECTED) + { + TNC->GavePermission = TRUE; + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + } + return FALSE; + } + + if (Param == 3) // Release Permission + { + if (TNC->CONNECTED) + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + } + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + + if (Scan->Bandwidth == 'W') // Set Wide Mode + { + if (TNC->WinmorCurrentMode != 1600) + { + if (TNC->WinmorCurrentMode == 0) + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + + if (TNC->CONNECTED) + send(TNC->TCPSock, "BW 1600\r\n", 9, 0); + TNC->WinmorCurrentMode = 1600; + } + TNC->WL2KMode = 22; + return 0; + } + + + if (Scan->Bandwidth == 'N') // Set Narrow Mode + { + if (TNC->WinmorCurrentMode != 500) + { + if (TNC->WinmorCurrentMode == 0) + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + + TNC->WinmorCurrentMode = 500; + if (TNC->CONNECTED) + send(TNC->TCPSock, "BW 500\r\n", 8, 0); + } + TNC->WL2KMode = 21; + return 0; + } + + if (Scan->Bandwidth == 'X') // Dont Allow Connects + { + if (TNC->WinmorCurrentMode != 0) + { + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + TNC->WinmorCurrentMode = 0; + } + + TNC->WL2KMode = 0; + return 0; + } + + return 0; + } + return 0; +} + +VOID ReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[256]; + char wtext[100]; + + ChangeMYC(TNC, TNC->NodeCall); + + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->hWnd) + { + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(TNC->hWnd, wtext); + } + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC) +{ + // Disable other TNCs in same Interlock Group + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (rxInterlock == 0 || txInterlock == 0) + return; + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc) + TNC->SuspendPortProc(TNC, ThisTNC); + } +} + +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC) +{ + // Enable other TNCs in same Interlock Group + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (rxInterlock == 0 && txInterlock == 0) + return; + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc) + TNC->ReleasePortProc(TNC); + } +} + +VOID WinmorSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + if (TNC->CONNECTED) + send(TNC->TCPSock, "CODEC FALSE\r\n", 14, 0); + + if (TNC->Busy) + { + TNC->Busy = FALSE; // Can't clear detector if CODEC off. + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } +} + +VOID WinmorReleasePort(struct TNCINFO * TNC) +{ + if (TNC->CONNECTED) + send(TNC->TCPSock, "CODEC TRUE\r\n", 13, 0); +} + +extern char WebProcTemplate[]; +extern char sliderBit[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "WINMOR Status", "WINMOR Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +void * WinmorExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_WINMOR; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = WinmorSuspendPort; + TNC->ReleasePortProc = WinmorReleasePort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "DebugLog True\r\n"); + strcat(TempScript, "CWID False\r\n"); + strcat(TempScript, "BW 1600\r\n"); + strcat(TempScript, "ROBUST False\r\n"); + strcat(TempScript, "MODE AUTO\r\n"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + TNC->WL2KMode = 22; // in case not scanning + + // Set MYCALL + + strcat(TNC->InitScript,"FECRCV True\r\n"); + strcat(TNC->InitScript,"AUTOBREAK True\r\n"); + + sprintf(Msg, "MYC %s\r\nCODEC TRUE\r\nLISTEN TRUE\r\nMYC\r\n", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + strcat(TNC->InitScript,"PROCESSID\r\n"); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + *ptr++ = ','; + *ptr = 0; + } + + strcat(Aux, Appl); + } + } + strcat(TNC->InitScript, Aux); + strcat(TNC->InitScript,"\r\nMYAUX\r\n"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Winmor TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Winmor TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + Consoleprintf("WINMOR Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoWINMOR(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +int ConnecttoWINMOR(int port) +{ + _beginthread(WINMORThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID WINMORThread(void * portptr) +{ + // Opens both sockets and looks for data on control socket. Data socket is polled from BG, + // but we need fast response to control messages for PTT porcessing + + int port = (int)(size_t)portptr; + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + if (TNC->HostName == NULL) + return; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + } +#endif + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(TNC->TCPSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for WINMOR socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->CONNECTING = FALSE; + TNC->TCPSock = 0; + + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for WINMOR Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // Send INIT script + + send(TNC->TCPSock, TNC->InitScript , (int)strlen(TNC->InitScript), 0); + TNC->Alerted = TRUE; + + if (TNC->Hardware == H_V4) + sprintf(TNC->WEB_COMMSSTATE, "Connected to V4 TNC"); + else + sprintf(TNC->WEB_COMMSSTATE, "Connected to WINMOR TNC"); + + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + } + else + { + sprintf(Msg, "Connect Failed for WINMOR Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + TNC->HeartBeat = 0; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + printf("Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + if (TNC->Hardware == H_V4) + V4ProcessReceivedData(TNC); + else + ProcessReceivedData(TNC); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "WINMOR Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + return; + } + } + else + { + // 90 secs without data. Shouldn't happen + + sprintf(Msg, "WINMOR Connection Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + TNC->TCPDataSock = 0; + TNC->TCPSock= 0; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + return; + } + } +} + +#ifdef WIN32 + +BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"WINMOR Sound Card TNC", 21) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + TNC->hWnd = hwnd; // save so we can reset title when sessicn closes + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + // Response on WINMOR control channel. Could be a reply to a command, or + // an Async Response + + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 2] = 0; + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + Debugprintf(Buffer); + + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + return; + } + + Buffer[MsgLen - 2] = 0; // Remove CRLF + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + Debugprintf(Buffer); + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 2); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "CONNECTED", 9) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + + Debugprintf(Buffer); + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + if (TNC->StartInRobust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incomming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + GetSemaphore(&Semaphore, 50); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + FreeSemaphore(&Semaphore); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("WINMOR Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("WINMOR Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + + send(TNC->TCPDataSock, buffptr->Data, (int)buffptr->Len, 0); + STREAM->bytesTXed += (int)buffptr->Len; + WritetoTrace(TNC, buffptr->Data, (int)buffptr->Len); + ReleaseBuffer(buffptr); + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + Debugprintf(Buffer); + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "Winmor} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + if (TNC->RestartAfterFailure) + { + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + + return; + } + + + // Release Session + + if (TNC->Streams[0].Connected) + { + hookL4SessionDeleted(TNC, STREAM); + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "MONCALL", 7) == 0) + { + Debugprintf(Buffer); + + // Add to MHEARD + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + UpdateMH(TNC, &Buffer[8], '!', 0); + + if (!TNC->FECMode) + return; // If in FEC mode pass ID messages to user. + } + + if (_memicmp(Buffer, "CMD", 3) == 0) + { + return; + } + + if (_memicmp(Buffer, "BUFFERS", 7) == 0) + { + int inq, inrx, Sent, BPM; + + sscanf(&Buffer[8], "%d%d%d%d%d", &inq, &inrx, &TNC->Streams[0].BytesOutstanding, &Sent, &BPM); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } +// else +// if (TNC->TXRXState == 'S') +// send(TNC->TCPSock,"OVER\r\n", 6, 0); + + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + SetWindowText(TNC->xIDC_TRAFFIC, &Buffer[8]); + strcpy(TNC->WEB_TRAFFIC, &Buffer[8]); + return; + } + + Debugprintf(Buffer); + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + // Debugprintf("WINMOR RX: %s", Buffer); + + strcpy(TNC->WEB_MODE, &Buffer[5]); + GetSemaphore(&Semaphore, 50); + MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); + FreeSemaphore(&Semaphore); + return; + } + + if (_memicmp(Buffer, "PENDING", 6) == 0) + return; + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + TNC->WinmorRestartCodecTimer = time(NULL); + + SetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "CONNECTPENDING", 14) == 0) // Save Pending state for scan control + TNC->ConnectPending = TRUE; + else + TNC->ConnectPending = FALSE; + + if (_memicmp(&Buffer[9], "DISCONNECTING", 13) == 0) // So we can timout stuck discpending + { + TNC->DiscPending = 600; + return; + } + if (_memicmp(&Buffer[9], "DISCONNECTED", 12) == 0) + { + TNC->DiscPending = FALSE; + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->PID = atoi(&Buffer[10]); + +#ifdef WIN32 + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + // Set Window Title to reflect BPQ Port Description + + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "Winmor} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + FreeSemaphore(&Semaphore); +} + +static int ProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return 0; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &TNC->TCPBuffer[TNC->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); + TNC->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = TNC->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, TNC->TCPBuffer, MsgLen); + + ProcessResponse(TNC, Buffer, MsgLen); + memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + return 0; +} + + +VOID ProcessDataSocketData(int port) +{ + // Info on Data Socket - just packetize and send on + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int InputLen, PacLen = 236; + PMSGWITHLEN buffptr; + char * msg; + + TNC->TimeSinceLast = 0; + +loop: + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + InputLen = recv(TNC->TCPDataSock, buffptr->Data, PacLen, 0); + + if (InputLen == -1) + { + ReleaseBuffer(buffptr); + return; + } + + + //Debugprintf("Winmor: RXD %d bytes", InputLen); + + if (InputLen == 0) + { + // Does this mean closed? + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + ReleaseBuffer(buffptr); + return; + } + + STREAM->bytesRXed += InputLen; + + msg = &buffptr->Data[0]; + msg[InputLen] = 0; + + WritetoTrace(TNC, msg, InputLen); + + if (TNC->FECMode) + { + InputLen = (int)strlen(&buffptr->Data[0]); + + if (msg[InputLen - 1] == 3) // End of errored block + msg[InputLen++] = 13; // Add CR + + } + buffptr->Len = InputLen; + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + goto loop; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ + +#ifdef LINBPQ +#include +#include +#endif + + +int KillTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath && _memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + // Try to Kill TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + char Msg[256]; + int Len; + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->PID) + Len = sprintf(Msg, "KILL %d", TNC->PID); + else + Len = sprintf(Msg, "KILLBYNAME %s", &TNC->ProgramPath[7]); + + sendto(sock, Msg, Len, 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + Sleep(100); + closesocket(sock); + + TNC->PID = 0; // So we don't try again + return 1; // Cant tell if it worked, but assume ok + } + + if (TNC->PID == 0) + return 0; + +#ifdef WIN32 + { + HANDLE hProc; + + Debugprintf("KillTNC Called for Pid %d", TNC->PID); + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } +#else + + printf("KillTNC Called for Pid %d Returned %d\n", TNC->PID, kill(TNC->PID, SIGTERM)); + +#endif + TNC->PID = 0; // So we don't try again + + return 0; +} + +BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Extract any parameters from command string + +#ifndef WIN32 + { + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Copy, * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + Copy = _strdup(TNC->ProgramPath); // Save as strtok mangles it + + arg_list[0] = strtok_s(Copy, " \n\r", &Context); + if (arg_list[0]) + arg_list[1] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[1]) + arg_list[2] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[2]) + arg_list[3] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[3]) + arg_list[4] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[4]) + arg_list[5] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[5]) + arg_list[6] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[6]) + arg_list[7] = strtok_s(NULL, " \n\r", &Context); + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + free(Copy); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + else + { + TNC->PID = child_pid; + printf("Started TNC, Process ID = %d\n", TNC->PID); + } + free(Copy); + return TRUE; + } +#else + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + while (KillOldTNC(TNC->ProgramPath) && n++ < 100) + { + Sleep(100); + } + + if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + send(TNC->TCPSock,"DIRTYDISCONNECT\r\n", 17, 0); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + send(TNC->TCPSock,"SENDID 0\r\n", 10, 0); + } +} + +BOOL KillOldTNC(char * Path) +{ +#ifdef WIN32 + HANDLE hProc; + char ExeName[256] = ""; + DWORD Pid = 0; + + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (EnumProcessesPtr == NULL) + return FALSE; + + if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) + return FALSE; + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + + // Path could have parameters, so use memcmp + + if (_memicmp(ExeName, Path, strlen(ExeName)) == 0) + { + Debugprintf("Killing Pid %d %s", Processes[i], ExeName); + TerminateProcess(hProc, 0); + CloseHandle(hProc); + return TRUE; + } + CloseHandle(hProc); + } + } + } +#endif + return FALSE; +} diff --git a/.svn/pristine/24/245b5d264b812bc35275cb94be12d218bdde8695.svn-base b/.svn/pristine/24/245b5d264b812bc35275cb94be12d218bdde8695.svn-base new file mode 100644 index 0000000..e7b28c3 --- /dev/null +++ b/.svn/pristine/24/245b5d264b812bc35275cb94be12d218bdde8695.svn-base @@ -0,0 +1,217 @@ +// Includes code from MiniUPnPc, used subject to the following conditions: + +/* + +MiniUPnPc +Copyright (c) 2005-2020, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#define MINIUPNP_STATICLIB + +#include +#ifdef _WIN32 +#include "upnpcommands.h" +#include "miniupnpc.h" +#include "upnperrors.h" +#include +#endif +#ifdef LINBPQ +#ifndef MACBPQ +#ifndef WIN32 +#include +#include +#include +#include +#endif +#endif +#endif +#ifdef MACBPQ +#include +#include +#include +#include +#endif +int AddMap(char * controlURL, char * eport, char * iport, char * proto); +int DeleteMap(char * controlURL, char * eport, char * iport, char * proto); + +void Consoleprintf(const char * format, ...); + +struct UPNP +{ + struct UPNP * Next; + char * Protocol; + char * LANport; + char * WANPort; +}; + +extern struct UPNP * UPNPConfig; + +char * controlURL = 0; +char * servicetype = 0; +char iaddr[] = "IP"; +char * inClient = NULL; +#ifdef LINBPQ +char desc[] = "LinBPQ "; +#else +char desc[] = "BPQ32 "; +#endif +char * remoteHost = NULL; +char * leaseDuration = NULL; + +struct UPNPDev * devlist = 0; +char lanaddr[64] = "unset"; /* my ip address on the LAN */ +char wanaddr[64] = "unset"; /* my ip address on the LAN */ +struct UPNPUrls urls; +struct IGDdatas data; + +int i; +const char * rootdescurl = 0; +const char * multicastif = 0; +const char * minissdpdpath = 0; +#ifdef UPNP_LOCAL_PORT_ANY +int localport = UPNP_LOCAL_PORT_ANY; +#else +#pragma message "API 10" +int localport = 0; +#endif +int retcode = 0; +int error = 0; +int ipv6 = 0; +int ignore = 0; +unsigned char ttl = 2; + + +int upnpInit() +{ + struct UPNP * Config = UPNPConfig; + int i; +#ifdef WIN32 + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if(nResult != NO_ERROR) + { + fprintf(stderr, "WSAStartup() failed.\n"); + return -1; + } +#endif + + while (Config) + { + if (devlist == NULL) + { +#if MINIUPNPC_API_VERSION == 10 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error); +#else + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); +#endif + if (devlist == NULL) + { + Consoleprintf("Failed to find a UPNP device"); + return 0; + } + +#if MINIUPNPC_API_VERSION == 18 + i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr)); +#else + i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); +#endif + } + + AddMap(devlist->descURL, Config->LANport, Config->WANPort, Config->Protocol); + Config = Config->Next; + } + + return 0; +} + +int upnpClose() +{ + struct UPNP * Config = UPNPConfig; + int i; + + while (Config) + { + if (devlist == NULL) + { +#if MINIUPNPC_API_VERSION == 10 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error); +#else + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); +#endif + if (devlist == NULL) + { + Consoleprintf("Failed to find a UPNP device"); + return 0; + } + +#if MINIUPNPC_API_VERSION == 18 + i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr)); +#else + i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); +#endif + } + + DeleteMap(devlist->descURL, Config->LANport, Config->WANPort, Config->Protocol); + Config = Config->Next; + } + + return 0; +} + +int AddMap(char * controlURL, char * eport, char * iport, char * proto) +{ + int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + eport, iport, lanaddr, desc, + proto, remoteHost, leaseDuration); + + if (r != UPNPCOMMAND_SUCCESS) + { + Consoleprintf("UPNP AddPortMapping(%s, %s, %s) failed with code %d (%s)", eport, iport, lanaddr, r, strupnperror(r)); + return -2; + } + Consoleprintf("UPNP AddPortMapping(%s, %s, %s) Succeeded", eport, iport, lanaddr, r); + return 0; +} + +int DeleteMap(char * controlURL, char * eport, char * iport, char * proto) +{ + int r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport, proto, remoteHost); + + if(r != UPNPCOMMAND_SUCCESS) + { + Consoleprintf("UPNP DeletePortMapping(%s, %s, %s) failed with code %d (%s)", eport, iport, lanaddr, r, strupnperror(r)); + return -2; + } + Consoleprintf("UPNP DeletePortMapping(%s, %s, %s) Succeeded", eport, iport, lanaddr, r); + + return 0; +} + + + + diff --git a/.svn/pristine/29/29bf82bdcaa2f402119bc1df741e66162ee28862.svn-base b/.svn/pristine/29/29bf82bdcaa2f402119bc1df741e66162ee28862.svn-base new file mode 100644 index 0000000..6d7c6e4 --- /dev/null +++ b/.svn/pristine/29/29bf82bdcaa2f402119bc1df741e66162ee28862.svn-base @@ -0,0 +1,43 @@ +# LinBPQ Makefile + +# To exclude i2c support run make noi2c + +OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pngwutil.o\ + pngread.o pngwrite.o png.o pngerror.o pngget.o pngmem.o APRSIconData.o AISCommon.o\ + upnp.o APRSStdPages.o HSMODEM.o WinRPR.o KISSHF.o TNCEmulators.o bpqhdlc.o SerialPort.o\ + adif.o WebMail.o utf8Routines.o VARA.o LzFind.o Alloc.o LzmaDec.o LzmaEnc.o LzmaLib.o \ + Multicast.o ARDOP.o IPCode.o FLDigi.o linether.o CMSAuth.o APRSCode.o BPQtoAGW.o KAMPactor.o\ + AEAPactor.o HALDriver.o MULTIPSK.o BBSHTMLConfig.o ChatHTMLConfig.o BBSUtilities.o bpqaxip.o\ + BPQINP3.o BPQNRR.o cMain.o Cmd.o CommonCode.o HTMLCommonCode.o compatbits.o config.o datadefs.o \ + FBBRoutines.o HFCommon.o Housekeeping.o HTTPcode.o kiss.o L2Code.o L3Code.o L4Code.o lzhuf32.o \ + MailCommands.o MailDataDefs.o LinBPQ.o MailRouting.o MailTCP.o MBLRoutines.o md5.o Moncode.o \ + NNTPRoutines.o RigControl.o TelnetV6.o WINMOR.o TNCCode.o UZ7HODrv.o WPRoutines.o \ + SCSTrackeMulti.o SCSPactor.o SCSTracker.o HanksRT.o UIRoutines.o AGWAPI.o AGWMoncode.o \ + DRATS.o FreeDATA.o base64.o Events.o nodeapi.o mailapi.o mqtt.o RHP.o + +# Configuration: + +CC = gcc + +all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon -fasynchronous-unwind-tables +all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a +all: linbpq + + +nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables +nomqtt: linbpq + +noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables +noi2c: linbpq + + +linbpq: $(OBJS) + gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lrt -lm -lz $(LDFLAGS) -lpthread -lconfig -lpcap -o linbpq + sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq + +-include *.d + +clean : + rm *.d + rm linbpq $(OBJS) + diff --git a/.svn/pristine/60/602359ea1897ba7460f3a7cb65433525d6ebe1bf.svn-base b/.svn/pristine/60/602359ea1897ba7460f3a7cb65433525d6ebe1bf.svn-base new file mode 100644 index 0000000..901280e --- /dev/null +++ b/.svn/pristine/60/602359ea1897ba7460f3a7cb65433525d6ebe1bf.svn-base @@ -0,0 +1,2167 @@ +/* +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 +#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(); + + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); +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(); +void RHPPoll(); + +VOID GetPGConfig(); +void SendBBSDataToPktMap(); + +extern uint64_t timeLoadedMS; + +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 */ + + +extern UCHAR BPQDirectory[260]; +extern UCHAR LogDirectory[260]; +extern UCHAR ConfigDirectory[260]; + +// overrides from params +UCHAR LogDir[260] = ""; +UCHAR ConfigDir[260] = ""; +UCHAR DataDir[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(); + +static config_t cfg; +static 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; + +// Console Terminal Support + +struct ConTermS +{ + int BPQStream; + BOOL Active; + int Incoming; + + char kbbuf[INPUTLEN]; + int kbptr; + + char * KbdStack[MAXSTACK]; + int StackIndex; + + BOOL CONNECTED; + int SlowTimer; +}; + +struct ConTermS ConTerm = {0, 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 + +#include +#include + +// Linux Signal Handlers +static void segvhandler(int sig) +{ + void *array[10]; + size_t size; + char msg[] = "\nSIGSEGV Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); + + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + + backtrace_symbols_fd(array, size, STDERR_FILENO); + + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); +} + +static void abrthandler(int sig) +{ + void *array[10]; + size_t size; + char msg[] = "\nSIGABRT Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); + write(STDOUT_FILENO, msg, strlen(msg)); + + // get void*'s for all entries on the stack + + size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); +} + + +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 from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + + 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; +uint64_t 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]; + +BOOL EventsEnabled = 0; + +UCHAR * GetBPQDirectory() +{ + return BPQDirectory; +} +UCHAR * GetLogDirectory() +{ + return LogDirectory; +} +extern int POP3Timer; + +// Console Terminal Stuff + +#ifndef WIN32 + + + +#define _getch getchar + +/** + Linux (POSIX) implementation of _kbhit(). + Morgan McGuire, morgan@cs.brown.edu + */ + +#include +#include +#include + +int _kbhit() { + static const int STDIN = 0; + static int initialized = 0; + + if (! initialized) { + // Use termios to turn off line buffering + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = 1; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + +#endif + +void ConTermInput(char * Msg) +{ + int i; + + if (ConTerm.BPQStream == 0) + { + ConTerm.BPQStream = FindFreeStream(); + + if (ConTerm.BPQStream == 255) + { + ConTerm.BPQStream = 0; + printf("No Free Streams\n"); + return; + } + } + + if (!ConTerm.CONNECTED) + SessionControl(ConTerm.BPQStream, 1, 0); + + ConTerm.StackIndex = 0; + + // Stack it + + if (ConTerm.KbdStack[19]) + free(ConTerm.KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + ConTerm.KbdStack[i+1] = ConTerm.KbdStack[i]; + } + + ConTerm.KbdStack[0] = _strdup(ConTerm.kbbuf); + + ConTerm.kbbuf[ConTerm.kbptr]=13; + + SendMsg(ConTerm.BPQStream, ConTerm.kbbuf, ConTerm.kbptr+1); +} + +void ConTermPoll() +{ + int port, sesstype, paclen, maxframe, l4window, len; + int state, change, InputLen, count; + char callsign[11] = ""; + char Msg[300]; + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(ConTerm.BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + ConTerm.CONNECTED = TRUE; + ConTerm.SlowTimer = 0; + } + else + { + ConTerm.CONNECTED = FALSE; + printf("*** Disconnected\n"); + } + } + + GetMsg(ConTerm.BPQStream, Msg, &InputLen, &count); + + if (InputLen) + { + char * ptr = Msg; + char * ptr2 = ptr; + Msg[InputLen] = 0; + + while (ptr) + { + ptr2 = strlop(ptr, 13); + + // Replace CR with CRLF + + printf("%s", ptr); + + if (ptr2) + printf("\r\n"); + + ptr = ptr2; + } + } + + if (_kbhit()) + { + unsigned char c = _getch(); + + if (c == 0xe0) + { + // Cursor control + + c = _getch(); + + if (c == 75) // cursor left + c = 8; + } + +#ifdef WIN32 + printf("%c", c); +#endif + if (c == 8) + { + if (ConTerm.kbptr) + ConTerm.kbptr--; + printf(" \b"); // Already echoed bs - clear typed char from screen + return; + } + + if (c == 13 || c == 10) + { + ConTermInput(ConTerm.kbbuf); + ConTerm.kbptr = 0; + return; + } + + ConTerm.kbbuf[ConTerm.kbptr++] = c; + fflush(NULL); + + } + + return; + +} + +#include + +static struct option long_options[] = +{ + {"logdir", required_argument, 0 , 'l'}, + {"configdir", required_argument, 0 , 'c'}, + {"datadir", required_argument, 0 , 'd'}, + {"help", no_argument, 0 , 'h'}, + { NULL , no_argument , NULL , no_argument } +}; + +char HelpScreen[] = + "Usage:\n" + "Optional Paramters\n" + "-l path or --logdir path Path for log files\n" + "-c path or --configdir path Path to Config file bpq32.cfg\n" + "-d path or --datadir path Path to Data Files\n" + "-v Show version and exit\n"; + +int Redirected = 0; + +static void segvhandler(int sig); +static void abrthandler(int sig); + +void GetRestartData(); + + +int main(int argc, char * argv[]) +{ + int i; + struct UserInfo * user = NULL; + ConnectionInfo * conn; + struct stat STAT; + PEXTPORTDATA PORTVEC; + +#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 + + signal(SIGSEGV, segvhandler); + signal(SIGABRT, abrthandler); + + 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 + + // Disable Console Terminal if stdout redirected + +// printf("STDOUT %d\n",isatty(STDOUT_FILENO)); +// printf("STDIN %d\n",isatty(STDIN_FILENO)); + + if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) + Redirected = 1; + + timeLoadedMS = GetTickCount(); + +#endif + + printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); + printf("%s\n", VerCopyright); + + + // look for optarg format parameters + + { + int val; + UCHAR * ptr1; + UCHAR * ptr2; + int c; + + while (1) + { + int option_index = 0; + + c = getopt_long(argc, argv, "l:c:d:hv", long_options, &option_index); + + // Check for end of operation or error + + if (c == -1) + break; + + // Handle options + switch (c) + { + case 'h': + + printf("%s", HelpScreen); + exit (0); + + case 'l': + strcpy(LogDir, optarg); + printf("cc %s\n", LogDir); + break; + + case 'c': + strcpy(ConfigDir, optarg); + break; + + case 'd': + strcpy(DataDir, optarg); + break; + + + case '?': + /* getopt_long already printed an error message. */ + break; + + case 'v': + 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); +#else + getcwd(BPQDirectory, 256); +#endif + + strcpy(ConfigDirectory, BPQDirectory); + strcpy(LogDirectory, BPQDirectory); + + Consoleprintf("Current Directory is %s", BPQDirectory); + + if (LogDir[0]) + { + strcpy(LogDirectory, LogDir); + } + if (DataDir[0]) + { + strcpy(BPQDirectory, DataDir); + Consoleprintf("Working Directory is %s", BPQDirectory); + } + if (ConfigDir[0]) + { + strcpy(ConfigDirectory, ConfigDir); + Consoleprintf("Config Directory is %s", ConfigDirectory); + } + + for (i = optind; i < argc; i++) + { + if (_memicmp(argv[i], "logdir=", 7) == 0) + { + strcpy(LogDirectory, &argv[i][7]); + Consoleprintf("Log Directory is %s\n", LogDirectory); + break; + } + } + + Consoleprintf("Log Directory is %s", LogDirectory); + + // Make sure logs directory exists + + sprintf(LogDir, "%s/logs", LogDirectory); + +#ifdef WIN32 + CreateDirectory(LogDir, NULL); +#else + printf("Making Directory %s\n", LogDir); + i = mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + if (i == -1 && errno != EEXIST) + { + perror("Couldn't create log directory\n"); + return 0; + } + 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 = optind; 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 = optind; 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(); + GetPGConfig(); + GetRestartData(); + + // 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"); + + InitialiseTCP(); + 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 = optind; 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"); + + APIClock = 0; + + SendBBSDataToPktMap(); + + } + } + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (Redirected == 0) + ConTerm.BPQStream = FindFreeStream(); + + +#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); + SaveRestartData(); + } + + 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 (Redirected == 0) + ConTermPoll(); + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + 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 % 20) == 0) + FWDTimerProc(); + + if (Slowtimer > 100) // 10 secs + { + time_t NOW = time(NULL); + struct tm * tm; + + TCPTimer(); + BBSSlowTimer(); + + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + + if (APIClock < NOW) + { + SendBBSDataToPktMap(); + APIClock = NOW + 7200; // Every 2 hours + } + + + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + 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 * KISSHFExtInit(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 +#ifndef MACBPQ + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; +#endif +#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, "KISSHF")) + return KISSHFExtInit; + + 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 + +#ifndef MACBPQ +#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 +#endif + + +uint64_t 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/.svn/pristine/6c/6cd7b080146a010c7b19d3474dd95106c4091790.svn-base b/.svn/pristine/6c/6cd7b080146a010c7b19d3474dd95106c4091790.svn-base new file mode 100644 index 0000000..57207f5 --- /dev/null +++ b/.svn/pristine/6c/6cd7b080146a010c7b19d3474dd95106c4091790.svn-base @@ -0,0 +1,3121 @@ +/* +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 + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +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); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT); +VOID NETROMMSG(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG); + +#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 int SemHeldByAPI; + +static RECT Rect; +extern void * TRACE_Q; + +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; + + TNC->Streams[0].Disconnecting = FALSE; + + 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, "VARAAC", 6) == 0) + TNC->VaraACAllowed = atoi(&buf[7]); + + 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); + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +void doVarACSend(struct TNCINFO * TNC) +{ + int hdrlen; + int txlen = strlen(TNC->VARACMsg); + char txbuff[64]; + + txlen--; // remove cr + hdrlen = sprintf(txbuff, "%d ", txlen); + send(TNC->TCPDataSock, txbuff, hdrlen, 0); // send length + send(TNC->TCPDataSock, TNC->VARACMsg, txlen, 0); + + free (TNC->VARACMsg); + TNC->VARACMsg = 0; + TNC->VARACSize = 0; + + TNC->VarACTimer = 0; + return ; +} +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes; + size_t 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 + + if (TNC->VarACTimer) + { + TNC->VarACTimer--; + + if (TNC->VarACTimer == 0) + doVarACSend(TNC); + } + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + VARASendCommand(TNC, "CLEANTXBUFFER\r", TRUE); + VARASendCommand(TNC, "DISCONNECT\r", TRUE); + STREAM->Disconnecting = TRUE; + TNC->SessionTimeLimit += 120; // Don't retrigger unless things have gone horribly wrong + } + } + + // Check ATTACH time limit + + if (STREAM->Attached) + { + if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) + { + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + + 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; + TNC->Streams[0].ConnectTime = time(NULL); + + 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; + TNC->Streams[0].ConnectTime = time(NULL); + + 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]; + + memset(STREAM, 0, sizeof(struct STREAMINFO)); + + 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 + STREAM->AttachTime = time(NULL); + + // 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( (TRANSPORTENTRY *) -1, Msg); + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + 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; + + if(TNC->VaraACMode || TNC->VaraModeSet == 0) + { + // Send in varac format - + + // 5 Hello, 15 de G8 BPQ + + buffptr->Data[txlen] = 0; // Null terminate + + STREAM->bytesTXed += txlen; + WritetoTrace(TNC, buffptr->Data, txlen); + + // Always add to stored data and set timer. If it expires send message + + if (TNC->VARACMsg == 0) + { + TNC->VARACMsg = zalloc(4096); + TNC->VARACSize = 4096; + } + else + { + if (strlen(TNC->VARACMsg) + txlen >= (TNC->VARACSize - 10)) + { + TNC->VARACSize += 4096; + TNC->VARACMsg = realloc(TNC->VARACMsg, TNC->VARACSize); + } + } + + strcat(TNC->VARACMsg, buffptr->Data); + + TNC->VarACTimer = 10; // One second + return 0; + } + + + + else + 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 = sprintf(buffptr->Data,"No Connection to VARA TNC\r"); + + 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) + { + unsigned char txbuff[512]; + + STREAM->PacketsSent++; + + if(TNC->VaraACMode == 0 && TNC->VaraModeSet == 1) + { + // Normal Send + + memcpy(txbuff, buff->L2DATA, txlen); + + bytes=send(TNC->TCPDataSock, txbuff, txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, buff->L2DATA, txlen); + return 0; + } + + // Send in varac format - len space data. No cr on end, but is implied + + // 5 Hello + + // I think we have to send a whole message (something terminated with a new line) + // may need to combine packets. Also I think we need to combine seqential sends + // (eg CTEXT and SID) + + + buff->L2DATA[txlen] = 0; // Null terminate + + STREAM->bytesTXed += txlen; + WritetoTrace(TNC, buff->L2DATA, txlen); + + // Always add to stored data and set timer. If it expires send message + + if (TNC->VARACMsg == 0) + { + TNC->VARACMsg = zalloc(4096); + TNC->VARACSize = 4096; + } + else + { + if (strlen(TNC->VARACMsg) + txlen >= (TNC->VARACSize - 10)) + { + TNC->VARACSize += 4096; + TNC->VARACMsg = realloc(TNC->VARACMsg, TNC->VARACSize); + } + } + + strcat(TNC->VARACMsg, buff->L2DATA); + + TNC->VarACTimer = 10; // One second + return 0; + } + 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, 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; + + hookL4SessionAttempt(STREAM, &buff->L2DATA[2], 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; + + VARASendCommand(TNC, Connect, TRUE); + TNC->Streams[0].ConnectTime = time(NULL); + + + 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) + { + if (TNC->Streams[0].Connected) + VARASendCommand(TNC, "ABORT\r", TRUE); +// 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

"; + +char Menubit[] = "" + "\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 (LOCAL) + Len += sprintf(&Buff[Len], Menubit, TNC->TXOffset, TNC->TXOffset); + + 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, struct TNCINFO * ThisTNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + +VOID VARAReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + + +void * VARAExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + int line; + APPLCALLS * APPL; + struct TNCINFO * TNC; + int AuxCount = 0; + char Appl[11]; + char * TempScript; + 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->PortRecord = PortEntry; + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(8192); + + if (TNC->ProgramPath) + TNC->WeStartedTNC = 1; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_VARA; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->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 + + if (TNC->NRNeighbour) + { + // NETROM over VARA Link + + TNC->NetRomMode = 1; + TNC->LISTENCALLS = MYNETROMCALL; + PORT->PortNoKeepAlive = 1; + TNC->DummyLink = zalloc(sizeof(struct _LINKTABLE)); + TNC->DummyLink->LINKPORT = &TNC->PortRecord->PORTCONTROL; + } + else + PORT->PORTQUALITY = 0; + + 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, 550, 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; + } + +// 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; + + 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; + TNC->ConnectPending = 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 + + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active) + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + return; + } + + if (_memicmp(Buffer, "PTT OFF", 6) == 0) + { +// Debugprintf("PTT Off"); + + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + + 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 + + TNC->BusyonTime = GetTickCount(); + + 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"); + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + + + 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->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + if (TNC->VARACMsg) + free(TNC->VARACMsg); + + TNC->VaraACMode = 0; + TNC->VARACMsg = 0; + TNC->VARACSize = 0; + if (TNC->VaraACAllowed == 0) + TNC->VaraModeSet = 1; // definitly not varaac + else + TNC->VaraModeSet = 0; // Don't know yet + + 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 || (TNC->NetRomMode && STREAM->Connecting == 0)) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + // Stop other ports in same group + + memset(STREAM, 0, sizeof(struct STREAMINFO)); + + STREAM->ConnectTime = time(NULL); + + SuspendOtherPorts(TNC); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Only allow VarAC mode for incomming sessions + + ProcessIncommingConnectEx(TNC, Call, 0, (TNC->NetRomMode == 0), 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((TRANSPORTENTRY *) -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((TRANSPORTENTRY *) -1, Status); + Debugprintf("VARA Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + if (TNC->NetRomMode) + { + // send any queued data + + int bytes; + + if (TNC->NetRomTxLen) + { + + STREAM->PacketsSent++; + + bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); + STREAM->bytesTXed += TNC->NetRomTxLen; + + free(TNC->NetRomTxBuffer); + TNC->NetRomTxBuffer = NULL; + TNC->NetRomTxLen = 0; + } + 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]; + char AppBuffer[64]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct 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(AppBuffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, AppBuffer, MsgLen); + + Debugprintf("Calling Application %s", AppBuffer); + + 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 + } + } + + strcpy(STREAM->MyCall, TNC->TargetCall); + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + STREAM->ConnectTime = time(NULL); + + if (TNC->NetRomMode) + { + // send any queued data + + int bytes; + + if (TNC->NetRomTxLen) + { + STREAM->PacketsSent++; + + bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); + STREAM->bytesTXed += TNC->NetRomTxLen; + free(TNC->NetRomTxBuffer); + TNC->NetRomTxBuffer = NULL; + TNC->NetRomTxLen = 0; + } + } + else + { + TNC->VaraACMode = 0; + TNC->VaraModeSet = 1; // Don't allow connect to VaraAC + + 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", STREAM->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 Session + + if (STREAM->Connected && STREAM->ConnectTime) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + } + + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + VARAReleaseTNC(TNC); + + STREAM->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, "LINK REGISTERED", 9) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "ENCRYPTION ", 11) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "UNENCRYPTED LINK ", 11) == 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[TNC->DataInputLen], 8192 - TNC->DataInputLen, 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; + + TNC->Streams[0].Disconnecting = FALSE; + + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + + return; + } + + TNC->DataInputLen += InputLen; + + if (TNC->NetRomMode) + { + // Unpack KISS frames from data stream + + unsigned char c; + int n = 0; + int Len = TNC->DataInputLen; + unsigned char KISSBuffer[600]; + int KissLen = 0; + + while (Len) + { + Len--; + + c = TNC->ARDOPDataBuffer[n++]; + + 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 (KissLen == 0) + { + // Start of Message. + + continue; + } + + // Have a complete KISS frame - remove from buffer and process + + if (KISSBuffer[0] == 255) + { + // NODE Message + + MESSAGE * Msg = GetBuff(); + + if (Msg) + { + // Set up header + + Msg->LENGTH = KissLen + (Msg->L2DATA - (unsigned char *)Msg); + memcpy(Msg->L2DATA, KISSBuffer, KissLen); + ConvToAX25(TNC->NRNeighbour, Msg->ORIGIN); + memcpy(Msg->DEST, NETROMCALL, 7); + + PROCESSNODEMESSAGE(Msg, &TNC->PortRecord->PORTCONTROL); + + Msg->PID = 0xcf; + Msg->PORT = TNC->Port | 0x80; + Msg->CTL = 3; + Msg->DEST[6] |= 0x80; // set Command Bit + Msg->ORIGIN[6] |= 1; // set end of address + time(&Msg->Timestamp); + BPQTRACE(Msg, FALSE); + } + } + else + { + // Netrom Message + + L3MESSAGEBUFFER * L3MSG = GetBuff(); + MESSAGE * Buffer = GetBuff(); + + if (L3MSG) + { + // Set up header + + L3MSG->LENGTH = KissLen + (L3MSG->L3SRCE - (unsigned char *)L3MSG); + memcpy(L3MSG->L3SRCE, KISSBuffer, KissLen); + L3MSG->L3PID = 0xcf; + + // Create copy to pass to monitor + // To trace we need to reformat as MESSAGE + + Buffer->PID = 0xcf; + Buffer->PORT = TNC->Port; + Buffer->CTL = 3; + Buffer->LENGTH = KissLen + (Buffer->L2DATA - (unsigned char *)Buffer); + memcpy(Buffer->L2DATA, KISSBuffer, KissLen); + + ConvToAX25(TNC->NRNeighbour, Buffer->DEST); + memcpy(Buffer->ORIGIN, NETROMCALL, 7); + Buffer->ORIGIN[6] |= 1; // set end of address + Buffer->DEST[6] |= 0x80; // set Command Bit + + time(&Buffer->Timestamp); + BPQTRACE(Buffer, FALSE); // TRACE + NETROMMSG(TNC->DummyLink, L3MSG); + } + } + + if (Len == 0) // All used + { + TNC->DataInputLen = 0; + } + else + { + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[n], Len); + TNC->DataInputLen = Len; + KissLen = 0; + n = 0; + } + + continue; + + case FESC: + + TNC->ESCFLAG = TRUE; + continue; + + } + } + + // Ok, a normal char + + KISSBuffer[KissLen++] = c; + + if (KissLen > 590) + KissLen = 0; + + } + + // End of input - if there is stuff left in the input buffer we will add the next block to it + + return; + } + + VARAProcessDataPacket(TNC, TNC->ARDOPDataBuffer, TNC->DataInputLen); + TNC->DataInputLen = 0; + return; +} + + + +VOID VARAProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[8192]; + + // 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 == 0 || InputLen == SOCKET_ERROR) + { + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + + TNC->ARDOPBuffer[TNC->InputLen] = 0; + Debugprintf("VARA Processing buffer - %s", TNC->ARDOPBuffer); + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + { + Debugprintf("VARA Part Packet Received - Waiting for rest"); + return; // Wait for it + } + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR + { + // Usual Case - single msg 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; + Debugprintf("VARA Corrupt multi command input"); + 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); + + // if VARAAC Mode, remove byte count from front and add cr + // could possibly be longer than buffer size + + if (TNC->VaraModeSet == 0) // Could be normal or VaraAC + { + unsigned char *ptr = memchr(Data, ' ', Length); // contains a space + + if (ptr) + { + int ACLen = atoi(Data); + int lenLen = (ptr - Data) + 1; + + if (ACLen == (Length - lenLen)) + TNC->VaraACMode = 1; // AC Mode + } + TNC->VaraModeSet = 1; // Know which mode + } + + if (TNC->VaraACMode) + { + char * lenp; + char * msg; + int len; + + lenp = Data; + msg = strlop(lenp, ' '); + + len = atoi(lenp); + if (len != strlen(msg)) + return; + + msg[len++] = 13; + msg[len] = 0; + + Length = len; + memmove(Data, msg, len + 1); + + } + + // 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( (TRANSPORTENTRY *) -1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); +} + +void SendVARANetrom(struct TNCINFO * TNC, unsigned char * Data, int Len) +{ + // Check that PID is 0xcf, then just send the data portion of packet + + // We need to delimit packets, and KISS encoding seems as good as any. Also + // need to buffer to avoid sending lots of small packets and maybe to wait for + // link to connect + + unsigned char Kiss[600]; + int KissLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (TNC->CONNECTED == 0) + return; // Don't Queue if no connection to TNC + + KissLen = KissEncode(Data, Kiss, Len); + + TNC->NetRomTxBuffer = realloc(TNC->NetRomTxBuffer, TNC->NetRomTxLen + KissLen); + memcpy(&TNC->NetRomTxBuffer[TNC->NetRomTxLen], Kiss, KissLen); + TNC->NetRomTxLen += KissLen; + + if (STREAM->Connected) + { + int bytes; + + STREAM->PacketsSent++; + + bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); + STREAM->bytesTXed += TNC->NetRomTxLen; + + free(TNC->NetRomTxBuffer); + TNC->NetRomTxBuffer = NULL; + TNC->NetRomTxLen = 0; + return; + } + + if (TNC->Streams[0].Connecting == 0 && TNC->Streams[0].Connected == 0) + { + // Try to connect to Neighbour + + char Connect[32]; + + TNC->NetRomMode = 1; + + sprintf(Connect, "CONNECT %s %s\r", MYNETROMCALL, TNC->NRNeighbour); + + // 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; + } + } + TNC->OverrideBusy = FALSE; + + VARASendCommand(TNC, Connect, TRUE); + TNC->Streams[0].ConnectTime = time(NULL); + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, MYNETROMCALL); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } +} + +void SendVARANetromNodes(struct TNCINFO * TNC, MESSAGE *Buffer) +{ + // Check that PID is 0xcf, then just send the data portion of packet + + int Len = Buffer->LENGTH - (Buffer->L2DATA - (unsigned char *)Buffer); + + if (Buffer->PID != 0xcf) + { + ReleaseBuffer(Buffer); + return; + } + + SendVARANetrom(TNC, Buffer->L2DATA, Len); + time(&Buffer->Timestamp); + + C_Q_ADD(&TRACE_Q, Buffer); + +} + +void SendVARANetromMsg(struct TNCINFO * TNC, L3MESSAGEBUFFER * MSG) +{ + MESSAGE * Buffer = (MESSAGE *)MSG; + + int Len = MSG->LENGTH - (MSG->L3SRCE - (unsigned char *)MSG); + + if (MSG->L3PID != 0xcf) + { + ReleaseBuffer(MSG); + return; + } + + SendVARANetrom(TNC, MSG->L3SRCE, Len); + + memmove(Buffer->L2DATA, MSG->L3SRCE, Len); + + // To trace we need to reformat as MESSAGE + + Buffer->PID = 0xcf; + Buffer->PORT = TNC->Port | 0x80; + Buffer->CTL = 3; + Buffer->LENGTH = Len + (Buffer->L2DATA - (unsigned char *)Buffer); + ConvToAX25(TNC->NRNeighbour, Buffer->DEST); + memcpy(Buffer->ORIGIN, NETROMCALL, 7); + Buffer->ORIGIN[6] |= 1; // set end of address + Buffer->DEST[6] |= 0x80; // set Command Bit + + + time(&Buffer->Timestamp); + + C_Q_ADD(&TRACE_Q, Buffer); + +} + + + + + + diff --git a/.svn/pristine/6e/6e158308d0b061edf74eec91683457323986b811.svn-base b/.svn/pristine/6e/6e158308d0b061edf74eec91683457323986b811.svn-base new file mode 100644 index 0000000..a9e524d --- /dev/null +++ b/.svn/pristine/6e/6e158308d0b061edf74eec91683457323986b811.svn-base @@ -0,0 +1,6439 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// 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 int SemHeldByAPI; + +static RECT Rect; + +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; + + TNC->Streams[0].Connecting = 0; + TNC->Streams[0].Connected = 0; + TNC->Streams[0].Attached = 0; + + 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); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + 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(struct TNCINFO * TNC); +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); + + +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; + + if (memcmp(Buff, "LISTEN ", 7) == 0) + { + strcpy(TNC->WEB_MODE, &Buff[7]); + MySetWindowText(TNC->xIDC_MODE, &Buff[7]); + } + + 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 + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + Debugprintf("ARDOP closing session on SessionTimelimit"); + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + // Check ATTACH time limit + + if (STREAM->Attached) + { + if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) + { + Debugprintf("ARDOP closing session on AttachTimelimit"); + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + 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 session active + + ReleaseBuffer(buffptr); + continue; + } + + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + *ptr++ = '^'; // delimit frame with ^ + + // 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 frame with ^ + + 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; + STREAM->AttachTime = time(NULL); + + 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( (TRANSPORTENTRY *) -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) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cmd); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (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); + + hookL4SessionAttempt(STREAM, &buff->L2DATA[2], 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; + STREAM->ConnectTime = time(NULL); + 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); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID ARDOPSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + +VOID ARDOPReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + +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
Listen%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; + TNC->PortRecord = PortEntry; + + 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->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_ARDOP; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + 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", "Listen", 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->ReportDISC = TRUE; + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + + } +} + + +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; + + ARDOPSendCommand(TNC, "LISTEN TRUE", 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); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + + 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); + + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->PTTState = FALSE; + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + TNC->BusyonTime = GetTickCount(); + + 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"); + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + + 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 + STREAM->AttachTime = time(NULL); + + // 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( (TRANSPORTENTRY *) -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( (TRANSPORTENTRY *) -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(struct 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 + } + } + strcpy(STREAM->MyCall, TNC->TargetCall); + 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 END ARQ CALL", 19) == 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); + } + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + + } + + + 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 to user + } + + 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; + + 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; + char Call[10] = ""; + + 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); + + memcpy(Call,ptr1, 9); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + + ReleaseBuffer(buffptr); + + } + 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 %d Complete - Entering Hostmode", TNC->Port); + + 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(struct 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); + strcat(Msg, "\n); + + 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/.svn/pristine/88/88aab017ad7d5100707ee83b6973c8d6b39d06af.svn-base b/.svn/pristine/88/88aab017ad7d5100707ee83b6973c8d6b39d06af.svn-base new file mode 100644 index 0000000..f1c3dcd --- /dev/null +++ b/.svn/pristine/88/88aab017ad7d5100707ee83b6973c8d6b39d06af.svn-base @@ -0,0 +1,5280 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#define DllImport + +#include "cheaders.h" +#include + +#include "tncinfo.h" +#include "time.h" +#include "bpq32.h" +#include "telnetserver.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + +#define CKernel +#include "httpconnectioninfo.h" + +extern int MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS, L3FRAMES; +extern int NUMBEROFNODES, MAXDESTS, L4CONNECTSOUT, L4CONNECTSIN, L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES; +extern int STATSTIME; +extern TRANSPORTENTRY * L4TABLE; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern BOOL APRSApplConnected; +extern char VersionString[]; +VOID FormatTime3(char * Time, time_t cTime); +DllExport int APIENTRY Get_APPLMASK(int Stream); +VOID SaveUIConfig(); +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID SetupUI(int Port); +VOID SendUIBeacon(int Port); +VOID GetParam(char * input, char * key, char * value); +VOID ARDOPAbort(struct TNCINFO * TNC); +VOID WriteMiniDump(); +BOOL KillTNC(struct TNCINFO * TNC); +BOOL RestartTNC(struct TNCINFO * TNC); +int GetAISPageInfo(char * Buffer, int ais, int adsb); +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +char * stristr (char *ch1, char *ch2); +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL); +char * GetStandardPage(char * FN, int * Len); +BOOL SHA1PasswordHash(char * String, char * Hash); +char * byte_base64_encode(char *str, int len); +int APIProcessHTTPMessage(char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); +int RHPProcessHTTPMessage(struct ConnectionInfo * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + +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 char * RigWebPage; +extern COLORREF Colours[256]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; + +extern BOOL APRSWeb; +extern BOOL RigActive; + +extern HKEY REGTREE; + +extern BOOL APRSActive; + +extern UCHAR LogDirectory[]; + +extern struct RIGPORTINFO * PORTInfo[34]; +extern int NumberofPorts; + +extern UCHAR ConfigDirectory[260]; + +extern struct AXIPPORTINFO * Portlist[]; + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); +int CompareRoutes(const void * a, const void * b); + +void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen, char * Token); +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +int SetupNodeMenu(char * Buff, int SYSOP); +int StatusProc(char * Buff); +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessMailAPISignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE); + + +static struct HTTPConnectionInfo * SessionList; // active term mode sessions + +char Mycall[10]; + +char MAILPipeFileName[] = "\\\\.\\pipe\\BPQMAILWebPipe"; +char CHATPipeFileName[] = "\\\\.\\pipe\\BPQCHATWebPipe"; + +char Index[] = "%s's BPQ32 Web Server

" +"" +"" +"
Node PagesAPRS Pages
"; + +char IndexNoAPRS[] = "" +""; + +//char APRSBit[] = "APRS Pages"; + +//char MailBit[] = "Mail Mgmt" +// "WebMail"; +//char ChatBit[] = "Chat Mgmt"; + +char Tail[] = ""; + +char RouteHddr[] = "

Routes

" +""; + +char RouteLine[] = ""; +char xNodeHddr[] = "
" +"
PortCallQualityNode CountFrame CountRetriesPercentMaxframeFrackLast HeardQueuedRem Qual
%s%d%s%s%d%d%d%d%d%%d%d%02d:%02d%d%d
" +"
" +"" +"
" +"

Nodes %s

"; + +char NodeHddr[] = "
" +"" +"" +"
" +"

Nodes %s

"; + +char NodeLine[] = ""; + + +char StatsHddr[] = "

Node Stats

%s:%s
" +""; + +char PortStatsHddr[] = "

Stats for Port %d

"; + +char PortStatsLine[] = ""; + + +char Beacons[] = "

Beacon Configuration for Port %d

You need to be signed in to save changes

%s %d
" +"" +"
" +"" +"" +"" +"" +"" +"
Send Interval (Minutes)
To
Path
Send From File
Text
" +"" + +"

" +""; + + +char LinkHddr[] = "

Links

" +""; + +char LinkLine[] = ""; + +char UserHddr[] = "

Sessions

Far CallOur CallPortax.25 stateLink Typeax.25 Version
%s%s%d%s%s%d
"; + +char UserLine[] = ""; + +char TermSignon[] = "BPQ32 Node %s Terminal Access" +"

BPQ32 Node %s Terminal Access

" +"

Please enter username and password to access the node

" +"" +"
%s%s%s
" +"" +"
User
Password
" +"

" +""; + + +char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +char BusyError[] = "

Sorry, No sessions available - please try later

"; + +char LostSession[] = "Sorry, Session had been lost - refresh page to sign in again"; +char NoSessions[] = "Sorry, No Sessions available - refresh page to try again"; + +char TermPage[] = "" +"BPQ32 Node %s" +"" +"" +"

BPQ32 Node %s

" +"
" +"

" +"" +"" +""; + +char TermOutput[] = "" +"" +"" +"" +"" +"" +"" +"
\""; + + +// font-family:monospace;background-color:black;color:lawngreen;font-size:12px + +char TermOutputTail[] = "
"; + +/* +char InputLine[] = "" +"
" +"" +"
"; +*/ +char InputLine[] = "" +"
" +"\" id=inp type=text text width=100%% name=input />" +"
"; + +static char NodeSignon[] = "BPQ32 Node SYSOP Access" +"

BPQ32 Node %s SYSOP Access

" +"

This page sets Cookies. Don't continue if you object to this

" +"

Please enter Callsign and Password to access the Node

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

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

BPQ32 Mail Server %s Access

" +"

Please enter Callsign and Password to access the BBS

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

"; + +static char ChatSignon[] = "BPQ32 Chat Server Access" +"

BPQ32 Chat Server %s Access

" +"

Please enter Callsign and Password to access the Chat Server

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

"; + + +static char MailLostSession[] = "" +"
" +"Sorry, Session had been lost

    " +"
"; + + +static char ConfigEditPage[] = "" +"Edit Config" +"
" +"

" +"
"; + +static char EXCEPTMSG[80] = ""; + + +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; +} + + + + +VOID PollSession(struct HTTPConnectionInfo * Session) +{ + int state, change; + int count, len; + char Msg[400] = ""; + char Formatted[8192]; + char * ptr1, * ptr2; + char c; + int Line; + + // Poll Node + + SessionState(Session->Stream, &state, &change); + + if (change == 1) + { + int Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + if (state == 1)// Connected + Session->ScreenLines[Line] = _strdup("*** Connected
\r\n"); + else + Session->ScreenLines[Line] = _strdup("*** Disconnected
\r\n"); + + if (Line == 99) + Session->LastLine = 0; + + Session->Changed = TRUE; + } + + if (RXCount(Session->Stream) > 0) + { + int realLen = 0; + + do + { + GetMsg(Session->Stream, &Msg[0], &len, &count); + + // replace cr with
and space with   + + + ptr1 = Msg; + ptr2 = &Formatted[0]; + + if (Session->PartLine) + { + // Last line was incomplete - append to it + + realLen = Session->PartLine; + + Line = Session->LastLine - 1; + + if (Line < 0) + Line = 99; + + strcpy(Formatted, Session->ScreenLines[Line]); + ptr2 += strlen(Formatted); + + Session->LastLine = Line; + Session->PartLine = FALSE; + } + + while (len--) + { + c = *(ptr1++); + realLen++; + + if (c == 13) + { + int LineLen; + + strcpy(ptr2, "
\r\n"); + + // Write to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + LineLen = (int)strlen(Formatted); + + // if line starts with a colour code, process it + + if (Formatted[0] == 0x1b && LineLen > 1) + { + int ColourCode = Formatted[1] - 10; + COLORREF Colour = Colours[ColourCode]; + char ColString[30]; + + memmove(&Formatted[20], &Formatted[2], LineLen); + sprintf(ColString, "", GetRValue(Colour), GetGValue(Colour), GetBValue(Colour)); + memcpy(Formatted, ColString, 20); + strcat(Formatted, ""); + LineLen =+ 28; + } + + Session->ScreenLineLen[Line] = LineLen; + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + + } + else if (c == 32) + { + memcpy(ptr2, " ", 6); + ptr2 += 6; + + // Make sure line isn't too long + // but beware of spaces expanded to   - count chars in line + + if ((realLen) > 100) + { + strcpy(ptr2, "
\r\n"); + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + } + } + else if (c == '>') + { + memcpy(ptr2, ">", 4); + ptr2 += 4; + } + else if (c == '<') + { + memcpy(ptr2, "<", 4); + ptr2 += 4; + } + else + *(ptr2++) = c; + + } + + *ptr2 = 0; + + if (ptr2 != &Formatted[0]) + { + // Incomplete line + + // Save to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + Session->PartLine = realLen; + } + + // strcat(Session->ScreenBuffer, Formatted); + Session->Changed = TRUE; + + } while (count > 0); + } +} + + +VOID HTTPTimer() +{ + // Run every tick. Check for status change and data available + + struct HTTPConnectionInfo * Session = SessionList; // active term mode sessions + struct HTTPConnectionInfo * PreviousSession = NULL; + +// inf(); + + while (Session) + { + Session->KillTimer++; + + if (Session->Key[0] != 'T') + { + PreviousSession = Session; + Session = Session->Next; + continue; + } + + if (Session->KillTimer > 3000) // Around 5 mins + { + int i; + int Stream = Session->Stream; + + for (i = 0; i < 100; i++) + { + free(Session->ScreenLines[i]); + } + + SessionControl(Stream, 2, 0); + SessionState(Stream, &i, &i); + DeallocateStream(Stream); + + if (PreviousSession) + PreviousSession->Next = Session->Next; // Remove from chain + else + SessionList = Session->Next; + + free(Session); + + break; + } + + PollSession(Session); + + // if (Session->ResponseTimer == 0 && Session->Changed) + // Debugprintf("Data to send but no outstanding GET"); + + if (Session->ResponseTimer) + { + Session->ResponseTimer--; + + if (Session->ResponseTimer == 0 || Session->Changed) + { + SOCKET sock = Session->sock; + char _REPLYBUFFER[100000]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int Last = Session->LastLine; + int n; + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + if ((strlen(Session->ScreenLines[n]) + strlen(_REPLYBUFFER)) < 99999) + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + 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(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + Session->ResponseTimer = Session->Changed = 0; + } + } + PreviousSession = Session; + Session = Session->Next; + } +} + +struct HTTPConnectionInfo * AllocateSession(SOCKET sock, char Mode) +{ + time_t KeyVal; + struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo)); + int i; + + if (Session == NULL) + return NULL; + + if (Mode == 'T') + { + // Terminal + + for (i = 0; i < 20; i++) + Session->ScreenLines[i] = _strdup("Scroll to end
"); + + for (i = 20; i < 100; i++) + Session->ScreenLines[i] = _strdup("
\r\n"); + + Session->Stream = FindFreeStream(); + + if (Session->Stream == 0) + return NULL; + + SessionControl(Session->Stream, 1, 0); + } + + KeyVal = (int)sock * time(NULL); + + sprintf(Session->Key, "%c%012X", Mode, (int)KeyVal); + + if (SessionList) + Session->Next = SessionList; + + SessionList = Session; + + return Session; +} + +struct HTTPConnectionInfo * FindSession(char * Key) +{ + struct HTTPConnectionInfo * Session = SessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + return Session; + + Session = Session->Next; + } + + return NULL; +} + +void ProcessTermInput(SOCKET sock, char * MsgPtr, int MsgLen, char * Key) +{ + char _REPLYBUFFER[2048]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int State; + struct HTTPConnectionInfo * Session = FindSession(Key); + int Stream; + int maxlen = 1000; + + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * end = &MsgPtr[MsgLen]; + int Line = Session->LastLine++; + char * ptr1, * ptr2; + char c; + UCHAR hex; + + int msglen = end - input; + + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + maxlen -= strlen(TCP->WebTermCSS); + + if (MsgLen > maxlen) + { + Session->KillTimer = 99999; // close session + return; + } + + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + + + Stream = Session->Stream; + + input += 10; + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (ptr1 != end) + { + c = *(ptr1++); + 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; + } + + end = ptr2; + + *ptr2 = 0; + + strcat(input, "
\r\n"); + + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(input); + + if (Line == 99) + Session->LastLine = 0; + + *end++ = 13; + *end = 0; + + SessionStateNoAck(Stream, &State); + + if (State == 0) + { + char AXCall[10]; + SessionControl(Stream, 1, 0); + if (BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION == NULL) + { + //No L4 sessions free + + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoSessions); + 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; + } + + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Stream, AXCall); + if (Session->USER) + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = Session->USER->Secure; + else + Debugprintf("HTTP Term Session->USER is NULL"); + } + + SendMsg(Stream, input, (int)(end - input)); + Session->Changed = TRUE; + Session->KillTimer = 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)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + + +void ProcessTermClose(SOCKET sock, char * MsgPtr, int MsgLen, char * Key, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo * Session = FindSession(Key); + + if (Session) + { + Session->KillTimer = 99999; + } + + 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); +} + +int ProcessTermSignon(struct TNCINFO * TNC, SOCKET sock, char * MsgPtr, int MsgLen, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Context, * Appl; + char NoApp[] = ""; + struct TCPINFO * TCP = TNC->TCPInfo; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + goto Sendit; + } + user = strtok_s(&input[9], "&", &Context); + password = strtok_s(NULL, "=", &Context); + password = strtok_s(NULL, "&", &Context); + + Appl = strtok_s(NULL, "=", &Context); + Appl = strtok_s(NULL, "&", &Context); + + if (Appl == 0) + Appl = NoApp; + + if (password == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + goto Sendit; + } + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if ((strcmp(password, USER->Password) == 0) && + ((_stricmp(user, USER->UserName) == 0 ) || (_stricmp(USER->UserName, "ANON") == 0))) + { + // ok + + struct HTTPConnectionInfo * Session = AllocateSession(sock, 'T'); + + if (Session) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, Session->Key, Session->Key, Session->Key); + if (_stricmp(USER->UserName, "ANON") == 0) + strcpy(Session->HTTPCall, _strupr(user)); + else + strcpy(Session->HTTPCall, USER->Callsign); + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Session->Stream, AXCall); + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + + if (USER->Appl[0]) + SendMsg(Session->Stream, USER->Appl, (int)strlen(USER->Appl)); + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + break; + } + } + + if (i == TCP->NumberofUsers) + { + // Not found + + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + } + + } + +Sendit: + + 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; +} + +char * LookupKey(char * Key) +{ + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + { + char Mycall[10]; + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + return _strdup(Mycall); + } + return NULL; +} + + +int ProcessSpecialPage(char * Buffer, int FileSize) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(100000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + int PrevLen; + int BytesLeft = FileSize; + int NewFileSize = FileSize; + char * StripPtr = ptr1; + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + 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, "css") == 0) + strcpy(Type, "Content-Type: text/css\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, "bmp") == 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; +} + +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 <= MAXBPQPORTS; 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 (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + 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 = 0, * 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 WIN32xx + + 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: websocket"); + + 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, ' '); + + + // Look for API messages + + if (_memicmp(Context, "/api/", 5) == 0 || _stricmp(Context, "/api") == 0) + { + char * Compressed; + + // if for mail api process signon here and rearrange url from + // api/v1/mail to mail/api/v1 so it goes to mail handler later + + if (_memicmp(Context, "/api/v1/mail/", 13) == 0) + { + memcpy(MsgPtr, "GET /mail/api/v1/", 17); + + if (memcmp(&Context[13], "login", 5) == 0) + { + ReplyLen = ProcessMailAPISignon(TCP, MsgPtr, "M", Reply, &Session, FALSE, LOCAL); + memcpy(MsgPtr, "GET /mail/api/v1/", 17); + + if (ReplyLen) // Error message + goto Returnit; + } + + memcpy(Context, "/mail/api/v1/", 13); + goto doHeader; + } + else + { + ReplyLen = APIProcessHTTPMessage(_REPLYBUFFER, Method, Context, MsgPtr, LOCAL, COOKIE); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Message - just send it + + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + return 0; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + + if (_memicmp(Context, "/rhp/", 5) == 0 || _stricmp(Context, "/rhp") == 0) + { + { + ReplyLen = RHPProcessHTTPMessage(conn, _REPLYBUFFER, Method, Context, MsgPtr, LOCAL, COOKIE); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Message - just send it + + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + return 0; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + + + // 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 && _memicmp(Context, "/Mail/API/", 10) != 0) + { + 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; + } + + if (NodeURL && _memicmp(NodeURL, "/mail/api/", 10) != 0) + { + // Add tail + + strcpy(&Reply[ReplyLen], Tail); + ReplyLen += strlen(Tail); + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(Reply, ReplyLen, &ReplyLen); + else + Compressed = Reply; + + if (NodeURL && _memicmp(NodeURL, "/mail/api/", 10) == 0) + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: application/json\r\nConnection: close\r\n%s\r\n", ReplyLen, Encoding); + else + 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; + char token[16] = ""; + + // look for auth header + + const char * auth_header = "Authorization: Bearer "; + char * token_begin = strstr(MsgPtr, auth_header); + int Flags = 0, n; + + char * Tok; + char * param; + + if (token_begin) + { + // Using Auth Header + + // Extract the token from the request (assuming it's present in the request headers) + + token_begin += strlen(auth_header); // Move to the beginning of the token + strncpy(token, token_begin, 13); + token[13] = '\0'; // Null-terminate the token + } + + ReplyLen = 0; + + if (Session == 0) + Session = &Dummy; + + if (LOCAL) + Session->TNC = (void *)1; // TNC only used for Web Terminal Sessions + else + Session->TNC = (void *)0; + + ProcessMailHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen, MsgLen, token); + + if (Context && _memicmp(Context, "/mail/api/", 10) == 0) + { + 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; + } + + // compress if allowed + + 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: application/json\r\nConnection: close\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + 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; + } + + if (Context && _memicmp(Context, "/mail/api/", 10) != 0) + { + + // Add tail + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += strlen(Tail); + + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, 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; + + +/* + + 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 + + NodeURL = Context; + + 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; + + if (LOCAL) + Session->TNC = (struct TNCINFO *)(uintptr_t)1; // TNC is only used on Web Terminal Sessions so can reuse as LOCAL flag + else + Session->TNC = 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, 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 <= MaxBPQPortNo) + { + 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 <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC == 0) + return 1; + + if (LOCAL == FALSE && COOKIE == FALSE) + 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[0]); + PORT = GetPortTableEntryFromPortNum(Port); // Need slot not number + if (PORT) + Slot = PORT->PortSlot; + + GetParam(input, "Every=", &Param[0]); + Interval[Slot] = atoi(&Param[0]); + + GetParam(input, "Dest=", &Param[0]); + _strupr(Param); + strcpy(UIUIDEST[Slot], &Param[0]); + + GetParam(input, "Path=", &Param[0]); + _strupr(Param); + if (UIUIDigi[Slot]) + free(UIUIDigi[Slot]); + UIUIDigi[Slot] = _strdup(&Param[0]); + + GetParam(input, "File=", &Param[0]); + strcpy(FN[Slot], &Param[0]); + GetParam(input, "Text=", &Param[0]); + strcpy(Message[Slot], &Param[0]); + + 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 <= MaxBPQPortNo) + { + 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); + + if (ReplyLen < 240000) + 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\nAccess-Control-Allow-Origin: *\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, "/portstats.txt", 15) == 0) + { + char * Compressed; + char * ptr; + int port; + struct PORTCONTROL * PORT; + + ptr = &NodeURL[15]; + + port = atoi(ptr); + + PORT = GetPortTableEntryFromPortNum(port); + + ReplyLen = 0; + + if (PORT && PORT->TX) + { + // We send the last 24 hours worth of data. Buffer is cyclic so oldest byte is at StatsPointer + + int first = PORT->StatsPointer; + int firstlen = 1440 - first; + + memcpy(&_REPLYBUFFER[0], &PORT->TX[first], firstlen); + memcpy(&_REPLYBUFFER[firstlen], PORT->TX, first); + + memcpy(&_REPLYBUFFER[1440], &PORT->BUSY[first], firstlen); + memcpy(&_REPLYBUFFER[1440 + firstlen], PORT->BUSY, first); + + ReplyLen = 2880; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\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 <= MaxBPQPortNo) + { + 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 <= MaxBPQPortNo) + { + 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 (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + 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]; + char StatsURL[64]; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PortsHddr); + + for (count = 1; count <= NUMBEROFPORTS; count++) + { + Port = GetPortTableEntryFromSlot(count); + ExtPort = (struct _EXTPORTDATA *)Port; + + // see if has a stats page + + if (Port->AVACTIVE) + sprintf(StatsURL, " Stats Graph", Port->PORTNUMBER); + else + StatsURL[0] = 0; + + 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, StatsURL); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); + + 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, StatsURL); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeacon, Port->PORTNUMBER, Port->PORTNUMBER, + DLL, DLL, Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); + } + + if (RigActive) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigControlLine, 64, "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 WindowStats Graph
%d %s%s
%d %s%s Beacons %s
%d%s%s %s
%d%s%s Driver Window%s
%d%s%s BeaconsDriver Window%s
%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 + + */ + + // AXIP Partners + + if (_stricmp(NodeURL, "/Node/AXIP.html") == 0) + { + int i; + char Normcall[10]; + int Width = 5; + int x = 0, n = 0, nd = 0; + struct arp_table_entry * List[1000]; + struct arp_table_entry * ListD[1000]; + char AXIPList[10000] = ""; + int ListLen = 0; + + struct AXIPPORTINFO * AXPORT = Portlist[0]; + struct PORTCONTROL * PORT = PORTTABLE; + struct arp_table_entry * arp; + time_t NOW = time(NULL); + + char AXIPHeader[] = + "" + "
AXIP UpAXIP Down
%s"; + + + while (PORT) + { + AXPORT = Portlist[PORT->PORTNUMBER]; + + if (AXPORT) + { + // Get ARP entries + + for (i = 0; i < AXPORT->arp_table_len; i++) + { + arp = &AXPORT->arp_table[i]; + + if (arp->LastHeard == 0 || (NOW - arp->LastHeard) > 3600) // Considered down + ListD[nd++] = arp; + else + List[n++] = arp; + } + } + PORT = PORT->PORTPOINTER; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareNode); + if (nd > 1) + qsort(ListD, nd, sizeof(void *), CompareNode); + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->callsign, Normcall); + Normcall[len]=0; + + ListLen += sprintf(&AXIPList[ListLen], "%02d - %s %d
", i + 1, Normcall, (List[i]->LastHeard)?(NOW - List[i]->LastHeard):0); + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], AXIPHeader, AXIPList); + + ListLen = 0; + + for (i = 0; i < nd; i++) + { + int len = ConvFromAX25(ListD[i]->callsign, Normcall); + Normcall[len]=0; + ListLen += sprintf(&AXIPList[ListLen], "%02d - %s %d
", i + 1, Normcall, (ListD[i]->LastHeard)?(NOW - ListD[i]->LastHeard):0); + } + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
%s", AXIPList); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
"); + } + + if (_stricmp(NodeURL, "/Node/Routes.html") == 0) + { + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int count, i; + char Normcall[10]; + char locked[4] = " "; + int NodeCount; + int Percent = 0; + int Iframes, Retries; + char Active[10]; + int Queued; + + int x = 0, n = 0; + struct ROUTE * List[1000]; + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); + + // Build and sort list of routes + + for (count = 0; count < MaxRoutes; count++) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + { + List[n++] = Routes; + + if (n > 999) + break; + } + + Routes++; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareRoutes); + + for (i = 0; i < n; i++) + { + Routes = List[i]; + { + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + Normcall[len]=0; + + if (Routes->NEIGHBOUR_FLAG == LOCKEDBYCONFIG) + strcpy(locked, "!"); + else if (Routes->NEIGHBOUR_FLAG == LOCKEDBYSYSOP) + strcpy(locked, "!!"); + else if (Routes->NEIGHBOUR_FLAG == LOCKEDBYSYSOP + LOCKEDBYCONFIG) + strcpy(locked, "!!!"); + else + strcpy(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 WIN32xx + } +#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 ProcessMailAPISignon(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; + struct HTTPConnectionInfo * NewSession; + int i; + struct UserRec * USER; + + if (strchr(MsgPtr, '?')) + { + // Check Password + + user = strlop(MsgPtr, '?'); + password = strlop(user, '&'); + strlop(password, ' '); + + 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; + + } + } + } + + // Pass failed attempt to BBS code so it can try a bbs user login + + // Need to put url back together + + if (user && user[0] && password && password[0]) + { + sprintf(&MsgPtr[strlen(MsgPtr)], "?%s&%s", user, password); + } + } + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + ReplyLen = 0; + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + + 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; + +} + +#define SHA1_HASH_LEN 20 + +/* + +Copyright (C) 1998, 2009 +Paul E. Jones + +Freeware Public License (FPL) + +This software is licensed as "freeware." Permission to distribute +this software in source and binary forms, including incorporation +into other products, is hereby granted without a fee. THIS SOFTWARE +IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD +LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER +DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA +OR DATA BEING RENDERED INACCURATE. +*/ + +/* sha1.h + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, const unsigned char *, unsigned); + +#endif + +BOOL SHA1PasswordHash(char * lpszPassword, char * Hash) +{ + SHA1Context sha; + int i; + + SHA1Reset(&sha); + SHA1Input(&sha, lpszPassword, strlen(lpszPassword)); + SHA1Result(&sha); + + // swap byte order if little endian + + for (i = 0; i < 5; i++) + sha.Message_Digest[i] = htonl(sha.Message_Digest[i]); + + memcpy(Hash, &sha.Message_Digest[0], 20); + + return TRUE; +} + +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 i, n; + struct ConnectionInfo * sockptr; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + for (i = 0; i < 33; i++) + { + TNC = TNCInfo[i]; + + 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); + } + } + } + } + } + } +} + +// Webmail web socket code + +int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); + +void ProcessWebmailWebSockThread(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + struct ConnectionInfo * sockptr = (struct ConnectionInfo *)conn; + char * URL = sockptr->WebURL; + int Loops = 0; + int Sent; + struct HTTPConnectionInfo Dummy = {0}; + int ReplyLen = 0; + int InputLen = 0; + +#ifdef LINBPQ + + char _REPLYBUFFER[250000]; + + ReplyLen = ProcessWebmailWebSock(URL, _REPLYBUFFER); + + // Send may block + + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { + ReplyLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + } + +#else + // Send URL to BPQMail via Pipe. Just need a dummy session, as URL contains session key + + HANDLE hPipe; + char Reply[250000]; + + + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + free(conn); + return; + } + + WriteFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, URL, strlen(URL), &InputLen, NULL); + + ReadFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + + // ?? do we need a thread to handle write which may block + + Sent = send(sockptr->socket, 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(sockptr->socket, Reply, ReplyLen, 0); + } +#endif + free(conn); + return; +} + +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} + + diff --git a/.svn/pristine/a9/a90e52328b0cbe547f7e4f7aaa5d5ad4c71b40a7.svn-base b/.svn/pristine/a9/a90e52328b0cbe547f7e4f7aaa5d5ad4c71b40a7.svn-base new file mode 100644 index 0000000..997107e --- /dev/null +++ b/.svn/pristine/a9/a90e52328b0cbe547f7e4f7aaa5d5ad4c71b40a7.svn-base @@ -0,0 +1,5865 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. S"paclenee 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" +#include "kiss.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, struct CMDX * CMD); +VOID WriteMiniDump(); +int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive); +int seeifInterlockneeded(struct PORTCONTROL * PORT); +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); +int CompareRoutes(const void * a, const void * b); + + +extern VOID KISSTX(struct KISSINFO * KISS, PMESSAGE Buffer); + +char COMMANDBUFFER[81] = ""; // Command Hander input buffer +char OrigCmdBuffer[81] = ""; // Command Hander input buffer before toupper + +struct DATAMESSAGE * REPLYBUFFER = NULL; +UINT APPLMASK = 0; +UCHAR SAVEDAPPLFLAGS = 0; + +UCHAR ALIASINVOKED = 0; + + +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(struct _LINKTABLE * LINK); +VOID RESET2(struct _LINKTABLE * LINK); + +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; + + + +struct CMDX COMMANDS[]; + +int CMDXLEN = sizeof (struct CMDX); + +VOID SENDNODESMSG(); +VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD); +void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD); +VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD); +void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); + + + +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, struct 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, struct CMDX * CMD) +{ + SaveMH(); + + strcpy(Bufferptr, OKMSG); + Bufferptr += (int)strlen(OKMSG); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SAVENODES(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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, struct 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, struct 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, struct 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, struct 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, struct 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, struct CMDX * CMD); + +VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + VALNODESFLAG = 1; + PORTVAL(Session, Bufferptr, CmdTail, CMD); +} + +VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + EXTONLY = 1; + PORTVAL(Session, Bufferptr, CmdTail, CMD); +} +VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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, struct 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, struct 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, struct 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, struct CMDX * CMD) +{ + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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, struct CMDX * CMD) +{ + CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link + ReleaseBuffer((UINT *)REPLYBUFFER); + return; +} + +VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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, struct 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, struct 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, struct 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, struct CMDX * CMD) +{ + char * ptr, *Context; + int Port = 0, cols = NUMBEROFPORTS, i; + 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) + { + struct tm * TM; + char UPTime[50]; + time_t szClock = STATSTIME * 60; + + TM = gmtime(&szClock); + + sprintf(UPTime, "Uptime (Days Hours Mins) %.2d:%.2d:%.2d\r", + TM->tm_yday, TM->tm_hour, TM->tm_min); + + 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 %% "); + Bufferptr = Cmdprintf(Session, Bufferptr, "Active(TX/Busy) %%"); + + 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, struct 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, struct 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)); +} + +extern int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK or UZ7HO host + +VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // Process PORTS Message + + // If extended show state of TNC (Open, Active, etc) + + struct PORTCONTROL * PORT = PORTTABLE; + char Extended = CmdTail[0]; + struct PORTCONTROL * SAVEPORT; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r"); + + while (PORT) + { + char Status[32] = "???????"; + int Portno = PORT->PORTNUMBER; + + if (PORT->Hide) + { + PORT = PORT->PORTPOINTER; + continue; + } + + if (Extended != 'E') + { + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION); + + PORT = PORT->PORTPOINTER; + continue; + } + + // Try to get port status - may not be possible with some + + if (PORT->PortStopped) + { + strcpy(Status, "Stopped"); + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %-7s %s\r", PORT->PORTNUMBER, Status, PORT->PORTDESCRIPTION); + + PORT = PORT->PORTPOINTER; + continue; + } + + if (PORT->PORTTYPE == 0) + { + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + NPASYINFO Port; + + SAVEPORT = PORT; + + if (KISS->FIRSTPORT && KISS->FIRSTPORT != KISS) + { + // Not first port on device + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; + Port = KISSInfo[Portno]; + } + + Port = KISSInfo[PORT->PORTNUMBER]; + + if (Port) + { + // KISS like - see if connected + + if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) + { + // KISS over UDP or TCP + + if (PORT->KISSTCP) + { + if (Port->Connected) + strcpy(Status, "Open "); + else + if (PORT->KISSSLAVE) + strcpy(Status, "Listen"); + else + strcpy(Status, "Closed"); + } + else + strcpy(Status, "UDP"); + } + else + if (Port->idComDev) // Serial port Open + strcpy(Status, "Open "); + else + strcpy(Status, "Closed"); + + PORT = SAVEPORT; + } + } + else if (PORT->PORTTYPE == 14) // Loopback + strcpy(Status, "Open "); + + else if (PORT->PORTTYPE == 16) // External + { + if (PORT->PROTOCOL == 10) // 'HF' Port + { + struct TNCINFO * TNC = TNCInfo[Portno]; + + if (TNC == NULL) + { + PORT = PORT->PORTPOINTER; + continue; + } + + switch (TNC->Hardware) // Hardware Type + { + case H_SCS: + case H_KAM: + case H_AEA: + case H_HAL: + case H_TRK: + case H_SERIAL: + + // Serial + + if (TNC->hDevice) + strcpy(Status, "Open "); + else + strcpy(Status, "Closed"); + + break; + + case H_UZ7HO: + + if (TNCInfo[MasterPort[Portno]]->CONNECTED) + strcpy(Status, "Open "); + else + strcpy(Status, "Closed"); + + break; + + case H_WINMOR: + case H_V4: + + case H_MPSK: + case H_FLDIGI: + case H_UIARQ: + case H_ARDOP: + case H_VARA: + case H_KISSHF: + case H_WINRPR: + case H_FREEDATA: + + // TCP + + if (TNC->CONNECTED) + { + if (TNC->Streams[0].Attached) + strcpy(Status, "In Use"); + else + strcpy(Status, "Open "); + } + else + strcpy(Status, "Closed"); + + break; + + case H_TELNET: + + strcpy(Status, "Open "); + } + } + else + { + // External but not HF - AXIP, BPQETHER VKISS, ?? + + struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT; + + strcpy(Status, "Open "); + } + } + + Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %-7s %s\r", PORT->PORTNUMBER, Status, 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[4] = " "; + 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 == LOCKEDBYCONFIG) + strcpy(locked, "!"); + else if (Routes->NEIGHBOUR_FLAG == LOCKEDBYSYSOP) + strcpy(locked, "!!"); + else if (Routes->NEIGHBOUR_FLAG == LOCKEDBYSYSOP + LOCKEDBYCONFIG) + 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, struct 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; + int count, i, n = 0; + struct ROUTE * List[1000]; + + 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"); + + // Build and sort list of routes + + for (count = 0; count < MaxRoutes; count++) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + { + List[n++] = Routes; + + if (n > 999) + break; + } + + Routes++; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareRoutes); + + for (i = 0; i < n; i++) + { + Routes = List[i]; + + if (Port == 0 || Port == Routes->NEIGHBOUR_PORT) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose); + } + 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 ^= LOCKEDBYSYSOP; // 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 ^= LOCKEDBYSYSOP; // FLIP LOCKED BIT + goto Displayit; + } + } + +Displayit: + + // Just display + + if (Found) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); + +SendReply: + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // PROCESS LISTEN COMMAND + + // for monitoring a remote ax.25 port + + int Port = 0, index =0; + uint64_t ListenMask = 0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + char ListenPortList[128] = ""; + int len = 0; + + 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 port 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; + } + } + + len += sprintf(&ListenPortList[len], " %d", Port); + + ListenMask |= ((uint64_t)1 << (Port - 1)); + } + + Session->LISTEN = ListenMask; + + if (ListenMask) + { + if (CountBits64(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, struct 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, struct 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, struct CMDX * CMD) +{ + // Send a CQ Beacon on a radio port. Must be in LISTEN state + + DIGIMESSAGE Msg; + int Port = 0; + int OneBits = 0; + uint64_t 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); + Msg.DEST[6] |= 0x80; // set Command Bit + 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, struct CMDX * UserCMD); + +VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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 && 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; + } + + ret = CheckKissInterlock(PORT, TRUE); + + if (ret) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Interlocked port %d is in use\r", ret); + 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 + { + seeifInterlockneeded(PORT); + + 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, struct 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)); +} + +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, struct 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, struct CMDX * UserCMD) +{ + // DISPLAY AVAILABLE COMMANDS + + int n; + char * ptr; + char ApplList[2048]; + char * out = ApplList; + + struct 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, struct 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 == NULL || ptr[0] == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number needed eg MH 1\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + 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' || CMD->String[3] == '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(TRANSPORTENTRY * Session, char * Command); + +VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD) +{ + char * ptr; + + if (Rig_Command(Session, 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, struct 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, struct CMDX * UserCMD) +{ + // ATTACH to a PACTOR or similar port + + TRANSPORTENTRY * NewSess; + struct _EXTPORTDATA * EXTPORT; + struct TNCINFO * TNC = 0; + + 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 (Disconnecting)\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 <= MAXBPQPORTS; i++) + { + OtherTNC = TNCInfo[i]; + + if (OtherTNC == NULL) + continue; + + if (OtherTNC == TNC) + continue; + + if (rxInterlock && rxInterlock == OtherTNC->RXRadio || txInterlock && txInterlock == OtherTNC->TXRadio) // Same Group + { + int n; + + for (n = 0; n <= 26; n++) + { + if (OtherTNC->PortRecord->ATTACHEDSESSIONS[n]) + { + 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 (Session Attached\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (PORT->PortSuspended) + { + // In use + + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port Suspended\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 set freq on attach is defined, do it + + if (TNC && TNC->ActiveRXFreq && TNC->RXRadio) + { + char Msg[128]; + + sprintf(Msg, "R%d %f", TNC->RXRadio, TNC->ActiveRXFreq); + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (TNC && TNC->ActiveTXFreq && TNC->TXRadio && TNC->TXRadio != TNC->RXRadio) + { + char Msg[128]; + + sprintf(Msg, "R%d %f", TNC->TXRadio, TNC->ActiveTXFreq); + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + 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 + +struct CMDX COMMANDS[] = +{ +// "SAVENODES ",8, SAVENODES(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD), 0, + "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, + "HELP ",1,HELPCMD,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, + "MHUV ",3,MHCMD,0, // UTC Times + "MHLV ",3,MHCMD,0, // Local Times + "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, + "UZ7HO ",5,UZ7HOCMD,0, + "QTSM ",4,QTSMCMD,0, + + "..FLMSG ",7,FLMSG,0 +}; + +struct CMDX * CMD = NULL; + +int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(struct 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) + { +// char LongMsg[512] = +// "VeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessage" +// "VeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessage"; + + 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); + Msg.DEST[6] |= 0x80; // set Command Bit + 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 + +// memcpy(&Msg.PID + 1, LongMsg, 260); +// Send_AX_Datagram(&Msg, 241, Port); // Len is Payload - CTL, PID and Data + + +// SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO); + + ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply + + // Assume we don't allow multiple lines in buffer with UI + + if (Session->PARTCMDBUFFER) + { + Buffer = Session->PARTCMDBUFFER; + ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply + Session->PARTCMDBUFFER = NULL; + } + 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; + uint64_t sum, sum2; + + // Interval is 60 secs + + while(PORT) + { + int index = PORT->StatsPointer++; + + if (index == 1439) + PORT->StatsPointer = 0; // Cyclic through 24 hours (1440 Mins) + + if (PORT->TNC) + { + struct TNCINFO * TNC = PORT->TNC; + if (TNC->Hardware == H_ARDOP || TNC->Hardware == H_VARA) + { + sum = TNC->PTTActivemS / 600; // ms but want % + PORT->AVSENDING = (UCHAR)sum; + TNC->PTTActivemS = 0; + + sum2 = TNC->BusyActivemS / 600; // ms but want % + PORT->AVACTIVE = (UCHAR)(sum + sum2); + TNC->BusyActivemS = 0; + } + } + else + { + // if KISS port using QtSM Average is already updated + + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + + if (PORT->PORTNUMBER == 17) + { + int x = 17; + } + + if ((void *)PORT->PORTTXROUTINE == (void *)KISSTX && (KISS->QtSMStats || KISS->FIRSTPORT->PORT.QtSMPort)) // KISS Port QtSM Stats + { + } + else + { + sum = PORT->SENDING / 11; + PORT->AVSENDING = (UCHAR)sum; + + sum = (PORT->SENDING + PORT->ACTIVE) /11; + PORT->AVACTIVE = (UCHAR)sum; + } + } + + if (PORT->TX == NULL && PORT->AVACTIVE) + { + PORT->TX = zalloc(1440); // Keep 1 day history + PORT->BUSY = zalloc(1440); + } + if (PORT->TX) + { + PORT->TX[index] = PORT->AVSENDING; + PORT->BUSY[index] = PORT->AVACTIVE; + } + + PORT->SENDING = 0; + PORT->ACTIVE = 0; + + PORT = PORT->PORTPOINTER; + } +} + + + +extern struct AXIPPORTINFO * Portlist[]; + +#define TCPConnected 4 + + +VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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, struct 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 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, struct 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); + +VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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, struct 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, struct 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, struct 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)) + 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; +} + + + +int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + char _REPLYBUFFER[1000] = ""; + char * ptr, * Context; + + int portno = 0; + struct PORTCONTROL * PORT = PORTTABLE; + int n = NUMBEROFPORTS; + UCHAR KissString[128]; + UCHAR ENCBUFF[256]; + int KissLen = 0; + unsigned char * Kissptr = KissString; + + // Send KISS Command to TNC + + // Get port number + + ptr = strtok_s(CmdTail, " ", &Context); + + if (ptr) + { + portno = atoi (ptr); + ptr = strtok_s(NULL, " ", &Context); + + while (ptr && ptr[0] && KissLen < 120) + { + *(Kissptr++) = atoi (ptr); + KissLen++; + ptr = strtok_s(NULL, " ", &Context); + + } + } + + if (portno == 0 || KissLen == 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; + + 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; + + // Send Command + + KissLen = KissEncode(KissString, ENCBUFF, KissLen); + + PORT->Session = Session; + PORT->LastKISSCmdTime = time(NULL); + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q + ASYSEND(PORT, ENCBUFF, KissLen); + + 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, struct 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, struct 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, struct 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; +} + +VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1, * ptr, * ptr2; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BPQDirectory, "NodeHelp.txt"); + + if (stat(MsgFile, &STAT) == -1) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + MsgBytes = malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize] = 0; + + ptr1 = MsgBytes; + + // Replace LF or CRLF with CR + + // First remove cr from crlf + + while(ptr2 = strstr(ptr1, "\r\n")) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + } + + // Now replace lf with cr + + ptr1 = MsgBytes; + + while (*ptr1) + { + if (*ptr1 == '\n') + *(ptr1) = '\r'; + + ptr1++; + } + + ptr = ptr1 = MsgBytes; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + // Read and send a line at a time, converting any line endings into CR + + while (*ptr1) + { + if (*ptr1 == '\r') + { + *(ptr1++) = 0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", ptr); + + ptr = ptr1; + } + else + ptr1++; + } + + free(MsgBytes); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int UZ7HOSetFreq(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr); +int UZ7HOSetModem(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr); +int UZ7HOSetFlags(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr); + + +VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + char * Cmd; + int port; + struct TNCINFO * TNC; + struct AGWINFO * AGW = 0; + PDATAMESSAGE buff; + PMSGWITHLEN buffptr; + + CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version + + Cmd = strlop(CmdTail, ' '); + port = atoi(CmdTail); + + // remove trailing spaces + + while(strlen(Cmd) && Cmd[strlen(Cmd) - 1] == ' ') + Cmd[strlen(Cmd) - 1] = 0; + + TNC = TNCInfo[port]; + + if (TNC) + AGW = TNC->AGWInfo; + + if (TNC == 0 || AGW == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - %d is not UZ7HO port\r", port); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (_memicmp(Cmd, "FREQ", 4) == 0 || _memicmp(Cmd, "MODEM", 5) == 0 || _memicmp(Cmd, "FLAGS", 5) == 0) + { + // Pass to procesing code in UZ7HO driver. This expects command in a PDATAMESSAGE amd places response in a PMSGWITHLEN buffer + + buff = (PDATAMESSAGE)GetBuff(); + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "UZ7HO Command Failed - no buffers\r"); + if (buff) + ReleaseBuffer(buff); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + + + buff->LENGTH = sprintf(buff->L2DATA, "%s\r", Cmd) + MSGHDDRLEN + 1; + + if (_memicmp(Cmd, "FREQ", 4) == 0) + UZ7HOSetFreq(port, TNC, AGW, buff, buffptr); + else if (_memicmp(Cmd, "FLAGS", 5) == 0) + UZ7HOSetFlags(port, TNC, AGW, buff, buffptr); + else + UZ7HOSetModem(port, TNC, AGW, buff, buffptr); + + + Bufferptr = Cmdprintf(Session, Bufferptr, buffptr->Data); + + ReleaseBuffer(buff); + ReleaseBuffer(buffptr); + } + else + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid UZ7HO Command (not Freq Modem or FLAGS)\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + +VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + int port; + struct PORTCONTROL * PORT; + struct KISSINFO * KISS; + + CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version + + port = atoi(CmdTail); + + PORT = GetPortTableEntryFromPortNum(port); + + if (PORT == NULL || (void *)PORT->PORTTXROUTINE != (void *)KISSTX) // Must be a kiss like port + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d is not a KISS port\r", port); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + KISS = (struct KISSINFO *)PORT; + + if (KISS->QtSMModem == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d has no QtSM information\r", port); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Modem %s Centre frequency %d\r", + (KISS->QtSMModem) ? KISS->QtSMModem : "Not Available", KISS->QtSMFreq); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; +} + + + + + + + + + + + + + diff --git a/.svn/pristine/ad/ad1d515c632ceb9b93c89ae688ba6c75c292d65d.svn-base b/.svn/pristine/ad/ad1d515c632ceb9b93c89ae688ba6c75c292d65d.svn-base new file mode 100644 index 0000000..7e00c10 --- /dev/null +++ b/.svn/pristine/ad/ad1d515c632ceb9b93c89ae688ba6c75c292d65d.svn-base @@ -0,0 +1,5662 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +// 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 +#include "mqtt.h" + +#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[71]; // 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); +void SendDataToPktMap(); + +extern BOOL LogAllConnects; +extern BOOL M0LTEMap; + +char * stristr (char *ch1, char *ch2); + +extern VOID * ENDBUFFERPOOL; + + +// 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; + UINT CodeDump[16]; + int i; + unsigned int rev; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore from %s Line %d", File, Line); + + // Make sure address is within pool + + if ((uintptr_t)BUFF < (uintptr_t)BUFFERPOOL || (uintptr_t)BUFF > (uintptr_t)ENDBUFFERPOOL) + { + // Not pointing to a buffer . debug points to the buffer that this is chained from + + // Dump first chunk and source tag + + memcpy(CodeDump, BUFF, 64); + + Debugprintf("Releasebuffer Buffer not in pool from %s Line %d, ptr %p prev %d", File, Line, BUFF, 0); + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + Bufferlist[n], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf(" %08x %08x %08x %08x %08x %08x %08x %08x", + CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); + + + return 0; + } + + 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; + + // validate free Queue + + pointer = FREE_Q; + debug = &FREE_Q; + + while (pointer) + { + // Validate pointer to make sure it is in pool - it may be a duff address if Q is corrupt + + Test = (PMESSAGE)pointer; + + if (Test->GuardZone || (uintptr_t)pointer < (uintptr_t)BUFFERPOOL || (uintptr_t)pointer > (uintptr_t)ENDBUFFERPOOL) + { + // Not pointing to a buffer . debug points to the buffer that this is chained from + + // Dump first chunk and source tag + + memcpy(CodeDump, debug, 64); + + Debugprintf("Releasebuffer Pool Corruption n = %d, ptr %p prev %p", n, pointer, debug); + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + Bufferlist[n], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf(" %08x %08x %08x %08x %08x %08x %08x %08x", + CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); + + if (debug[400]) + Debugprintf(" %s", &debug[400]); + + } + + // See if already on free Queue + + if (pointer == BUFF) + { + Debugprintf("Trying to free buffer %p when already on FREE_Q called from %s Line %d", BUFF, File, Line); +// 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[0], 0, 64); // simplify debugging lost buffers + memset(&byteaddr[400], 0, 64); // simplify debugging lost buffers + sprintf(&byteaddr[400], "%s %d", fptr, Line); + + Msg->Process = (short)GetCurrentProcessId(); + Msg->Linkptr = NULL; + Msg->Padding[0] = 0; // Used for modem status info + } + 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(struct TNCINFO * TNC, int Stream), VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream), VOID CloseComplete(struct TNCINFO * TNC, int Stream)) +{ + 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) + { + + // 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 + + hookL4SessionDeleted(TNC, STREAM); + + 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); + + if (TNC->DefaultRXFreq && TNC->RXRadio) + { + char Msg[128]; + + sprintf(Msg, "R%d %f", TNC->RXRadio, TNC->DefaultRXFreq); + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (TNC->DefaultTXFreq && TNC->TXRadio && TNC->TXRadio != TNC->RXRadio) + { + char Msg[128]; + + sprintf(Msg, "R%d %f", TNC->TXRadio, TNC->DefaultTXFreq); + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + while(STREAM->BPQtoPACTOR_Q) + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + } + + while(STREAM->PACTORtoBPQ_Q) + { + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + ReleaseBuffer(buffptr); + } + } +} + +char * CheckAppl(struct TNCINFO * TNC, char * Appl) +{ + APPLCALLS * APPL; + BPQVECSTRUC * PORTVEC; + int Allocated = 0, Available = 0; + int App, Stream; + struct TNCINFO * APPLTNC; + +// Debugprintf("Checking if %s is running", Appl); + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + + if (_memicmp(APPL->APPLCMD, Appl, 12) == 0) + { + int _APPLMASK = 1 << App; + + // If App has an alias, assume it is running , unless a CMS alias - then check CMS + + if (APPL->APPLHASALIAS) + { + if (_memicmp(APPL->APPLCMD, "RELAY ", 6) == 0) + return APPL->APPLCALL_TEXT; // Assume people using RELAY know what they are doing + + if (APPL->APPLPORT && (_memicmp(APPL->APPLCMD, "RMS ", 4) == 0)) + { + APPLTNC = TNCInfo[APPL->APPLPORT]; + { + if (APPLTNC) + { + if (APPLTNC->TCPInfo && !APPLTNC->TCPInfo->CMSOK && !APPLTNC->TCPInfo->FallbacktoRelay) + return NULL; + } + } + } + return APPL->APPLCALL_TEXT; + } + + // See if App is running + + PORTVEC = &BPQHOSTVECTOR[0]; + + for (Stream = 0; Stream < 64; Stream++) + { + if (PORTVEC->HOSTAPPLMASK & _APPLMASK) + { + Allocated++; + + if (PORTVEC->HOSTSESSION == 0 && (PORTVEC->HOSTFLAGS & 3) == 0) + { + // Free and no outstanding report + + return APPL->APPLCALL_TEXT; // Running + } + } + PORTVEC++; + } + } + } + + return NULL; // Not Running +} + +VOID SetApplPorts() +{ + // If any appl has an alias, get port number + + struct APPLCONFIG * App; + APPLCALLS * APPL; + + char C[80]; + char Port[80]; + char Call[80]; + + int i, n; + + App = &xxcfg.C_APPL[0]; + + for (i=0; i < NumberofAppls; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLHASALIAS) + { + n = sscanf(App->CommandAlias, "%s %s %s", &C[0], &Port[0], &Call[0]); + if (n == 3) + APPL->APPLPORT = atoi(Port); + } + App++; + } +} + + +char Modenames[19][10] = {"WINMOR", "SCS", "KAM", "AEA", "HAL", "TELNET", "TRK", + "V4", "UZ7HO", "MPSK", "FLDIGI", "UIARQ", "ARDOP", "VARA", + "SERIAL", "KISSHF", "WINRPR", "HSMODEM", "FREEDATA"}; + +BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT) +{ + return ProcessIncommingConnectEx(TNC, Call, Stream, SENDCTEXT, FALSE); +} + +BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR) +{ + TRANSPORTENTRY * Session; + int Index = 0; + PMSGWITHLEN buffptr; + int Totallen = 0; + UCHAR * ptr; + struct PORTCONTROL * PORT = (struct PORTCONTROL *)TNC->PortRecord; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + // Stop Scanner + + if (Stream == 0 || TNC->Hardware == H_UZ7HO) + { + char Msg[80]; + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command((TRANSPORTENTRY *) -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(STREAM->RemoteCall, Call, 9); // Save Text Callsign + + // May be subsequently rejected but a good place to capture calls + + hookL4SessionAccepted(STREAM, Call, TNC->TargetCall); + + 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; + 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; + + 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, int Port)) +{ + 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); + WritetoConsoleLocal("\n"); + } + } + } + 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); +} + +int CompareRoutes(struct ROUTE ** a, struct ROUTE ** b) +{ + return memcmp(a[0]->NEIGHBOUR_CALL, b[0]->NEIGHBOUR_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; +} + + +int SendMsgEx(int stream, char * msg, int len, int GetSem); + +int SendMsgNoSem(int stream, char * msg, int len) +{ + return SendMsgEx(stream, msg, len, 0); +} + +DllExport int APIENTRY SendMsg(int stream, char * msg, int len) +{ + return SendMsgEx(stream, msg, len, 1); +} + + +int SendMsgEx(int stream, char * msg, int len, int GetSem) +{ + // 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 + + if (GetSem) + GetSemaphore(&Semaphore, 10); + + if ((MSG = GetBuff()) == 0) + { + if (GetSem) + 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); + if (GetSem) + 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; + + if (GetSem) + GetSemaphore(&Semaphore, 22); + + SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + + if (QCOUNT < 40) // PLENTY FREE? + { + if (GetSem) + 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); + if (GetSem) + FreeSemaphore(&Semaphore); + return 1; + } + } + + if ((MSG = GetBuff()) == 0) + { + if (GetSem) + 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); + + if (GetSem) + 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 && PORT->HWType != H_KISSHF) // PACTOR/WINMOR Style + { + // Pactor Style. Probably will only be used for Tracker unless 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, BUFFLEN - sizeof(void *)); // To c + + *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; +} + +int CanPortDigi(int Port) +{ + struct PORTCONTROL * PORTVEC = GetPortTableEntryFromPortNum(Port); + struct TNCINFO * TNC; + + if (PORTVEC == NULL) + return FALSE; + + TNC = PORTVEC->TNC; + + if (TNC == NULL) + return TRUE; + + if (TNC->Hardware == H_SCS || TNC->Hardware == H_TRK || TNC->Hardware == H_TRKM || TNC->Hardware == H_WINRPR) + return FALSE; + + return TRUE; +} + +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[256]; + 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; + DWORD Mask = 0; + int Err; + + Err = GetCommModemStatus(fd, &Mask); + +// if ((Mask & MS_CTS_ON) == 0) // trap com0com other end not open +// return TRUE; + + 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[512]; + + // Linux Version. + + int fd; + int hwflag = 0; + u_long param=1; + struct termios term; + struct speed_struct *s; + + if ((uintptr_t)pPort < 256) + sprintf(Port, "%s/com%d", BPQDirectory, (int)(uintptr_t)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; + int loops = 100; + + while (ToSend && loops-- > 0) + { + 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; + } + +// if (ToSend) +// { +// // Send timed out. Close and reopen device +// +// } + 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) + { + // Dont save routes from config file here or they are difficult to get rid of + + if (Routes->NEIGHBOUR_FLAG & LOCKEDBYCONFIG) + { + Routes++; + continue; + } + + 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 %c\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, + (Routes->NEIGHBOUR_FLAG & LOCKEDBYSYSOP)?'!':' '); + + 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); +} + + +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, (struct sockaddr *)&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); + + if (M0LTEMap) + SendDataToPktMap(); + + 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 && 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 + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) + + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line) +{ + // + // 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; + Semaphore->Line = Line; + strcpy(Semaphore->File, File); + + 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 = 1200; // 2 mins - Give Rigcontrol time to start + + 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); + + printf("MQTT Enabled %d\n", MQTT); + + if (MQTT) + MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS); +} + +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[MaxBPQPortNo + 1] = {0}; // Tab nunber to port + +char * UIUIDigi[MaxBPQPortNo + 1]= {0}; +char * UIUIDigiAX[MaxBPQPortNo + 1] = {0}; // ax.25 version of digistring +int UIUIDigiLen[MaxBPQPortNo + 1] = {0}; // Length of AX string + +char UIUIDEST[MaxBPQPortNo + 1][11] = {0}; // Dest for Beacons + +char UIAXDEST[MaxBPQPortNo + 1][7] = {0}; + + +UCHAR FN[MaxBPQPortNo + 1][256]; // Filename +int Interval[MaxBPQPortNo + 1]; // Beacon Interval (Mins) +int MinCounter[MaxBPQPortNo + 1]; // Interval Countdown + +BOOL SendFromFile[MaxBPQPortNo + 1]; +char Message[MaxBPQPortNo + 1][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[MaxBPQPortNo + 1]; + +} 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); +} + +int GetRegConfig(); + +VOID GetUIConfig() +{ + char Key[100]; + char CfgFN[256]; + char Digis[100]; + struct stat STAT; + + config_t cfg; + config_setting_t *group; + int Port, MaxPort = GetNumberofPorts(); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if (BPQDirectory[0] == 0) + { + strcpy(CfgFN,"UIUtil.cfg"); + } + else + { + strcpy(CfgFN,BPQDirectory); + strcat(CfgFN,"/"); + strcat(CfgFN,"UIUtil.cfg"); + } + + if (stat(CfgFN, &STAT) == -1) + { + // No file. If Windows try to read from registy + +#ifndef LINBPQ + GetRegConfig(); +#else + Debugprintf("UIUtil Config File not found\n"); +#endif + 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], 11); + GetStringValue(group, "FileName", &FN[Port][0], 256); + GetStringValue(group, "Message", &Message[Port][0], 1000); + GetStringValue(group, "Digis", Digis, 100); + UIUIDigi[Port] = _strdup(Digis); + + Interval[Port] = GetIntValue(group, "Interval"); + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = GetIntValue(group, "SendFromFile"); + + SetupUI(Port); + } + } + } + + + _beginthread(UIThread, 0, NULL); + +} + +#ifndef LINBPQ + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen) +{ + char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = (char *)config_setting_get_string(setting); + + if (strlen(str) > maxlen) + { + Debugprintf("Suspect config record %s", str); + str[maxlen] = 0; + } + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +int GetRegConfig() +{ + 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(); + + return TRUE; +} + +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, struct CMDX * CMD) +{ + char FN[250]; + FILE *hFile; + struct stat STAT; + struct PORTCONTROL * PORT = PORTTABLE; + char PortList[256] = ""; + int len = 0; + + 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'; + + + len += sprintf(&PortList[len], ",%d", 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]); +} + +// Get the current frequency for a port. This can get a bit complicated, especially if looking for centre freq +// rather than dial freq (as this depends on mode). +// +// Used for various reporting functions - MH, Maps, BBS New User message, + +// I think I'll try PORT "PortFreq" setting first then if that isn't available via rigcontrol. +// +// For now at least will report dial freq if using RIGCONTROL + +DllExport uint64_t APIENTRY GetPortFrequency(int PortNo, char * FreqString) +{ + struct PORTCONTROL * PORT = GetPortTableEntryFromPortNum(PortNo); + double freq = 0.0; + uint64_t freqint = 0; + + char * ptr; + int n = 3; + + FreqString[0] = 0; + + if (PORT == 0) + return 0; + + if (PORT->PortFreq) + { + freqint = PORT->PortFreq; + freq = freqint / 1000000.0; + } + else + { + // Try rigcontrol + + + struct TNCINFO * TNC; + struct RIGINFO * RIG = 0; + + if (PORT->RIGPort) + TNC = TNCInfo[PORT->RIGPort]; + else + TNC = TNCInfo[PortNo]; + + if (TNC) + RIG = TNC->RIG; + + if (RIG == 0) + return 0; + + // Frequency should be in valchar + + if (RIG->Valchar[0] == 0) + return 0; + + freq = atof(TNC->RIG->Valchar); + freqint = (int64_t)(freq * 1000000.0); + } + + sprintf(FreqString, "%.6f", freq); + + // Return 3 digits after . (KHz) unless more are significant + + ptr = &FreqString[strlen(FreqString) - 1]; + + while (n-- && *(ptr) == '0') + *ptr-- = 0; + + return freqint; +} + +SOCKET OpenHTTPSock(char * Host) +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(80); + + // Resolve name to address + + HostEnt = gethostbyname (Host); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", Host, 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; +} + +static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" + "Accept: app N B lication/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" + "User-Agent: %s%s\r\n" +// "Expect: 100-continue\r\n" + "\r\n"; + + +DllExport VOID WINAPI SendWebRequest(char * Host, char * Request, char * Params, char * Return) +{ + SOCKET sock; + int InputLen = 0; + int inptr = 0; + char Buffer[4096]; + char Header[256]; + char * ptr, * ptr1; + int Sent; + int Len = strlen(Params); + + if (M0LTEMap == 0) + return; + + sock = OpenHTTPSock(Host); + + if (sock == 0) + return; + +#ifdef LINBPQ + sprintf(Header, HeaderTemplate, Request, Host, 80, Len, "linbpq/", VersionString, Params); +#else + sprintf(Header, HeaderTemplate, Request, Host, 80, Len, "bpq32/", VersionString, Params); +#endif + Sent = send(sock, Header, (int)strlen(Header), 0); + Sent = send(sock, Params, (int)strlen(Params), 0); + + if (Sent == -1) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from Web Update send()", Err); + closesocket(sock); + return; + } + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 4095 - inptr, 0); + + if (InputLen == -1 || InputLen == 0) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from Web Update recv()", Err); + closesocket(sock); + return; + } + + inptr += InputLen; + + Buffer[inptr] = 0; + + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + // got header + + int Hddrlen = (int)(ptr - Buffer); + + ptr1 = strstr(Buffer, "Content-Length:"); + + if (ptr1) + { + // Have content length + + int ContentLen = atoi(ptr1 + 16); + + if (ContentLen + Hddrlen + 4 == inptr) + { + // got whole response + + if (strstr(Buffer, " 200 OK")) + { + if (Return) + { + memcpy(Return, ptr + 4, ContentLen); + Return[ContentLen] = 0; + } + else + Debugprintf("Map Database update ok"); + + } + else + { + strlop(Buffer, 13); + Debugprintf("Map Update failed - %s", Buffer); + } + closesocket(sock); + return; + } + } + else + { + ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); + + if (ptr1) + { + // Just accept anything until I've sorted things with Lee + + closesocket(sock); + Debugprintf("Web Database update ok"); + return; + } + } + } + } +} + +// https://packetnodes.spots.radio/api/NodeData/{callsign} + +//SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + +#include "kiss.h" + +extern char MYALIASLOPPED[10]; +extern int MasterPort[MAXBPQPORTS+1]; + + +// G7TAJ // +/* + {"mheard": [ + { + "Callsign": "GB7CIP-7", + "Port": "VHF", + "Packets": 70369, + "LastHeard": "2024-12-29 20:26:32" + }, +*/ + +void BuildPortMH(char * MHJSON, struct PORTCONTROL * PORT) +{ + struct tm * TM; + static char MHTIME[50]; + time_t szClock; + MHSTRUC * MH = PORT->PORTMHEARD; + int count = MHENTRIES; + char Normcall[20]; + int len; + char * ptr; + char mhstr[400]; + int i; + char c; + + if (MH == NULL) + return; + + while (count--) + { + if (MH->MHCALL[0] == 0) + break; + + len = ConvFromAX25(MH->MHCALL, Normcall); + Normcall[len] = 0; + + ptr = &MH->MHCALL[6]; // End of Address bit + + if ((*ptr & 1) == 0) + { + // at least one digi - which we are not going to include + MH++; + continue; + } + + // validate call to prevent corruption of json + + for (i=0; i < len; i++) + { + c = Normcall[i]; + + if (!isalnum(c) && !(c == '#') && !(c == ' ') && !(c == '-')) + goto skipit; + } + + + //format TIME + + szClock = MH->MHTIME; + TM = gmtime(&szClock); + sprintf(MHTIME, "%d-%d-%d %02d:%02d:%02d", + TM->tm_year+1900, TM->tm_mon + 1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec); + + sprintf(mhstr, "{\"callSign\": \"%s\", \"port\": \"%d\", \"packets\": %d, \"lastHeard\": \"%s\" },\r\n" , + Normcall, PORT->PORTNUMBER, MH->MHCOUNT, MHTIME); + + strcat( MHJSON, mhstr ); +skipit: + MH++; + } +} + +void SendDataToPktMapThread(); + +void SendDataToPktMap() +{ + _beginthread(SendDataToPktMapThread,2048000,0); +} + +void SendDataToPktMapThread() +{ + char Return[256] = ""; + char Request[64]; + char Params[50000]; + + struct PORTCONTROL * PORT = PORTTABLE; + struct PORTCONTROL * SAVEPORT; + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + + int PortNo; + int Active; + uint64_t Freq; + int Baud; + int Bitrate; + char * Mode; + char * Use; + char * Type; + char * Modulation; + char * Usage; + + char locked[] = " ! "; + int Percent = 0; + int Port = 0; + char Normcall[10]; + char Copy[20]; + char ID[33]; + + char * ptr = Params; + +// G7TAJ // + char MHJSON[50000]; + char * mhptr; + char * b4Routesptr; + + MHJSON[0]=0; +// G7TAJ // + +// printf("Sending to new map\n"); + + sprintf(Request, "/api/NodeData/%s", MYNODECALL); + +// https://packetnodes.spots.radio/swagger/index.html + + // This builds the request and sends it + + // Minimum header seems to be + + // "nodeAlias": "BPQ", + // "location": {"locator": "IO68VL"}, + // "software": {"name": "BPQ32","version": "6.0.24.3"}, + + ptr += sprintf(ptr, "{\"nodeAlias\": \"%s\",\r\n", MYALIASLOPPED); + + if (strlen(LOCATOR) == 6) + ptr += sprintf(ptr, "\"location\": {\"locator\": \"%s\"},\r\n", LOCATOR); + else + { + // Lat Lon + + double myLat, myLon; + char LocCopy[80]; + char * context; + + strcpy(LocCopy, LOCATOR); + + myLat = atof(strtok_s(LocCopy, ",:; ", &context)); + myLon = atof(context); + + ptr += sprintf(ptr, "\"location\": {\"coords\": {\"lat\": %f, \"lon\": %f}},\r\n", + myLat, myLon); + + } + +#ifdef LINBPQ + ptr += sprintf(ptr, "\"software\": {\"name\": \"LINBPQ\",\"version\": \"%s\"},\r\n", VersionString); +#else + ptr += sprintf(ptr, "\"software\": {\"name\": \"BPQ32\",\"version\": \"%s\"},\r\n", VersionString); +#endif + ptr += sprintf(ptr, "\"source\": \"ReportedByNode\",\r\n"); + +// G7TAJ // + sprintf(MHJSON, ",\"mheard\": ["); +// G7TAJ // + + + //Ports + + ptr += sprintf(ptr, "\"ports\": ["); + + // Get active ports + + while (PORT) + { + PortNo = PORT->PORTNUMBER; + + if (PORT->Hide) + { + PORT = PORT->PORTPOINTER; + continue; + } + + if (PORT->SendtoM0LTEMap == 0) + { + PORT = PORT->PORTPOINTER; + continue; + } + + // Try to get port status - may not be possible with some + + if (PORT->PortStopped) + { + PORT = PORT->PORTPOINTER; + continue; + } + + Active = 0; + Freq = 0; + Baud = 0; + Mode = "ax.25"; + Use = ""; + Type = "RF"; + Bitrate = 0; + Modulation = "FSK"; + Usage = "Access"; + + if (PORT->PortFreq) + Freq = PORT->PortFreq; + + if (PORT->PORTTYPE == 0) + { + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + NPASYINFO Port; + + SAVEPORT = PORT; + + if (KISS->FIRSTPORT && KISS->FIRSTPORT != KISS) + { + // Not first port on device + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; + Port = KISSInfo[PortNo]; + } + + Port = KISSInfo[PORT->PORTNUMBER]; + + if (Port) + { + // KISS like - see if connected + + if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) + { + // KISS over UDP or TCP + + if (PORT->KISSTCP) + { + if (Port->Connected) + Active = 1; + } + else + Active = 1; // UDP - Cant tell + } + else + if (Port->idComDev) // Serial port Open + Active = 1; + + PORT = SAVEPORT; + } + } + else if (PORT->PORTTYPE == 14) // Loopback + Active = 0; + + else if (PORT->PORTTYPE == 16) // External + { + if (PORT->PROTOCOL == 10) // 'HF' Port + { + struct TNCINFO * TNC = TNCInfo[PortNo]; + struct AGWINFO * AGW; + + if (TNC == NULL) + { + PORT = PORT->PORTPOINTER; + continue; + } + + if (Freq == 0 && TNC->RIG) + Freq = TNC->RIG->RigFreq * 1000000; + + switch (TNC->Hardware) // Hardware Type + { + case H_KAM: + case H_AEA: + case H_HAL: + case H_SERIAL: + + // Serial + + if (TNC->hDevice) + Active = 1; + + break; + + case H_SCS: + case H_TRK: + case H_WINRPR: + + if (TNC->HostMode) + Active = 1; + + break; + + + case H_UZ7HO: + + if (TNCInfo[MasterPort[PortNo]]->CONNECTED) + Active = 1; + + // Try to get mode and frequency + + AGW = TNC->AGWInfo; + + if (AGW && AGW->isQTSM) + { + if (AGW->ModemName[0]) + { + char * ptr1, * ptr2, *Context; + + strcpy(Copy, AGW->ModemName); + ptr1 = strtok_s(Copy, " ", & Context); + ptr2 = strtok_s(NULL, " ", & Context); + + if (Context) + { + Modulation = Copy; + + if (strstr(ptr1, "BPSK") || strstr(ptr1, "AFSK")) + { + Baud = Bitrate = atoi(Context); + } + else if (strstr(ptr1, "QPSK")) + { + Modulation = "QPSK"; + Bitrate = atoi(Context); + Baud = Bitrate /2; + } + } + } + } + + break; + + case H_KISSHF: + + // Try to get mode from ID then drop through + + if (stristr(PORT->PORTDESCRIPTION, "BPSK")) + { + Modulation = "BPSK"; + } + + case H_WINMOR: + case H_V4: + + case H_MPSK: + case H_FLDIGI: + case H_UIARQ: + case H_ARDOP: + case H_VARA: + + case H_FREEDATA: + + // TCP + + Mode = Modenames[TNC->Hardware - 1]; + + if (TNC->CONNECTED) + Active = 1; + + break; + + case H_TELNET: + + Active = 1; + Type = "Internet"; + Mode = ""; + } + } + else + { + // External but not HF - AXIP, BPQETHER VKISS, ?? + + struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT; + Type = "Internet"; + Active = 1; + } + } + + if (Active) + { + char * ptr2 = &ID[29]; + strcpy(ID, PORT->PORTDESCRIPTION); + while (*(ptr2) == ' ' && ptr2 != ID) + *(ptr2--) = 0; + + if (PORT->M0LTEMapInfo) + { + // Override with user configured values - RF,7.045,BPSK,300,300,Access + + char param[256]; + char *p1, *p2, *p3, *p4, *p5; + + strcpy(param, PORT->M0LTEMapInfo); + + p1 = strlop(param, ','); + p2 = strlop(p1, ','); + p3 = strlop(p2, ','); + p4 = strlop(p3, ','); + p5 = strlop(p4, ','); + + // int n = sscanf(PORT->M0LTEMapInfo, "%s,%s,%s,%s,%s,%s", &p1, &p2, &p3, &p4, &p5, &p6); + + if (p5) + { + if (param[0]) Type = param; + + if (p1[0]) + { + // if set to DIAL+=n and frequency set from config or rigcontrol modify it + + uint64_t offset = 0; + + if (_memicmp(p1, "DIAL+", 5) == 0) + offset = atoi(&p1[5]); + else if (_memicmp(p1, "DIAL-", 5) == 0) + offset = -atoi(&p1[5]); + else + Freq = atof(p1) * 1000000; + + if (Freq != 0) + Freq += offset; + + } + + if (p2[0]) Modulation = p2; + if (p3[0]) Baud = atoi(p3); + if (p4[0]) Bitrate = atoi(p4); + if (p5[0]) Usage = p5; + } + } + + ptr += sprintf(ptr, "{\"id\": \"%d\",\"linkType\": \"%s\"," + "\"freq\": \"%lld\",\"mode\": \"%s\",\"modulation\": \"%s\"," + "\"baud\": \"%d\",\"bitrate\": \"%d\",\"usage\": \"%s\",\"comment\": \"%s\"},\r\n", + PortNo, Type, + Freq, Mode, Modulation, + Baud, Bitrate, Usage, ID); + +// G7TAJ // + // make MH list to be added later + BuildPortMH(MHJSON, PORT); + +// G7TAJ // + + + } + + PORT = PORT->PORTPOINTER; + } + + ptr -= 3; + ptr += sprintf(ptr, "],\r\n"); + + // Neighbours + +// G7TAJ // + b4Routesptr = ptr-3; +// G7TAJ // + + ptr += sprintf(ptr, "\"neighbours\": [\r\n"); + + while (MaxRoutes--) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + { + ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + strlop(Normcall, ' '); + + ptr += sprintf(ptr, + "{\"node\": \"%s\", \"port\": \"%d\", \"quality\": \"%d\"},\r\n", + Normcall, Routes->NEIGHBOUR_PORT, Routes->NEIGHBOUR_QUAL); + } + + Routes++; + } + +// G7TAJ // + + // if !strstr quality, then there are none, so remove neighbours portion + if ( strstr(Params, "quality") == NULL ) { + ptr = b4Routesptr; + } else { + ptr -= 3; + ptr += sprintf(ptr, "]"); + } + + if ( strlen(MHJSON) > 15 ) { + mhptr = MHJSON + strlen(MHJSON); + mhptr -= 3; + sprintf(mhptr, "]\r\n"); + ptr += sprintf(ptr, "\r\n%s", MHJSON); + + } + + ptr += sprintf(ptr, "}"); + + + +// G7TAJ // + + +/* +{ + "nodeAlias": "BPQ", + "location": {"locator": "IO92KX"}, + "software": {"name": "BPQ32","version": "6.0.24.11 Debug Build "}, + "contact": "G8BPQ", + "sysopComment": "Testing", + "source": "ReportedByNode" +} + + "ports": [ + { + "id": "string", + "linkType": "RF", + "freq": 0, + "mode": "string", + "modulation": "string", + "baud": 0, + "bitrate": 0, + "usage": "Access", + "comment": "string" + } + ], + +*/ + // "contact": "string", + // "neighbours": [{"node": "G7TAJ","port": "30"}] + + SendWebRequest("packetnodes.spots.radio", Request, Params, 0); +} + +// ="{\"neighbours\": [{\"node\": \"G7TAJ\",\"port\": \"30\"}]}"; + +//'POST' \ +// 'https://packetnodes.spots.radio/api/NodeData/GM8BPQ' \ +// -H 'accept: */*' \ +// -H 'Content-Type: application/json' \ +// -d '{ +// "nodeAlias": "BPQ", +// "location": {"locator": "IO68VL"}, +// "software": {"name": "BPQ32","version": "6.0.24.3"}, +// "contact": "string", +// "neighbours": [{"node": "G7TAJ","port": "30"}] +//}' + + + + + + + + + diff --git a/.svn/pristine/ad/adaeb994cfaa1e28b5d322f032bbd3b78c960f16.svn-base b/.svn/pristine/ad/adaeb994cfaa1e28b5d322f032bbd3b78c960f16.svn-base new file mode 100644 index 0000000..94dc557 --- /dev/null +++ b/.svn/pristine/ad/adaeb994cfaa1e28b5d322f032bbd3b78c960f16.svn-base @@ -0,0 +1,4549 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// C replacement for 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" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + + +#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 0x2 // 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 L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer); +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); +int seeifInterlockneeded(struct PORTCONTROL * PORT); +int seeifUnlockneeded(struct _LINKTABLE * LINK); +int CheckKissInterlock(struct PORTCONTROL * MYPORT, int Exclusive); +void hookL2SessionAccepted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +void hookL2SessionDeleted(struct _LINKTABLE * LINK); +void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len); + +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) + +extern int L2Compress; +extern int L2CompMaxframe; +extern int L2CompPaclen; + +UCHAR NO_CTEXT = 0; +UCHAR ALIASMSG = 0; + +static UCHAR ISNETROMMSG = 0; +UCHAR MSGFLAG = 0; +extern char * 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 BOOL LogAllConnects; + +APPLCALLS * APPL; + + +void SendL2ToMonMap(struct PORTCONTROL * PORT, char * ReportCall, char Mode, char Direction) +{ + // if Port Freq < 30Mhz send to Node Map + + if (PORT->PortFreq && PORT->PortFreq < 30000000) + { + char ReportMode[16]; + char ReportFreq[350] = ""; + + ReportMode[0] = '@'; + ReportMode[1] = Mode; + ReportMode[2] = '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + _gcvt(PORT->PortFreq, 9, ReportFreq); + + SendMH(0, ReportCall, ReportFreq, 0, ReportMode); + } +} + +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; + unsigned int APPLMASK = 0; + + // 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; + 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 %d", 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); + + LINK->APPLMASK = APPLMASK; + + 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[64] = ""; + char DIGI = '*'; + double ReportFreq = 0; + + // if port has a freq associated with it use it + + GetPortFrequency(PORT->PORTNUMBER, Freq); + + // 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 && 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; + + // Check Interlock - should we also check exclude etc?. No, checked in L2FORUS + + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + 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) + { + unsigned char * typeptr = ptr; + 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; + + case 16: + + // Compression + + if (L2Compress) + { + LINK->AllowCompress = 1; + // return as 17 + *typeptr = 17; + } + else + { + ptr = &ADJBUFFER->PID; + ptr[3] -= 2; // Length field - remove compress option + Buffer->LENGTH -=2; + } + } + } + + // 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; + + LINK->KILLTIMER = L2KILLTIME - 60*3; // Time out after 60 secs if SABM not received + + // 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 + + // We need to save APPLMASK and ALIASPTR so following SABM connects to application + + // LINK->APPLMASK now set in L2FORUS + LINK->ALIASPTR = ALIASPTR; + + 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; + unsigned char * ptr; + + 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 + + // if Compress enabled set it + + 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 + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 17: + + // Compression + + if (L2Compress) + LINK->AllowCompress = 1; + + } + } + + } + + 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? + { + LINK->APPLMASK; + ALIASPTR = LINK->ALIASPTR; + + 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 + LINK->CIRCUITPOINTER = 0; + + 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; + + char toCall[12], fromCall[12]; + + + if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + + SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG); + + if (LINK->L2STATE != 5) // Setup OK? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed + return; + } + + // See if need to Interlock non-sharable modes, eg ARDOP and VARA + + seeifInterlockneeded(PORT); + + // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION + + if (LINK->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 (LogAllConnects) + WriteConnectLog(fromCall, toCall, "AX.25"); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + AttachKISSHF(PORT, Buffer); + + 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); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + 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 + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + AttachKISSHF(PORT, Buffer); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + + 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; + } + + } + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + Msg = GetBuff(); + + if (Msg) + { + Msg->PID = 0xf0; + + memcpy(Msg->L2DATA, ALIASPTR, 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, LINK->APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0) + { + // NO BBS AVAILABLE + + CLEARSESSIONENTRY(Session); + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + L2SENDDM(PORT, Buffer, ADJBUFFER); + + return; + } + + 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); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + AttachKISSHF(PORT, Buffer); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + 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; + } +} + +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) +{ + if (CheckExcludeList(Buffer->ORIGIN) == 0) // if in exclude, don't send DM + { + ReleaseBuffer(Buffer); // not sure that this is the right place for releasing? + return; + } + + 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 accepting 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 + + char fromCall[12]; + + fromCall[ConvFromAX25(Buffer->ORIGIN, fromCall)] = 0; + + RESET2X(LINK); // LEAVE QUEUED STUFF + + SendL2ToMonMap(PORT, fromCall, '+', 'O'); + + 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 RESP 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); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + 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 + + 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; + } + else + if (LINK->L2ACKREQ == 0) // Resptime is zero so send RR now + SEND_RR_RESP(LINK, 0); + +} + +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + + +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; + + LINK->bytesRXed += Length; + LINK->Received += Length - 1; // Exclude 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 0xf2: + + // Intermediate fragment of compressed. Save + + // Length and Info include pid + + Length--; + Info++; + + if (LINK->unCompress == 0) + LINK->unCompress = malloc(8192); + + // Save data + + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + + ReleaseBuffer(Buffer); + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + return; + + + case 0xf1: + + // Compressed last or only + + { + char exBuffer[8192]; + int Len; + int outLen; + int sendLen; + char * sendptr = exBuffer; + + Length--; + Info++; + + // we may have previous fragments + + if (LINK->unCompressLen) + { + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + Len = doinflate(LINK->unCompress, exBuffer, LINK->unCompressLen, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + + LINK->unCompressLen = 0; + } + else + { + Len = doinflate(Info, exBuffer, Length, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + } + sendLen = outLen; + + // Send first bit in input buffer. If still some left get new buffers for it + + if (sendLen > 257) + sendLen = 257; + + // First byte is original PID + + memcpy(&Msg->PID, exBuffer, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Msg); + + outLen -= sendLen; + sendptr += sendLen; + + while (outLen > 0) + { + sendLen = outLen; + + if (sendLen > 236) + sendLen = 236; + + Msg = GetBuff(); + + if (Msg) + { + // Just ignore if no buffers - shouldn't happen + + Msg->PID = exBuffer[0]; + Msg->PORT = LINK->LINKPORT->PORTNUMBER; + + memcpy(Msg->L2DATA, sendptr, sendLen); + Length = sendLen + 1; + + Msg->LENGTH = Length + MSGHDDRLEN; + C_Q_ADD(&LINK->RX_Q, Msg); + } + + outLen -= sendLen; + sendptr += sendLen; + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + + return; + } + + + 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); + LINK->ReceivedAfterExpansion += Length - 1; + + 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) +{ + char toCall[10]; + char fromCall[10]; + + toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0; + fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0; + hookL2SessionAttempt(LINK->LINKPORT->PORTNUMBER, fromCall, toCall, 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; + struct DATAMESSAGE * Msg; + MESSAGE * Buffer; + + // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS + +// if (LINK->L2RESEQ_Q) +// return; + + 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) + { + // Try compressing here. Only Compress PID 0xF0 frames - NETROM doesn't treat L2 session as a byte stream + + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + if (LINK->AllowCompress && Msg->LENGTH > 20 && LINK->TX_Q && Msg->PID == 240) // if short and no more not worth trying compression + { + int complen = 0; + int dataLen; + int savePort = Msg->PORT; + int savePID = Msg->PID; + unsigned char Compressed[8192]; + unsigned char toCompress[8192]; + int toCompressLen = 0; + + int slots = 0; + int n = LINK->SDTSLOT; + int maxcompsize; + int PACLEN = LINK->LINKPORT->PORTPACLEN; + unsigned char * compdata; + int sendLen = complen; + int uncompressed = 0; + + if (PACLEN == 0) + PACLEN = 256; + + // I think I need to know how many slots are available, so I don't compress too much + // Then collect data, compressing after each frame to make sure will fit in available space + + while (LINK->FRAMES[n] == NULL && slots < 8) + { + slots++; + n++; + n &= 7; + } + + maxcompsize = slots * PACLEN; + + // Save first packet, then see if more on TX_Q + + toCompressLen = 0; + + dataLen = Msg->LENGTH - MSGHDDRLEN; + + LINK->Sent += dataLen; + + memcpy(&toCompress[toCompressLen], &Msg->PID, dataLen); + toCompressLen += dataLen; + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + ReleaseBuffer(Msg); + + while (LINK->TX_Q) + { + Msg = LINK->TX_Q; // Leave on queue until sure it will fit + dataLen = Msg->LENGTH - MSGHDDRLEN -1; // PID only on 1st fragment + + memcpy(&toCompress[toCompressLen], &Msg->L2DATA, dataLen); + toCompressLen += dataLen; + + // Need to make sure we don't go over maxcompsize + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + if (complen > maxcompsize) + { + // Remove last fragment and compress again + + toCompressLen -= dataLen; + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + break; + } + else + { + LINK->Sent += dataLen; + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + ReleaseBuffer(Msg); + } + } + + if (complen >= toCompressLen) + { + // Won't compress, so just send original data + // May still need to fragment + + memcpy(Compressed, toCompress, toCompressLen); + complen = toCompressLen - 1; // Remove leading PID + uncompressed = 1; + compdata = &Compressed[1]; + } + else + compdata = Compressed; + + // We now need to packetize and add to FRAMES + + LINK->SentAfterCompression += complen; + + sendLen = PACLEN; + + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + else + sendLen = complen; + + if (uncompressed) + PID = Compressed[0]; + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = savePort; + Msg->PID = PID; + + memcpy(&Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; + + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + + compdata += sendLen; + complen -= sendLen; + } + + toCompressLen = 0; + + } + else + { + 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 + + // if in XID received state session was never established so don't send DISC + + if (LINK->L2STATE == 1) + { + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + } + else + { + 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->IFrameRetryCounter = 0; + + 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 + + // Need to kill link if we are getting repeated RR(F) after timeout + // (Indicating other station is seeing our RR(P) but not the resent I frame) + + if (LINK->IFrameRetryCounter++ > LINK->LINKPORT->PORTN2) + { + Debugprintf("Too many repeats of same I frame - closing connection"); + LINK->L2TIMER = 1; // USE TIMER TO SEND DISC + LINK->L2STATE = 4; // DISCONNECTING + return; + } + + + 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(struct _LINKTABLE * LINK); + +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 + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + 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 + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + 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) +{ + hookL2SessionDeleted(LINK); + + seeifUnlockneeded(LINK); + + CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS + + if (LINK->unCompress) + free(LINK->unCompress); + + 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; + + if (L2Compress) + *ptr++ = 0x12; // Length 18 + else + *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 + + // if L2Compress Enabled request it + + if (L2Compress) + { + *ptr++ = 0x10; // Compress + *ptr++ = 0x00; // Len + } + + 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); + CLEARSESSIONENTRY(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]); + + 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(struct _LINKTABLE * LINK); + +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 INCOMMING 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 ((CountBits64(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, PORT->PORTNUMBER, &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; +} + + +int COUNTLINKS(int Port); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + + +int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive) +{ + // This checks for interlocked kiss and other ports. Returns 1 if attach/connect not allowed + + // If Exclusive is not set allow connects on specified port up to l2limit, + + // If Exclusive is set also don't allow any connects on specified port. + + // Generally use Exclusive if locking a port that doesn't allow shared access, eg ARDOP, VARAus + + // Maybe only Exclusive is needed, and just check session mode ports. Sharing of KISS ports is controlled by USERS + + int Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + PORT = PORTTABLE; + + if (Exclusive) + { + while(PORT) + { + if (PORT->TNC) + { + struct TNCINFO * TNC = PORT->TNC; + + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + { + // See if port in use + + int n; + + for (n = 0; n <= 26; n++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[n]) + { + return TNC->Port; ; // Refuse Connect + } + } + } + } + PORT = PORT->PORTPOINTER; + } + } + return 0; // ok to connect +} + +int seeifInterlockneeded(struct PORTCONTROL * PORT) +{ + // Can we just call SuspendOtherPorts - it won't do any harm if already suspended + // No, at that needs a TNC Record, so duplicate code here + + int i; + int Interlock = PORT->PORTINTERLOCK; + struct TNCINFO * TNC; + + if (Interlock == 0) + return 0; // No locking + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == FALSE) + TNC->SuspendPortProc(TNC, TNC); + } + + return 0; +} + +int seeifUnlockneeded(struct _LINKTABLE * LINK) +{ + // We need to see if any other links are active on any interlocked KISS ports. If not, release the lock + + int i; + int links = 0; + + int Interlock; + struct TNCINFO * TNC; + struct PORTCONTROL * PORT = LINK->LINKPORT; + + if (PORT == NULL) + return 0; + + // Should only be called for KISS links, but just in case + + if (PORT->PORTTYPE > 12) // INTERNAL or EXTERNAL? + return 0; // Not KISS Port + + Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + + // Count all L2 links on interlocked KISS ports + + PORT = PORTTABLE; + + while(PORT) + { + if (PORT->PORTTYPE <= 12) // INTERNAL or EXTERNAL? + if (Interlock == PORT->PORTINTERLOCK) + links += COUNTLINKS(PORT->PORTNUMBER); + + PORT = PORT->PORTPOINTER; + } + + if (links > 1) // must be the one we are closing + return 0; // Keep lock + + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == TRUE) + TNC->ReleasePortProc(TNC); + } + + return 0; +} + +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len) +{ + z_stream defstream; + int maxSize; + + 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); + + if (maxSize > OutSize) + return 0; + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + return defstream.total_out; +} + + + + diff --git a/.svn/pristine/c7/c79df3fbc4e69bc6df441bb7cebf2f19cb6ecb3c.svn-base b/.svn/pristine/c7/c79df3fbc4e69bc6df441bb7cebf2f19cb6ecb3c.svn-base new file mode 100644 index 0000000..28b1a55 --- /dev/null +++ b/.svn/pristine/c7/c79df3fbc4e69bc6df441bb7cebf2f19cb6ecb3c.svn-base @@ -0,0 +1,799 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +/* + + Paula (G8PZT)'s Remote Host Protocol interface. + For now only sufficient support for WhatsPac + + +*/ +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include "bpq32.h" +#include "telnetserver.h" + +int FindFreeStreamNoSem(); +DllExport int APIENTRY DeallocateStream(int stream); +int SendMsgNoSem(int stream, char * msg, int len); + +static void GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value, int Len); +static int GetJSONInt(char * _REPLYBUFFER, char * Name); + +// Generally Can have multiple RHP connections and each can have multiple RHF Sessions + +struct RHPSessionInfo +{ + struct ConnectionInfo * sockptr; + SOCKET Socket; // Websocks Socket + int BPQStream; + int Handle; // RHP session ID + int Seq; + char Local[12]; + char Remote[12]; + BOOL Connecting; // Set while waiting for connection to complete + BOOL Listening; + BOOL Connected; + int Busy; +}; + +struct RHPConnectionInfo +{ + SOCKET socket; + struct RHPSessionInfo ** RHPSessions; + int NumberofRHPSessions; +}; + +// Struct passed by beginhread + +struct RHPParamBlock +{ + unsigned char * Msg; + int Len; + SOCKET Socket; + struct ConnectionInfo * sockptr; +}; + + + +//struct RHPConnectionInfo ** RHPSockets = NULL; +//int NumberofRHPConnections = 0; + +struct RHPSessionInfo ** RHPSessions; +int NumberofRHPSessions; + +char ErrCodes[18][24] = +{ + "Ok", + "Unspecified", + "Bad or missing type", + "Invalid handle", + "No memory", + "Bad or missing mode", + "Invalid local address", + "Invalid remote address" , + "Bad or missing family" , + "Duplicate socket" , + "No such port" , + "Invalid protocol" , + "Bad parameter" , + "No buffers" , + "Unauthorised" , + "No Route" , + "Operation not supported"}; + + + + +extern char pgm[256]; + +SOCKET agwsock; + +extern int SemHeldByAPI; + +char szBuff[80]; + +int WhatsPacConfigured = 1; + + +int RHPPaclen = 236; + + +int processRHCPOpen(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, char * ReplyBuffer); +int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer); +int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer); +int processRHCPStatus(SOCKET Socket, char * Msg, char * ReplyBuffer); + + + +void SendWebSockMessage(SOCKET socket, char * Msg, int Len) +{ + int Loops = 0; + int Sent; + int TxLen; + char * OutBuffer = Msg; + + // WebSock Encode. Buffer has 10 bytes on front for header but header len depends on Msg len + + if (Len < 126) + { + // Two Byte Header + + OutBuffer[8] = 0x81; // Fin, Data + OutBuffer[9] = Len; + + TxLen = Len + 2; + OutBuffer = &Msg[8]; + } + else if (Len < 65536) + { + OutBuffer[6] = 0x81; // Fin, Data + OutBuffer[7] = 126; // Unmasked, Extended Len 16 + OutBuffer[8] = Len >> 8; + OutBuffer[9] = Len & 0xff; + TxLen = Len + 4; + OutBuffer = &Msg[6]; + } + else + { + OutBuffer[0] = 0x81; // Fin, Data + OutBuffer[1] = 127; // Unmasked, Extended Len 64 bits + // Len is 32 bits, so pad with zeros + OutBuffer[2] = 0; + OutBuffer[3] = 0; + OutBuffer[4] = 0; + OutBuffer[5] = 0; + OutBuffer[6] = (Len >> 24) & 0xff; + OutBuffer[7] = (Len >> 16) & 0xff; + OutBuffer[8] = (Len >> 8) & 0xff; + OutBuffer[9] = Len & 0xff; + + TxLen = Len + + 10; + OutBuffer = &Msg[0]; + } + + // Send may block + + Sent = send(socket, OutBuffer, TxLen, 0); + + while (Sent != TxLen && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { + TxLen -= Sent; + memmove(OutBuffer, &OutBuffer[Sent], TxLen); + } + + Sleep(30); + Sent = send(socket, OutBuffer, TxLen, 0); + if (Sent == -1) + break; + } + + free(Msg); + return; +} + +void ProcessRHPWebSock(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, int MsgLen); + +void RHPThread(void * Params) +{ + // Params and data buffer are malloced blocks so free when done with it + + struct RHPParamBlock * Block = (struct RHPParamBlock *)Params; + + ProcessRHPWebSock(Block->sockptr, Block->Socket, Block->Msg, Block->Len); + + free(Block->Msg); + free(Params); +} + +int RHPProcessHTTPMessage(void * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE) +{ + // RHP messages can be sent over Websocks or normal http but I think WhatsPac only uses WebSocks + return 0; +} + +void ProcessRHPWebSock(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, int MsgLen) +{ + int Loops = 0; + int InputLen = 0; + int Len; + + char Value[16]; + char * OutBuffer = malloc(250000); + +// struct RHPConnectionInfo * RHPSocket = NULL; +// int n; + + Msg[MsgLen] = 0; + + // Find Connection Record. If none, create one + + // I dont think I need connection records + +/* + for (n = 0; n < NumberofRHPConnections; n++) + { + if (RHPSockets[n]->socket == socket) + { + RHPSocket = RHPSockets[n]; + break; + } + } + + if (RHPSocket == 0) + { + // See if there is an old one we can reuse + + for (n = 0; n < NumberofRHPConnections; n++) + { + if (RHPSockets[n]-Socket == -1) + { + RHPSocket = RHPSockets[n]; + RHP + + break; + } + } + + if (RHPSocket == 0) + + NumberofRHPConnections; + RHPSockets = realloc(RHPSockets, sizeof(void *) * (NumberofRHPConnections + 1)); + RHPSocket = RHPSockets[NumberofRHPConnections] = zalloc(sizeof (struct RHPConnectionInfo)); + NumberofRHPConnections++; + RHPSocket->socket = socket; + } +*/ + +// {"type":"open","id":5,"pfam":"ax25","mode":"stream","port":"1","local":"G8BPQ","remote":"G8BPQ-2","flags":128} +// {"type": "openReply", "id": 82, "handle": 1, "errCode": 0, "errText": "Ok"} +// {"seqno": 0, "type": "status", "handle": 1, "flags": 0} +// ("seqno": 1, "type": "close", "handle": 1} +// {"id":40,"type":"close","handle":1} + +// {"seqno": 0, "type": "status", "handle": 1, "flags": 2}.~. +// {"seqno": 1, "type": "recv", "handle": 1, "data": "Welcome to G8BPQ's Test Switch in Nottingham \rType ? for list of available commands.\r"}. + +// {"type": "status", "handle": 0}. XRouter will reply with {"type": "statusReply", "handle": 0, "errcode": 12, "errtext": "invalid handle"}. It +// {type: 'keepalive'} if there has been no other activity for nearly 3 minutes. Replies with {"type": "keepaliveReply"} + + GetJSONValue(Msg, "\"type\":", Value, 15); + + if (_stricmp(Value, "open") == 0) + { + Len = processRHCPOpen(sockptr, Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header + if (Len) + SendWebSockMessage(Socket, OutBuffer, Len); + return; + } + + if (_stricmp(Value, "send") == 0) + { + Len = processRHCPSend(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header + SendWebSockMessage(Socket, OutBuffer, Len); + return; + } + + if (_stricmp(Value, "close") == 0) + { + Len = processRHCPClose(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header + SendWebSockMessage(Socket, OutBuffer, Len); + return; + } + + if (_stricmp(Value, "status") == 0) + { + Len = processRHCPStatus(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header + SendWebSockMessage(Socket, OutBuffer, Len); + return; + } + + if (_stricmp(Value, "keepalive") == 0) + { + Len = sprintf(&OutBuffer[10], "{\"type\": \"keepaliveReply\"}"); // Space at front for WebSock Header + SendWebSockMessage(Socket, OutBuffer, Len); + return; + } + + Debugprintf("Unrecognised RHP Message - %s", Msg); +} + +void ProcessRHPWebSockClosed(SOCKET socket) +{ + // Close any connections on this scoket and delete socket entry + + struct RHPSessionInfo * RHPSession = 0; + int n; + + // Find and close any Sessions + + for (n = 0; n < NumberofRHPSessions; n++) + { + if (RHPSessions[n]->Socket == socket) + { + RHPSession = RHPSessions[n]; + + if (RHPSession->BPQStream) + { + Disconnect(RHPSession->BPQStream); + DeallocateStream(RHPSession->BPQStream); + + RHPSession->BPQStream = 0; + + } + + RHPSession->Connecting = 0; + + // We can't send a close to RHP endpont as socket has gone + + RHPSession->Connected = 0; + } + } +} + + + + +int processRHCPOpen(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, char * ReplyBuffer) +{ + //{"type":"open","id":5,"pfam":"ax25","mode":"stream","port":"1","local":"G8BPQ","remote":"G8BPQ-2","flags":128} + + struct RHPSessionInfo * RHPSession = 0; + + char * Value = malloc(strlen(Msg)); // Will always be long enough + int ID; + + char pfam[16]; + char Mode[16]; + int Port; + char Local[16]; + char Remote[16]; + int flags; + int Handle = 1; + int Stream; + unsigned char AXCall[10]; + + int n; + + // ID seems to be used for control commands like open. SeqNo for data within a session (i Think! + + ID = GetJSONInt(Msg, "\"id\":"); + GetJSONValue(Msg, "\"pfam\":", pfam, 15); + GetJSONValue(Msg, "\"mode\":", Mode, 15); + Port = GetJSONInt(Msg, "\"port\":"); + GetJSONValue(Msg, "\"local\":", Local, 15); + GetJSONValue(Msg, "\"remote\":", Remote, 15); + flags = GetJSONInt(Msg, "\"flags\":"); + + if (_stricmp(pfam, "ax25") != 0) + return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0); + + if (_stricmp(Mode, "stream") == 0) + { + { + // Allocate a RHP Session + + // See if there is an old one we can reuse + + for (n = 0; n < NumberofRHPSessions; n++) + { + if (RHPSessions[n]->BPQStream == 0) + { + RHPSession = RHPSessions[n]; + Handle = n + 1; + Stream = RHPSessions[n]->BPQStream; + + break; + } + } + + if (RHPSession == 0) + { + RHPSessions = realloc(RHPSessions, sizeof(void *) * (NumberofRHPSessions + 1)); + RHPSession = RHPSessions[NumberofRHPSessions] = zalloc(sizeof (struct RHPSessionInfo)); + NumberofRHPSessions++; + + Handle = NumberofRHPSessions; + } + + strcpy(pgm, "RHP"); + Stream = FindFreeStream(); + strcpy(pgm, "bpq32.exe"); + + if (Stream == 255) + return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0); + + RHPSession->BPQStream = Stream; + RHPSession->Handle = Handle; + RHPSession->Connecting = TRUE; + RHPSession->Socket = Socket; + RHPSession->sockptr = sockptr; + + strcpy(RHPSession->Local, Local); + strcpy(RHPSession->Remote, Remote); + + Connect(Stream); + + ConvToAX25(Local, AXCall); + ChangeSessionCallsign(Stream, AXCall); + + return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 0, \"errText\": \"Ok\"}", ID, Handle); + } + } + return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0); +} + +int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) +{ + // {"type":"send","handle":1,"data":";;;;;;\r","id":70} + + struct RHPSessionInfo * RHPSession; + + int ID; + char * Data; + char * ptr; + unsigned char * uptr; + int c; + int Len; + unsigned int HexCode1; + unsigned int HexCode2; + + int n; + + int Handle = 1; + + Data = malloc(strlen(Msg)); + + ID = GetJSONInt(Msg, "\"id\":"); + Handle = GetJSONInt(Msg, "\"handle\":"); + GetJSONValue(Msg, "\"data\":", Data, strlen(Msg) - 1); + + if (Handle < 1 || Handle > NumberofRHPSessions) + { + free(Data); + return sprintf(ReplyBuffer, "{\"type\": \"sendReply\", \"id\": %d, \"handle\": %d, \"errCode\": 3, \"errtext\": \"Invalid handle\"}", ID, Handle); + } + + RHPSession = RHPSessions[Handle - 1]; + + // Look for \ escapes, Can now also get \u00c3 + + ptr = Data; + Len = strlen(Data); // in case no escapes + + while (ptr = strchr(ptr, '\\')) + { + c = ptr[1]; + + switch (c) + { + case 'r': + + *ptr = 13; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); + break; + + case 'u': + + HexCode1 = HexCode2 = 0; + + n = toupper(ptr[2]) - '0'; + if (n > 9) n = n - 7; + HexCode1 |= n << 4; + + n = toupper(ptr[3]) - '0'; + if (n > 9) n = n - 7; + HexCode1 |= n; + + n = toupper(ptr[4]) - '0'; + if (n > 9) n = n - 7; + HexCode2 |= n << 4; + + n = toupper(ptr[5]) - '0'; + if (n > 9) n = n - 7; + HexCode2 |= n; + + if (HexCode1 == 0 || HexCode1 == 0xC2) + { + uptr = ptr; + *uptr = HexCode2; + } + else if (HexCode1 == 0xc2) + { + uptr = ptr; + *uptr = HexCode2 + 0x40; + } + + memmove(ptr + 1, ptr + 6, strlen(ptr + 5)); + break; + + + case '\\': + + *ptr = '\\'; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); + break; + + case '"': + + *ptr = '"'; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); + break; + } + ptr++; + Len = ptr - Data; + } + + ptr = Data; + + while (Len > RHPPaclen) + { + SendMsg(RHPSession->BPQStream, ptr, RHPPaclen); + Len -= RHPPaclen; + ptr += RHPPaclen; + } + + SendMsg(RHPSession->BPQStream, ptr, Len); + + free(Data); + return sprintf(ReplyBuffer, "{\"type\": \"sendReply\", \"id\": %d, \"handle\": %d, \"errCode\": 0, \"errText\": \"Ok\", \"status\": %d}", ID, Handle, 2); +} + + +int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer) +{ + + // {"id":70,"type":"close","handle":1} + + + struct RHPSessionInfo * RHPSession; + + int ID; + int Handle = 1; + + char * OutBuffer = malloc(256); + + ID = GetJSONInt(Msg, "\"id\":"); + Handle = GetJSONInt(Msg, "\"handle\":"); + + if (Handle < 1 || Handle > NumberofRHPSessions) + return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 3, \"errtext\": \"Invalid handle\"}", ID, Handle); + + + RHPSession = RHPSessions[Handle - 1]; + Disconnect(RHPSession->BPQStream); + RHPSession->Connected = 0; + RHPSession->Connecting = 0; + + DeallocateStream(RHPSession->BPQStream); + RHPSession->BPQStream = 0; + + return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 0, \"errtext\": \"Ok\"}", ID, Handle); +} + +int processRHCPStatus(SOCKET Socket, char * Msg, char * ReplyBuffer) +{ + // {"type": "status", "handle": 0}. XRouter will reply with {"type": "statusReply", "handle": 0, "errcode": 3, "errtext": "invalid handle"}. It + + struct RHPSessionInfo * RHPSession; + int Handle = 0; + + Handle = GetJSONInt(Msg, "\"handle\":"); + + if (Handle < 1 || Handle > NumberofRHPSessions) + return sprintf(ReplyBuffer, "{\"type\": \"statusReply\", \"handle\": %d, \"errcode\": 3, \"errtext\": \"Invalid handle\"}", Handle); + + RHPSession = RHPSessions[Handle - 1]; + + return sprintf(ReplyBuffer, "{\"type\": \"status\", \"handle\": %d, \"flags\": 2}", RHPSession->Handle); + +} + +char toHex[] = "0123456789abcdef"; + +void RHPPoll() +{ + int Stream; + int n; + int state, change; + int Len; + char * RHPMsg; + unsigned char Buffer[2048]; // Space to escape control chars + int pktlen, count; + + struct RHPSessionInfo * RHPSession; + + for (n = 0; n < NumberofRHPSessions; n++) + { + RHPSession = RHPSessions[n]; + Stream = RHPSession->BPQStream; + + // See if connected state has changed + + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + RHPSession->Seq = 0; + RHPSession->Connecting = FALSE; + RHPSession->Connected = TRUE; + + RHPMsg = malloc(256); + Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"status\", \"handle\": %d, \"flags\": 2}", RHPSession->Seq++, RHPSession->Handle); + SendWebSockMessage(RHPSession->Socket, RHPMsg, Len); + + // Send RHP CTEXT + + RHPMsg = malloc(256); + Sleep(10); // otherwise WhatsPac doesn't display connected + Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"recv\", \"handle\": %d, \"data\": \"Connected to RHP Server\\r\"}", RHPSession->Seq++, RHPSession->Handle); + SendWebSockMessage(RHPSession->Socket, RHPMsg, Len); + } + else + { + // Disconnected. Send Close to client + + RHPMsg = malloc(256); + Len = sprintf(&RHPMsg[10], "{\"type\": \"close\", \"seqno\": %d, \"handle\": %d}", RHPSession->Seq++, RHPSession->Handle); + SendWebSockMessage(RHPSession->Socket, RHPMsg, Len); + + RHPSession->Connected = 0; + RHPSession->Connecting = 0; + + DeallocateStream(RHPSession->BPQStream); + RHPSession->BPQStream = 0; + } + } + do + { + GetMsg(Stream, Buffer, &pktlen, &count); + + if (pktlen > 0) + { + char * ptr = Buffer; + unsigned char c; + + Buffer[pktlen] = 0; + + RHPSession->sockptr->LastSendTime = time(NULL); + + + // Message is JSON so Convert CR to \r, \ to \\ " to \" + + // Looks like I need to escape everything not between 0x20 and 0x7f eg \u00c3 + + + while (c = *(ptr)) + { + switch (c) + { + case 13: + + memmove(ptr + 2, ptr + 1, strlen(ptr) + 1); + *(ptr++) = '\\'; + *(ptr++) = 'r'; + break; + + case '"': + + memmove(ptr + 2, ptr + 1, strlen(ptr) + 1); + *(ptr++) = '\\'; + *(ptr++) = '"'; + break; + + case '\\': + + memmove(ptr + 2, ptr + 1, strlen(ptr) + 1); + *(ptr++) = '\\'; + *(ptr++) = '\\'; + break; + + default: + + if (c > 127) + { + memmove(ptr + 6, ptr + 1, strlen(ptr) + 1); + *(ptr++) = '\\'; + *(ptr++) = 'u'; + *(ptr++) = '0'; + *(ptr++) = '0'; + *(ptr++) = toHex[c >> 4]; + *(ptr++) = toHex[c & 15]; + break; + } + else + ptr++; + } + } + + RHPMsg = malloc(2048); + + Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"recv\", \"handle\": %d, \"data\": \"%s\"}", RHPSession->Seq++, RHPSession->Handle, Buffer); + SendWebSockMessage(RHPSession->Socket, RHPMsg, Len); + + } + + } + while (count > 0); + } +} + + + +static void GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value, int Len) +{ + char * ptr1, * ptr2; + + Value[0] = 0; + + ptr1 = strstr(_REPLYBUFFER, Name); + + if (ptr1 == 0) + return; + + ptr1 += (strlen(Name) + 1); + +// "data":"{\"t\":\"c\",\"n\":\"John\",\"c\":\"G8BPQ\",\"lm\":1737912636,\"le\":1737883907,\"led\":1737758451,\"v\":0.33,\"cc\":[{\"cid\":1,\"lp\":1737917257201,\"le\":1737913735726,\"led\":1737905249785},{\"cid\":0,\"lp\":1737324074107,\"le\":1737323831510,\"led\":1737322973662},{\"cid\":5,\"lp\":1737992107419,\"le\":1737931466510,\"led\":1737770056244}]}\r","id":28} + + // There may be escaped " in data stream + + ptr2 = strchr(ptr1, '"'); + + while (*(ptr2 - 1) == '\\') + { + ptr2 = strchr(ptr2 + 2, '"'); + } + + + if (ptr2) + { + size_t ValLen = ptr2 - ptr1; + if (ValLen > Len) + ValLen = Len; + + memcpy(Value, ptr1, ValLen); + Value[ValLen] = 0; + } + + return; +} + + +static int GetJSONInt(char * _REPLYBUFFER, char * Name) +{ + char * ptr1; + + ptr1 = strstr(_REPLYBUFFER, Name); + + if (ptr1 == 0) + return 0; + + ptr1 += (strlen(Name)); + + return atoi(ptr1); +} + + diff --git a/.svn/pristine/f0/f0dbb3a3ca0a919f9983b238ae37e00b12d88c86.svn-base b/.svn/pristine/f0/f0dbb3a3ca0a919f9983b238ae37e00b12d88c86.svn-base new file mode 100644 index 0000000..b85789f --- /dev/null +++ b/.svn/pristine/f0/f0dbb3a3ca0a919f9983b238ae37e00b12d88c86.svn-base @@ -0,0 +1,126 @@ + +#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,24,70 +#define KVerstring "6.0.24.70\0" + + +#ifdef CKernel + +#define Vers KVers +#define Verstring KVerstring +#define Datestring "April 2025" +#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring +#define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" +#define VerProduct "BPQ32" + +#endif + +#ifdef TermTCP + +#define Vers 1,0,16,2 +#define Verstring "1.0.16.2\0" +#define VerComments "Internet Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2025 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,2 +#define Verstring "2.2.5.2\0" +#define VerComments "Simple Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2025 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-2025 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-2025 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-2025 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-2025 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-2025 John Wiseman G8BPQ\0" +#define VerDesc "Chat server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQChat" + +#endif diff --git a/.svn/pristine/f8/f86d0ffea83bcbb7de3066bd72fd0647f6d547e4.svn-base b/.svn/pristine/f8/f86d0ffea83bcbb7de3066bd72fd0647f6d547e4.svn-base new file mode 100644 index 0000000..1e419f4 --- /dev/null +++ b/.svn/pristine/f8/f86d0ffea83bcbb7de3066bd72fd0647f6d547e4.svn-base @@ -0,0 +1,435 @@ + +#define _CRT_SECURE_NO_DEPRECATE + +#ifndef NOMQTT + +#include "MQTTAsync.h" +#ifndef WIN32 +#include +#endif + +#include "cheaders.h" +#include "asmstrucs.h" +#include "mqtt.h" + +extern int MQTT_Connecting; +extern int MQTT_Connected; + + +DllExport int APIENTRY MQTTSend(char * topic, char * Msg, int MsgLen); + +MQTTAsync client = NULL; + +time_t MQTTLastStatus = 0; + +void MQTTSendStatus() +{ + char topic[256]; + char payload[128]; + + sprintf(topic, "PACKETNODE/%s", NODECALLLOPPED); + strcpy(payload,"{\"status\":\"online\"}"); + + MQTTSend(topic, payload, strlen(payload)); + MQTTLastStatus = time(NULL); +} + +void MQTTTimer() +{ + if (MQTT_Connecting == 0 && MQTT_Connected == 0) + MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS); + + if ((time(NULL) - MQTTLastStatus) > 1800) + MQTTSendStatus(); + +} + + +void MQTTDisconnect() +{ + if (MQTT_Connected) + { + MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer; + + MQTTAsync_disconnect(client, &disc_opts); + + MQTT_Connecting = MQTT_Connected = 0; + + // Try to recconect. If it fails system will rety every minute + + MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS); + } +} + +DllExport int APIENTRY MQTTSend(char * topic, char * Msg, int MsgLen) +{ + int rc; + + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + pubmsg.payload = Msg; + pubmsg.payloadlen = MsgLen; + rc = MQTTAsync_sendMessage(client, topic, &pubmsg, &opts); + + if (rc) + MQTTDisconnect(); + + return rc; +} + + + +void onConnect(void* context, MQTTAsync_successData* response) +{ + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + MQTT_Connecting = 0; + MQTT_Connected = 1; + + printf("Successful MQTT connection\n"); + + // Send start up message + + MQTTSendStatus(); + +} + +void onConnectFailure(void* context, MQTTAsync_failureData* response) +{ + printf("MQTT connection failed, rc %d\n", response ? response->code : 0); + MQTT_Connecting = 0; +} + + + +char* jsonEncodeMessage(MESSAGE *msg) +{ + char From[10]; + char To[10]; + + char buffer[1024]; + unsigned long long SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + int len; + char *msg_str; + char payload_timestamp[16]; + + struct tm * TM = localtime(&msg->Timestamp); + sprintf(payload_timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + + IntSetTraceOptionsEx(MMASK, TRUE, TRUE, FALSE); + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + len = IntDecodeFrame(msg, buffer, msg->Timestamp, 0xffffffffffffffff, FALSE, FALSE); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + buffer[len] = 0; + +#ifdef WIN32 + + msg_str = zalloc(2048); + + sprintf(msg_str, "{\"from\": \"%s\", \"to\": \"%s\", \"payload\": \"%s\", \"port\": %d, \"timestamp\": \"%s\"}", + From, To, buffer, msg->PORT, payload_timestamp); + +#else + + json_t *root; + + root = json_object(); + + json_object_set_new(root, "from", json_string(From)); + json_object_set_new(root, "to", json_string(To)); + + + json_object_set_new(root, "payload", json_string(buffer)); + json_object_set_new(root, "port", json_integer(msg->PORT)); + sprintf(payload_timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + json_object_set_new(root, "timestamp", json_string(payload_timestamp)); + msg_str = json_dumps(root, 0); + json_decref(root); + +#endif + + return msg_str; +} + +void MQTTKISSTX(void *message) +{ + MESSAGE *msg = (MESSAGE *)message; + char topic[256]; + char *msg_str; + + sprintf(topic, "PACKETNODE/ax25/trace/bpqformat/%s/sent/%d", NODECALLLOPPED, msg->PORT); + + msg_str = jsonEncodeMessage(msg); + + MQTTSend(topic, msg_str, strlen(msg_str)); + + free(msg_str); +} + +void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT) +{ + PPORTCONTROL PPORT = (PPORTCONTROL)PORT; + char topic[256]; + + sprintf(topic, "PACKETNODE/kiss/%s/sent/%d", NODECALLLOPPED, PPORT->PORTNUMBER); + + MQTTSend(topic, buffer, bufferLength); +} + + +void MQTTKISSRX(void *message) +{ + MESSAGE *msg = (MESSAGE *)message; + char topic[256]; + char *msg_str; + + + sprintf(topic, "PACKETNODE/ax25/trace/bpqformat/%s/rcvd/%d", NODECALLLOPPED, msg->PORT); + msg_str = jsonEncodeMessage(msg); + + MQTTSend(topic, msg_str, strlen(msg_str)); + + free(msg_str); +} + +void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) +{ + PPORTCONTROL PPORT = (PPORTCONTROL)PORT; + char topic[256]; + + sprintf(topic, "PACKETNODE/kiss/%s/rcvd/%d", NODECALLLOPPED, PPORT->PORTNUMBER); + + MQTTSend(topic, buffer, bufferLength); + +} + +void MQTTReportSession(char * Msg) +{ + char topic[256]; + sprintf(topic, "PACKETNODE/stats/session/%s", NODECALLLOPPED); + + MQTTSend(topic, Msg, strlen(Msg)); +} + + +char* replace(char* str, char* a, char* b) +{ + int len = strlen(str); + int lena = strlen(a), lenb = strlen(b); + char * p; + + for (p = str; p = strstr(p, a); p) { + if (lena != lenb) // shift end as needed + memmove(p + lenb, p + lena, + len - (p - str) + lenb); + memcpy(p, b, lenb); + } + return str; +} + +int MQTTPublish(void *message, char *topic) +{ + MESSAGE *msg = (MESSAGE *)message; + char From[10]; + char To[10]; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + unsigned long long SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + int len; + char* replaced_buffer; + char buffer[1024]; + + time_t timestamp = msg->Timestamp; + + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + + IntSetTraceOptionsEx(8, TRUE, TRUE, FALSE); + len = IntDecodeFrame(msg, buffer, timestamp, 1, FALSE, FALSE); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + // MQTT _really_ doesn't like \r, so replace it with something + // that is at least human readable + + replaced_buffer = replace(buffer, "\r", "\r\n"); + + pubmsg.payload = replaced_buffer; + pubmsg.payloadlen = strlen(replaced_buffer); + + printf("%s\n", replaced_buffer); + + return MQTTAsync_sendMessage(client, topic, &pubmsg, &opts); +} + +int MQTTConnect(char* host, int port, char* user, char* pass) +{ + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + char hostString[256]; + + sprintf(hostString, "tcp://%s:%d", host, port); + + printf("MQTT Connect to %s\n", hostString); + + rc = MQTTAsync_create(&client, hostString, NODECALLLOPPED, MQTTCLIENT_PERSISTENCE_NONE, NULL); + + if (rc != MQTTASYNC_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + return rc; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.username = user; + conn_opts.password = pass; + conn_opts.onSuccess = onConnect; + conn_opts.onFailure = onConnectFailure; +// conn_opts.automaticReconnect = 1; +// conn_opts.minRetryInterval = 30; +// conn_opts.maxRetryInterval = 300; + + rc = MQTTAsync_connect(client, &conn_opts); + + if (rc != MQTTASYNC_SUCCESS) + { + printf("Failed to start connect, return code %d\n", rc); + return rc; + } + + MQTT_Connecting = 1; + + return 0; +} + +// Message Database Entry. Designed to be compatible with FBB + +#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy +#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. + +#pragma pack(1) + +struct MsgInfo +{ + char type; + char status; + int number; + int length; + int xdatereceived; + char bbsfrom[7]; // ? BBS we got it from ? + char via[41]; + char from[7]; + char to[7]; + char bid[13]; + char title[61]; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; // Not all flags specific to B2 + + #define B2Msg 1 // Set if Message File is a formatted B2 message + #define Attachments 2 // Set if B2 message has attachments + #define FromPaclink 4 + #define FromCMS 8 + #define FromRMSExpress 16 + #define RadioOnlyMsg 32 // Received using call-T + #define RadioOnlyFwd 64 // Received using call-R + #define WarnNotForwardedSent 128 + + int xdatecreated; + int xdatechanged; + UCHAR fbbs[NBMASK]; + UCHAR forw[NBMASK]; + char emailfrom[41]; + char Locked; // Set if selected for sending (NTS Pickup) + char Defered; // FBB response '=' received + UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) + +// For 64 bit time_t compatibility define as long long +// (so struct is same with 32 or 64 bit time_t) + + int64_t datereceived; + int64_t datecreated; + int64_t datechanged; + + char Spare[61 - 24]; // For future use +} ; + +#pragma pack() + +void MQTTMessageEvent(void* message) +{ + struct MsgInfo* msg = (struct MsgInfo *)message; + char *msg_str; + char * ptr; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + char topic[256]; + + json_t *root = json_object(); + json_object_set_new(root, "id", json_integer(msg->number)); + json_object_set_new(root, "size", json_integer(msg->length)); + json_object_set_new(root, "type", json_string(msg->type == 'P' ? "P" : "B")); + json_object_set_new(root, "to", json_string(msg->to)); + json_object_set_new(root, "from", json_string(msg->from)); + json_object_set_new(root, "subj", json_string(msg->title)); + + switch(msg->status) { + case 'N': + json_object_set_new(root, "event", json_string("newmsg")); + break; + case 'F': + json_object_set_new(root, "event", json_string("fwded")); + break; + case 'R': + json_object_set_new(root, "event", json_string("read")); + break; + case 'K': + json_object_set_new(root, "event", json_string("killed")); + break; + } + + msg_str = json_dumps(root, 0); + + pubmsg.payload = msg_str; + pubmsg.payloadlen = strlen(msg_str); + + + sprintf(topic, "PACKETNODE/event/%s/pmsg", NODECALLLOPPED); + + MQTTAsync_sendMessage(client, topic, &pubmsg, &opts); +} + +#else + +// Dummies ofr build without MQTT libraries + +int MQTTConnect(char* host, int port, char* user, char* pass) +{ + return 0; +} + +void MQTTKISSTX(void *message) {}; +void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT) {}; +void MQTTKISSRX(void *message) {}; +void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {}; +void MQTTTimer() {}; +void MQTTReportSession(char * Msg) {}; +void MQTTMessageEvent(void* message); + +#endif + diff --git a/.svn/wc.db b/.svn/wc.db index 2c22d6d..be92d58 100644 Binary files a/.svn/wc.db and b/.svn/wc.db differ diff --git a/ARDOP.c b/ARDOP.c index a46fe3e..8230039 100644 --- a/ARDOP.c +++ b/ARDOP.c @@ -888,6 +888,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) { + Debugprintf("ARDOP closing session on SessionTimelimit"); ARDOPSendCommand(TNC, "DISCONNECT", TRUE); STREAM->ReportDISC = 1; STREAM->AttachTime = 0; @@ -900,6 +901,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) { + Debugprintf("ARDOP closing session on AttachTimelimit"); STREAM->ReportDISC = 1; STREAM->AttachTime = 0; } @@ -3141,6 +3143,7 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) // Incoming Connect TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + STREAM->AttachTime = time(NULL); // Stop other ports in same group diff --git a/Bpq32.c b/Bpq32.c index 36b5786..6755ea9 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1257,8 +1257,14 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Fix RigConrol with Chanxx but no other settings (66) // Add option to compress L2 frames (67) // Sort Routes displays (67) - - +// Fix Ardop session premature close (70) +// Add timestamps to log entries in Web Driver windows (70) +// Generate stack backtrace if SIGSEGV or SIGABRT occur (Linux) (70) +// Remove some debug logging from L2 code (70) +// Fix compiling LinBPQ with nomqtt option (70) +// Improve handling of binary data in RHP interface (70) +// Fix sending KISS commands to multiport or multidropped TNCs (70) +// Add MHUV and MHLV commands (Verbose listing with timestamps in clock time) (70) #define CKernel diff --git a/Cmd.c b/Cmd.c index 3ba2a55..399585b 100644 --- a/Cmd.c +++ b/Cmd.c @@ -3622,7 +3622,7 @@ VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CM } else { - if (CMD->String[2] == 'V') // MHV + if (CMD->String[2] == 'V' || CMD->String[3] == '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"); @@ -4326,6 +4326,8 @@ struct CMDX COMMANDS[] = "MHU ",3,MHCMD,0, // UTC Times "MHL ",3,MHCMD,0, // Local Times "MHV ",3,MHCMD,0, + "MHUV ",3,MHCMD,0, // UTC Times + "MHLV ",3,MHCMD,0, // Local Times "MHEARD ",1,MHCMD,0, "APRS ",2,APRSCMD,0, "ATTACH ",1,ATTACHCMD,0, @@ -5543,22 +5545,14 @@ VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct 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 KissLen = KissEncode(KissString, ENCBUFF, KissLen); - PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q - PORT->Session = Session; PORT->LastKISSCmdTime = time(NULL); + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q ASYSEND(PORT, ENCBUFF, KissLen); Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); diff --git a/CommonCode.c b/CommonCode.c index f5d5125..7344f4d 100644 --- a/CommonCode.c +++ b/CommonCode.c @@ -1061,6 +1061,7 @@ BOOL ReadConfigFile(int Port, int ProcLine(char * buf, int Port)) WritetoConsoleLocal("\n"); WritetoConsoleLocal("Bad config record "); WritetoConsoleLocal(errbuf); + WritetoConsoleLocal("\n"); } } } diff --git a/L2Code.c b/L2Code.c index 94fc9f5..2624d9e 100644 --- a/L2Code.c +++ b/L2Code.c @@ -2431,12 +2431,11 @@ CheckNSLoop: { // 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); +// Debugprintf ("Frame %d out of seq - save", NS); } Buffer->CHAIN = 0; @@ -3919,7 +3918,6 @@ CheckNSLoop2: 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. diff --git a/LinBPQ.c b/LinBPQ.c index fc5e900..a7641e3 100644 --- a/LinBPQ.c +++ b/LinBPQ.c @@ -381,41 +381,48 @@ BOOL CtrlHandler(DWORD fdwCtrlType) #include // Linux Signal Handlers - static void segvhandler(int sig) { - void *array[10]; - size_t size; - char msg[] = "SIGSEGV Received\n"; + void *array[10]; + size_t size; + char msg[] = "\nSIGSEGV Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); - write(STDERR_FILENO, msg, strlen(msg)); + // get void*'s for all entries on the stack + size = backtrace(array, 10); - // get void*'s for all entries on the stack - size = backtrace(array, 10); + // print out all the frames to stderr - // print out all the frames to stderr + backtrace_symbols_fd(array, size, STDERR_FILENO); - backtrace_symbols_fd(array, size, STDERR_FILENO); + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); - exit(1); + exit(1); } static void abrthandler(int sig) { - void *array[10]; - size_t size; - char msg[] = "SIGABRT Received\n"; + void *array[10]; + size_t size; + char msg[] = "\nSIGABRT Received\n"; - write(STDERR_FILENO, msg, strlen(msg)); + write(STDERR_FILENO, msg, strlen(msg)); + write(STDOUT_FILENO, msg, strlen(msg)); - // get void*'s for all entries on the stack + // get void*'s for all entries on the stack - size = backtrace(array, 10); - backtrace_symbols_fd(array, size, STDERR_FILENO); + size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); } + static void sigterm_handler(int sig) { syslog(LOG_INFO, "terminating on SIGTERM\n"); diff --git a/RHP.c b/RHP.c index 18b933d..a82b091 100644 --- a/RHP.c +++ b/RHP.c @@ -451,8 +451,13 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) int ID; char * Data; char * ptr; + unsigned char * uptr; int c; int Len; + unsigned int HexCode1; + unsigned int HexCode2; + + int n; int Handle = 1; @@ -470,9 +475,10 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) RHPSession = RHPSessions[Handle - 1]; - // Look for \ escapes + // Look for \ escapes, Can now also get \u00c3 ptr = Data; + Len = strlen(Data); // in case no escapes while (ptr = strchr(ptr, '\\')) { @@ -483,23 +489,60 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer) case 'r': *ptr = 13; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); break; + + case 'u': + + HexCode1 = HexCode2 = 0; + + n = toupper(ptr[2]) - '0'; + if (n > 9) n = n - 7; + HexCode1 |= n << 4; + + n = toupper(ptr[3]) - '0'; + if (n > 9) n = n - 7; + HexCode1 |= n; + + n = toupper(ptr[4]) - '0'; + if (n > 9) n = n - 7; + HexCode2 |= n << 4; + + n = toupper(ptr[5]) - '0'; + if (n > 9) n = n - 7; + HexCode2 |= n; + + if (HexCode1 == 0 || HexCode1 == 0xC2) + { + uptr = ptr; + *uptr = HexCode2; + } + else if (HexCode1 == 0xc2) + { + uptr = ptr; + *uptr = HexCode2 + 0x40; + } + + memmove(ptr + 1, ptr + 6, strlen(ptr + 5)); + break; + case '\\': *ptr = '\\'; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); break; case '"': *ptr = '"'; + memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); break; } - memmove(ptr + 1, ptr + 2, strlen(ptr + 1)); ptr++; + Len = ptr - Data; } - Len = strlen(Data); ptr = Data; while (Len > RHPPaclen) @@ -640,7 +683,7 @@ void RHPPoll() // Message is JSON so Convert CR to \r, \ to \\ " to \" - // Looks like I need to escape everything not between 0x20 and 0x7f eg \U00c3 + // Looks like I need to escape everything not between 0x20 and 0x7f eg \u00c3 while (c = *(ptr)) diff --git a/VARA.c b/VARA.c index 4d46984..4574fa4 100644 --- a/VARA.c +++ b/VARA.c @@ -1720,7 +1720,6 @@ VConnected: GetSemaphore(&Semaphore, 52); VARAProcessReceivedControl(TNC); FreeSemaphore(&Semaphore); - Debugprintf("VARA Returned from processing control packet"); } if (FD_ISSET(TNC->TCPDataSock, &readfs)) diff --git a/Versions.h b/Versions.h index 707d0f4..b014fb0 100644 --- a/Versions.h +++ b/Versions.h @@ -10,15 +10,15 @@ #endif -#define KVers 6,0,24,69 -#define KVerstring "6.0.24.69\0" +#define KVers 6,0,24,70 +#define KVerstring "6.0.24.70\0" #ifdef CKernel #define Vers KVers #define Verstring KVerstring -#define Datestring "March 2025" +#define Datestring "April 2025" #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0" #define VerDesc "BPQ32 Switch\0" diff --git a/WINMOR.c b/WINMOR.c index f0f9d98..2a3d766 100644 --- a/WINMOR.c +++ b/WINMOR.c @@ -139,6 +139,10 @@ VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) int LineLen, i; UCHAR Save; int SaveLen = Len; + char Time[16]; + time_t T; + struct tm * tm; + if (Len < 0) return; @@ -206,10 +210,16 @@ lineloop: #endif // Write to Web Buffer + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + + strcat(TNC->WebBuffer, Time); strcat(TNC->WebBuffer, Line); strcat(TNC->WebBuffer, "\r\n"); if (strlen(TNC->WebBuffer) > 4500) - memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved Skip: ptr1 = ptr2; @@ -248,10 +258,16 @@ lineloop: #else index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); #endif + T = time(NULL); + tm = gmtime(&T); + + sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min); + strcat(TNC->WebBuffer, Time); + strcat(TNC->WebBuffer, ptr1); strcat(TNC->WebBuffer, "\r\n"); if (strlen(TNC->WebBuffer) > 4500) - memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved } } diff --git a/makefile b/makefile index 7f870ff..6d7c6e4 100644 --- a/makefile +++ b/makefile @@ -19,15 +19,15 @@ OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pn CC = gcc -all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon +all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon -fasynchronous-unwind-tables all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a all: linbpq -nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT +nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables nomqtt: linbpq -noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon +noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables noi2c: linbpq diff --git a/mqtt.c b/mqtt.c index ff1c716..ec141c3 100644 --- a/mqtt.c +++ b/mqtt.c @@ -429,6 +429,7 @@ void MQTTKISSRX(void *message) {}; void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {}; void MQTTTimer() {}; void MQTTReportSession(char * Msg) {}; +void MQTTMessageEvent(void* message); #endif diff --git a/upnp.c b/upnp.c index 96cb9d3..931d0c7 100644 --- a/upnp.c +++ b/upnp.c @@ -93,7 +93,12 @@ int i; const char * rootdescurl = 0; const char * multicastif = 0; const char * minissdpdpath = 0; +#ifdef UPNP_LOCAL_PORT_ANY int localport = UPNP_LOCAL_PORT_ANY; +#else +#pragma message "API 10" +int localport = 0; +#endif int retcode = 0; int error = 0; int ipv6 = 0; @@ -119,8 +124,11 @@ int upnpInit() { if (devlist == NULL) { +#if MINIUPNPC_API_VERSION == 10 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error); +#else devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); - +#endif if (devlist == NULL) { Consoleprintf("Failed to find a UPNP device"); @@ -150,8 +158,11 @@ int upnpClose() { if (devlist == NULL) { +#if MINIUPNPC_API_VERSION == 10 + devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error); +#else devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); - +#endif if (devlist == NULL) { Consoleprintf("Failed to find a UPNP device");