From 48f7c6192c43498a0dc5d7171907019bdee3c28d Mon Sep 17 00:00:00 2001
From: John Wiseman
Date: Thu, 26 Jun 2025 07:27:23 +0100
Subject: [PATCH] 6.0.24.75
---
...eb5f4923cf350fd823885589ed27082b2.svn-base | 1475 ++
...efa92a9f216ca7b20c078d512b27c7651.svn-base | 126 +
...be9dd27b9f65d60e4740c6fdcd97b0f64.svn-base | 3743 ++++
...7bae8af6d7cc5bf04448872bfcbeea6ef.svn-base | 825 +
...ceece14e8c1ac8486bc403a84d9d8f8d2.svn-base | 5678 ++++++
...27d90c9501bad9d7d68500e6575c6a013.svn-base | 6508 +++++++
...28e5aa212bea028ed2fd889fa4b647226.svn-base | 2271 +++
...27e79e2735604e7a7b4ebbd159efc7131.svn-base | 7214 +++++++
...5ebc255e8deab2d4959906c90cd4da0bc.svn-base | 451 +
...7d130b6ae7b7f63f09cb0c226b889957b.svn-base | 604 +
...20b69ea8a4b5a5a7440fee25ef7793638.svn-base | 16028 ++++++++++++++++
BBSUtilities.c | 58 +-
BPQMail.c | 4 +
ChatHTMLConfig.c | 4 +-
CommonCode.c | 11 +-
KAMPactor.c | 123 +-
TelnetV6.c | 2 +
Versions.h | 6 +-
WebMail.c | 36 +
asmstrucs.h | 3 +
bpq32.h | 2 +
cheaders.h | 2 +-
22 files changed, 45151 insertions(+), 23 deletions(-)
create mode 100644 .svn/pristine/29/2913092eb5f4923cf350fd823885589ed27082b2.svn-base
create mode 100644 .svn/pristine/3a/3a85a28efa92a9f216ca7b20c078d512b27c7651.svn-base
create mode 100644 .svn/pristine/3b/3bea880be9dd27b9f65d60e4740c6fdcd97b0f64.svn-base
create mode 100644 .svn/pristine/90/90a09ec7bae8af6d7cc5bf04448872bfcbeea6ef.svn-base
create mode 100644 .svn/pristine/9a/9a51b1dceece14e8c1ac8486bc403a84d9d8f8d2.svn-base
create mode 100644 .svn/pristine/a7/a780a3d27d90c9501bad9d7d68500e6575c6a013.svn-base
create mode 100644 .svn/pristine/b4/b49079528e5aa212bea028ed2fd889fa4b647226.svn-base
create mode 100644 .svn/pristine/b5/b526b9227e79e2735604e7a7b4ebbd159efc7131.svn-base
create mode 100644 .svn/pristine/b7/b7180e45ebc255e8deab2d4959906c90cd4da0bc.svn-base
create mode 100644 .svn/pristine/c2/c2e58237d130b6ae7b7f63f09cb0c226b889957b.svn-base
create mode 100644 .svn/pristine/f1/f18b5f920b69ea8a4b5a5a7440fee25ef7793638.svn-base
diff --git a/.svn/pristine/29/2913092eb5f4923cf350fd823885589ed27082b2.svn-base b/.svn/pristine/29/2913092eb5f4923cf350fd823885589ed27082b2.svn-base
new file mode 100644
index 0000000..aa73b93
--- /dev/null
+++ b/.svn/pristine/29/2913092eb5f4923cf350fd823885589ed27082b2.svn-base
@@ -0,0 +1,1475 @@
+// C equivalents of ASM STRUCS
+
+// Aug 2010 Extend Applmask to 32 bit
+
+#ifndef _ASMSTRUCS_
+#define _ASMSTRUCS_
+
+#ifdef WIN32
+
+#include "winsock2.h"
+#include "ws2tcpip.h"
+#include "winstdint.h"
+
+#endif
+
+#define BPQICON 2
+
+#define BUFFLEN 400
+#define BUFFALLOC 464 // Actual size of buffer. Extra 64 bytes for a source code tag to help find buffer leaks
+
+
+//#define ApplStringLen 48 // Length of each config entry
+#define NumberofAppls 32 // Max APPLICATIONS= values
+#define ALIASLEN 48
+#define MHENTRIES 30 // Entries in MH List
+
+#define MaxLockedRoutes 100
+
+typedef int (FAR *FARPROCY)();
+
+#define NRPID 0xcf // NETROM PID
+
+#define L4BUSY 0x80 // BNA - DONT SEND ANY MORE
+#define L4NAK 0x40 // NEGATIVE RESPONSE FLAG
+#define L4MORE 0x20 // MORE DATA FOLLOWS - FRAGMENTATION FLAG
+#define L4COMP 0x10 // BPQ Specific use of spare it - data is compressed
+
+#define L4CREQ 1 // CONNECT REQUEST
+#define L4CACK 2 // CONNECT ACK
+#define L4DREQ 3 // DISCONNECT REQUEST
+#define L4DACK 4 // DISCONNECT ACK
+#define L4INFO 5 // INFORMATION
+#define L4IACK 6 // INFORMATION ACK
+#define L4RESET 7 // Paula's extension
+
+
+extern char MYCALL[]; // 7 chars, ax.25 format
+extern char MYALIASTEXT[]; // 6 chars, not null terminated
+extern UCHAR L3RTT[7]; // 7 chars, ax.25 format
+extern UCHAR L3KEEP[7]; // 7 chars, ax.25 format
+//extern int SENDNETFRAME();
+extern struct _DATABASE * DataBase;
+//extern APPLCALLS APPLCALLTABLE[8];
+
+extern int MAXDESTS;
+
+//extern VOID * GETBUFF();
+//extern VOID SETUPNODEHEADER();
+extern VOID POSTDATAAVAIL();
+
+
+extern int DATABASE;
+extern int ENDOFDATA;
+extern int L3LIVES;
+extern int NUMBEROFNODES;
+
+struct CMDX
+{
+ char String[12]; // COMMAND STRING
+ UCHAR CMDLEN; // SIGNIFICANT LENGTH
+// VOID (*CMDPROC)(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);// COMMAND PROCESSOR
+ VOID (*CMDPROC)();// COMMAND PROCESSOR
+ size_t CMDFLAG; // FLAG/VALUE Offset
+
+};
+
+struct APPLCONFIG
+{
+ char Command[12];
+ char CommandAlias[48];
+ char ApplCall[10];
+ char ApplAlias[10];
+ char L2Alias[10];
+ int ApplQual;
+};
+
+typedef struct _MHSTRUC
+{
+ UCHAR MHCALL[7];
+ UCHAR MHDIGIS[7][8];
+ time_t MHTIME;
+ int MHCOUNT;
+ BYTE MHDIGI;
+ char MHFreq[12];
+ char MHLocator[6];
+} MHSTRUC, *PMHSTRUC;
+
+//
+// Session Record
+//
+
+typedef struct _TRANSPORTENTRY
+{
+ UCHAR L4USER[7]; // CALL OF ORIGINATING USER
+
+ union // POINTER TO TARGET HOST/LINK/DEST/PORT (if Pactor)
+ {
+ struct PORTCONTROL * PORT;
+ struct _LINKTABLE * LINK;
+ struct DEST_LIST * DEST;
+ struct _BPQVECSTRUC * HOST;
+ struct _EXTPORTDATA * EXTPORT;
+ } L4TARGET;
+
+ UCHAR L4MYCALL[7]; // CALL WE ARE USING
+
+ UCHAR CIRCUITINDEX; // OUR CIRCUIT INFO
+ UCHAR CIRCUITID; //
+
+ UCHAR FARINDEX; //
+ UCHAR FARID; // OTHER END'S INFO
+
+ UCHAR L4WINDOW; // WINDOW SIZE
+ UCHAR L4WS; // WINDOW START - NEXT FRAME TO ACK
+ UCHAR TXSEQNO; //
+ UCHAR RXSEQNO; // TRANSPORT LEVEL SEQUENCE INFO
+ UCHAR L4LASTACKED; // LAST SEQUENCE ACKED
+
+ UCHAR FLAGS; // TRANSPORT LEVEL FLAGS
+ UCHAR NAKBITS; // NAK & CHOKE BITS TO BE SENT
+ struct _TRANSPORTENTRY * L4CROSSLINK; // POINTER TO LINKED L4 SESSION ENTRY
+ UCHAR L4CIRCUITTYPE; // BIT SIGNIFICANT - SEE BELOW
+ UCHAR KAMSESSION; // Session Number on KAM Host Mode TNC
+ struct DATAMESSAGE * L4TX_Q;
+ struct _L3MESSAGEBUFFER * L4RX_Q;
+ struct DATAMESSAGE * L4HOLD_Q; // FRAMES WAITING TO BE ACKED
+ struct _L3MESSAGEBUFFER * L4RESEQ_Q; // FRAMES RECEIVED OUT OF SEQUENCE
+
+ UCHAR L4STATE;
+ USHORT L4TIMER;
+ UCHAR L4ACKREQ; // DATA ACK NEEDED
+ UCHAR L4RETRIES; // RETRY COUNTER
+ USHORT L4KILLTIMER; // IDLE CIRCUIT TIMER
+ USHORT SESSIONT1; // TIMEOUT FOR SESSIONS FROM HERE
+ UCHAR SESSPACLEN; // PACLEN FOR THIS SESSION
+ UCHAR BADCOMMANDS; // SUCCESSIVE BAD COMMANDS
+ UCHAR STAYFLAG; // STAY CONNECTED FLAG
+ UCHAR SPYFLAG; // SPY - CONNECT TO NODE VIA BBS CALLSIGN
+
+ UCHAR RTT_SEQ; // SEQUENCE NUMBER BEING TIMED
+ uint32_t RTT_TIMER; // TIME ABOVE SEQUENCE WAS SENT
+
+ USHORT PASSWORD; // AUTHORISATION CODE FOR REMOTE SYSOP
+
+ UCHAR SESS_APPLFLAGS; // APPL FLAGS FOR THIS SESSION
+
+ UCHAR Secure_Session; // Set if Host session from BPQTerminal or BPQMailChat
+
+ VOID * DUMPPTR; // POINTER FOR REMOTnE DUMP MODE
+ VOID * PARTCMDBUFFER; // Save area for incomplete commmand
+
+ long long Frequency; // If known - for CMS Reporting Hz
+ char RMSCall[10];
+ UCHAR Mode; // ditto
+
+ int UNPROTO; // Unproto Mode flag - port number if in unproto mode
+ int UAddrLen; //
+ char UADDRESS[64]; // Unproto Address String - Dest + Digis
+
+ uint64_t LISTEN; // Port Mask if in Listen Mode
+
+ char APPL[16]; // Set if session initiated by an APPL
+ int L4LIMIT; // Idle time for this Session
+
+ // Now support compressing NetRom Sessions.
+ // We collect as much data as possible before compressing and re-packetizing
+
+ int AllowCompress;
+
+ unsigned char * unCompress; // Data being saved to uncompress
+ int unCompressLen;
+
+ int Sent;
+ int SentAfterCompression;
+
+ int Received;
+ int ReceivedAfterExpansion;
+
+
+} TRANSPORTENTRY;
+
+//
+// CIRCUITTYPE EQUATES
+//
+
+#define L2LINK 1
+#define SESSION 2
+#define UPLINK 4
+#define DOWNLINK 8
+#define BPQHOST 32
+#define PACTOR 64
+
+typedef struct ROUTE
+{
+ // Adjacent Nodes
+
+ UCHAR NEIGHBOUR_CALL[7]; // AX25 CALLSIGN
+ UCHAR NEIGHBOUR_DIGI1[7]; // DIGIS EN ROUTE (MAX 2 - ?? REMOVE)
+ UCHAR NEIGHBOUR_DIGI2[7]; // DIGIS EN ROUTE (MAX 2 - ?? REMOVE)
+
+ UCHAR NEIGHBOUR_PORT;
+ UCHAR NEIGHBOUR_QUAL;
+ UCHAR NEIGHBOUR_FLAG; // SET IF 'LOCKED' ROUTE
+
+#define LOCKEDBYCONFIG 1
+#define LOCKEDBYSYSOP 2
+
+ struct _LINKTABLE * NEIGHBOUR_LINK; // POINTER TO LINK FOR THIS NEIGHBOUR
+
+ USHORT NEIGHBOUR_TIME; // TIME LAST HEARD (HH MM)
+
+ int NBOUR_IFRAMES; // FRAMES SENT/RECEIVED
+ int NBOUR_RETRIES; // RETRASMISSIONS
+
+ UCHAR NBOUR_MAXFRAME; // FOR OPTIMISATION CODE
+ UCHAR NBOUR_FRACK;
+ UCHAR NBOUR_PACLEN;
+
+ BOOL INP3Node;
+ BOOL NoKeepAlive; // Suppress Keepalive Processing
+ int LastConnectAttempt; // To stop us trying too often
+
+ int Status; //
+ int LastRTT; // Last Value Reported
+ int RTT; // Current
+ int SRTT; // Smoothed RTT
+ int NeighbourSRTT; // Other End SRTT
+// int RTTIncrement; // Average of Ours and Neighbours SRTT in 10 ms
+ int BCTimer; // Time to next L3RTT Broadcast
+ int Timeout; // Lost Response Timer
+ int Retries; // Lost Response Count
+ struct _L3MESSAGEBUFFER * Msg; // RIF being built
+
+ int OtherendsRouteQual; // Route quality used by other end.
+ int OtherendLocked; // Route quality locked by ROUTES entry.
+ int FirstTimeFlag; // Set once quality has been set by direct receive
+
+} *PROUTE;
+
+// Status Equates
+
+#define GotRTTRequest 1 // Other end has sent us a RTT Packet
+#define GotRTTResponse 2 // Other end has sent us a RTT Response
+#define GotRIF 4 // Other end has sent RIF, so is probably an INP3 Node
+ // (could just be monitoring RTT for some reason
+#define SentRTTRequest 8
+#define SentOurRIF 16 // Set when we have sent a rif for our Call and any ApplCalls
+ // (only sent when we have seen both a request and response)
+
+#pragma pack(1)
+
+typedef struct _L3MESSAGEBUFFER
+{
+//
+// NETROM LEVEL 3 MESSAGE with Buffer Header
+//
+ struct _L3MESSAGEBUFFER * Next;
+ UCHAR Port;
+ SHORT LENGTH;
+ UCHAR L3PID; // PID
+
+ UCHAR L3SRCE[7]; // ORIGIN NODE
+ UCHAR L3DEST[7]; // DEST NODE
+ UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING
+ // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP
+//
+// NETROM LEVEL 4 DATA
+//
+ UCHAR L4INDEX; // TRANSPORT SESSION INDEX
+ UCHAR L4ID; // TRANSPORT SESSION ID
+ UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER
+ UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER
+ UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS
+
+ UCHAR L4DATA[236] ; //DATA
+
+} L3MESSAGEBUFFER, *PL3MESSAGEBUFFER;
+
+
+typedef struct _L3MESSAGE
+{
+//
+// NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO
+//
+ UCHAR L3SRCE[7]; // ORIGIN NODE
+ UCHAR L3DEST[7]; // DEST NODE
+ UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP
+//
+// NETROM LEVEL 4 DATA
+//
+ UCHAR L4INDEX; // TRANSPORT SESSION INDEX
+ UCHAR L4ID; // TRANSPORT SESSION ID
+ UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER
+ UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER
+ UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS
+
+ UCHAR L4DATA[236] ; //DATA
+
+} L3MESSAGE, *PL3MESSAGE;
+
+#define MSGHDDRLEN (USHORT)(sizeof(VOID *) + sizeof(UCHAR) + sizeof(USHORT))
+
+typedef struct _MESSAGE
+{
+// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT
+
+ struct _MESSAGE * CHAIN;
+
+ UCHAR PORT;
+ USHORT LENGTH;
+
+ UCHAR DEST[7];
+ UCHAR ORIGIN[7];
+
+// MAY BE UP TO 56 BYTES OF DIGIS
+
+ UCHAR CTL;
+ UCHAR PID;
+
+ union
+ { /* array named screen */
+ UCHAR L2DATA[256];
+ struct _L3MESSAGE L3MSG;
+ };
+
+ UCHAR Padding[BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + 256 + MSGHDDRLEN + 16)]; // 16 = Addrs CTL PID
+
+ time_t Timestamp;
+ struct _LINKTABLE * Linkptr; // For ACKMODE processing
+ unsigned short Process; // Process that got buffer
+ unsigned short GuardZone; // Should always be zero
+
+}MESSAGE, *PMESSAGE;
+
+#define MAXDATA BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + MSGHDDRLEN + 16) // 16 = Addrs CTL PID
+
+
+typedef struct HDDRWITHDIGIS
+{
+// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT
+
+ struct _MESSAGE * CHAIN;
+
+ UCHAR PORT;
+ USHORT LENGTH;
+
+ UCHAR DEST[7];
+ UCHAR ORIGIN[7];
+ UCHAR DIGIS[8][7];
+
+ UCHAR CTL;
+ UCHAR PID;
+
+ union
+ {
+ UCHAR L2DATA[256];
+ struct _L3MESSAGE L3MSG;
+
+ };
+
+ UCHAR Padding[BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + 256 + 56 + MSGHDDRLEN + 16)]; // 16 = Addrs CTL PID
+
+ time_t Timestamp;
+ struct _LINKTABLE * Linkptr; // For ACKMODE processing
+ unsigned short Process; // Process that got buffer
+ unsigned short GuardZone;
+
+} DIGIMESSAGE, *PDIGIMESSAGE;
+
+
+typedef struct DATAMESSAGE
+{
+// BASIC LINK LEVEL MESSAGE HEADERLAYOUT
+
+ struct DATAMESSAGE * CHAIN;
+
+ UCHAR PORT;
+ USHORT LENGTH;
+
+ UCHAR PID;
+
+ UCHAR L2DATA[256];
+
+} *PDATAMESSAGE;
+
+typedef struct MSGWITHLEN
+{
+ // BASIC LINK LEVEL MESSAGE HEADERLAYOUT
+
+ struct MSGWITHLEN * Next;
+ size_t Len;
+ UCHAR Data[256];
+
+} *PMSGWITHLEN;
+
+typedef struct MSGWITHOUTLEN
+{
+ // Basic Chained Buffer
+
+ struct MSGWITHLEN * Next;
+ UCHAR Data[256];
+
+} *PMSGWITHOUTLEN;
+
+//
+// BPQHOST MODE VECTOR STRUC
+//
+
+#pragma pack()
+
+typedef struct _BPQVECSTRUC
+{
+ struct _TRANSPORTENTRY * HOSTSESSION; // Pointer to Session
+ UCHAR HOSTFLAGS; // ALLOCATED AND STATE CHANGE FLAGS
+ ULONG HOSTAPPLMASK;
+ UCHAR HOSTAPPLFLAGS;
+ UCHAR HOSTSTREAM; // STREAM NUMBER
+ PMESSAGE HOSTTRACEQ;
+ HWND HOSTHANDLE; // HANDLE FOR POSTING MSGS TO
+ ULONG HOSTAPPLNUM; // Application Number
+ ULONG STREAMOWNER; // PID of Process owning stream
+ char PgmName[32]; // Program Name;
+
+
+} BPQVECSTRUC, *PBPQVECSTRUC;
+
+typedef struct _APPLCALLS
+{
+
+// Application Calls/Alias Supports multiple L4 application calls
+
+ UCHAR APPLCALL[7]; // ax.25
+ char APPLALIAS_TEXT[10]; // TEXT, WITH APPENDED SPACE
+
+ char APPLCALL_TEXT[10];
+ UCHAR APPLALIAS[6];
+ char Filler; // So we can use ConvtoAX25 on Alias
+ USHORT APPLQUAL;
+ struct DEST_LIST * NODEPOINTER; // Pointer to "NODES" entry for this App (if L4)
+
+ char APPLCMD[13]; //
+ BOOL APPLHASALIAS;
+ int APPLPORT; // Port used if APPL has an Alias
+ char APPLALIASVAL[48]; // Alias if defined
+ UCHAR L2ALIAS[7]; // Additional Alias foe L2 connects
+
+} APPLCALLS;
+
+//
+// We store the Time Received from our neighbour. Before using it we add our current route SRTT
+// This way our times adjust to changes of neighbour SRTT. We can't cater for changes to other hop RTTs,
+// But if these are significant (say 25% or 100 ms) they will be retransmitted
+
+// We treat the Routes as an array of 6. First 3 are NODES routes, next 3 are INP3 Routes. This works, but maybe is not ideal
+
+typedef struct NR_DEST_ROUTE_ENTRY
+{
+ struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH
+ UCHAR ROUT_QUALITY; // QUALITY
+ UCHAR ROUT_OBSCOUNT;
+ UCHAR ROUT_LOCKED;
+ UCHAR Padding[4]; // So Entries are the same length
+} *PNR_DEST_ROUTE_ENTRY;
+
+typedef struct INP3_DEST_ROUTE_ENTRY
+{
+ struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH
+ USHORT LastRTT; // Last Value Reported
+ USHORT RTT; // Current
+ USHORT SRTT; // Smoothed RTT
+ UCHAR Hops;
+} *PDEST_ROUTE_ENTRY;
+
+typedef struct DEST_LIST
+{
+ // L4 Destinations (NODES)
+
+ struct DEST_LIST * DEST_CHAIN; // SORTED LIST CHAIN
+
+ UCHAR DEST_CALL[7]; // DESTINATION CALLSIGN (AX25 FORMAT)
+ UCHAR DEST_ALIAS[6];
+
+ UCHAR DEST_STATE; // CONTROL BITS - SETTING UP, ACTIVE ETC
+ UCHAR DEST_LOCKED;
+
+ UCHAR DEST_ROUTE; // CURRENTY ACTIVE DESTINATION
+ UCHAR INP3FLAGS;
+
+ struct NR_DEST_ROUTE_ENTRY NRROUTE[3]; // Best 3 NR neighbours for this dest
+ struct INP3_DEST_ROUTE_ENTRY INP3ROUTE[3]; // Best 3 INP neighbours for this dest
+
+ void * DEST_Q; // QUEUE OF FRAMES FOR THIS DESTINATION
+
+ int DEST_RTT; // SMOOTHED ROUND TRIP TIMER
+ int DEST_COUNT; // FRAMES SENT
+
+} dest_list;
+
+// IMNP3FLAGS Equates
+
+#define NewNode 1 // Just added to table, so need to pass on
+
+struct XDIGI
+{
+ struct XDIGI * Next; // Chain
+
+ UCHAR Call[7];
+ UCHAR Alias[7];
+ int Port;
+ BOOL UIOnly;
+};
+
+struct WL2KInfo
+{
+ struct WL2KInfo * Next;
+
+ char * Host;
+ short WL2KPort;
+
+ char RMSCall[10];
+ char BaseCall[10];
+ char GridSquare[7];
+ char Times[80];
+ char ServiceCode[17];
+
+ BOOL UseRigCtrlFreqs;
+ char WL2KFreq[12];
+ char xWL2KMode; // WL2K reporting mode
+ char WL2KModeChar; // W or N
+ BOOL DontReportNarrowOnWideFreqs;
+
+// char NARROWMODE;
+// char WIDEMODE; // Mode numbers to report to WL2K
+
+// struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K
+
+ long long Freq;
+ char Bandwidth;
+// char * TimeList; // eg 06-10,12-15
+ int mode; // see below (an integer)
+ int baud; // see below (an integer)
+ int power; // actual power if known, default to 100 for HF, 30 for VHF/UHF (an integer)
+ int height; // antenna height in feet if known, default to 25
+ int gain; // antenna gain if known, default to 0
+ int direction; // primary antenna direction in degrees if known, use 000 for omni (an integer)
+ BOOL RPonPTC; // Set if scanning for Robust Packet on a PTC
+};
+
+
+typedef struct PORTCONTROL
+{
+ UCHAR PORTCALL[7];
+ UCHAR PORTALIAS[7]; //USED FOR UPLINKS ONLY
+ char PORTNUMBER;
+ char PROTOCOL; // PORT PROTOCOL
+ // 0 = KISS, 2 = NETROM, 4 = BPQKISS
+ //; 6 = HDLC, 8 = L2
+
+ struct PORTCONTROL * PORTPOINTER; // NEXT IN CHAIN
+
+ PMESSAGE PORTRX_Q; // FRAMES RECEIVED ON THIS PORT
+ PMESSAGE PORTTX_Q; // FRAMES TO BE SENT ON THIS PORT
+
+ void (FAR * PORTTXROUTINE)(struct _EXTPORTDATA * PORTVEC, MESSAGE * Buffer); // POINTER TO TRANSMIT ROUTINE FOR THIS PORT
+ void (FAR * PORTRXROUTINE)(struct _EXTPORTDATA * PORTVEC); // POINTER TO RECEIVE ROUTINE FOR THIS PORT
+ void (FAR * PORTINITCODE)(struct PORTCONTROL * PortVector); // INITIALISATION ROUTINE
+ void (FAR * PORTTIMERCODE)(struct PORTCONTROL * PortVector); //
+ void (FAR * PORTCLOSECODE)(struct PORTCONTROL * PortVector); // CLOSE ROUTINE
+ int (FAR * PORTTXCHECKCODE)(struct PORTCONTROL * PORTVEC, int Chan); // OK to TX Check
+ BOOL (FAR * PORTSTOPCODE)(struct PORTCONTROL * PORT); // Temporarily Stop Port
+ BOOL (FAR * PORTSTARTCODE)(struct PORTCONTROL * PORT); // Restart Port
+ BOOL PortStopped; // STOPPORT command used
+ BOOL PortSuspended; // Suspended by interlock
+
+ char PORTDESCRIPTION[31];// TEXT DESCRIPTION OF FREQ/SPEED ETC (31 so null terminated)
+
+ UCHAR PORTQUALITY; // 'STANDARD' QUALITY FOR THIS PORT
+
+ char PORTBBSFLAG; // NZ MEANS PORT CALL/ALIAS ARE FOR BBS
+ char PORTL3FLAG; // NZ RESTRICTS OUTGOING L2 CONNECTS
+//
+// CWID FIELDS
+//
+
+ USHORT CWID[9]; // 8 ELEMENTS + FLAG
+ USHORT ELEMENT; // REMAINING BITS OF CURRENT CHAR
+ USHORT * CWPOINTER; // POINTER TO NEXT CHAR
+ USHORT CWIDTIMER; // TIME TO NEXT ID
+ char CWSTATE; // STATE MACHINE FOR CWID
+ char CWTYPE; // SET TO USE ON/OFF KEYING INSTEAD OF
+ // FSK (FOR RUH MODEMS)
+ UCHAR PORTMINQUAL; // MIN QUAL TO BRAOCAST ON THIS PORT
+
+// STATS COUNTERS
+
+ int L2DIGIED;
+ int L2FRAMES;
+ int L2FRAMESFORUS;
+ int L2FRAMESSENT;
+ int L2TIMEOUTS;
+ int L2ORUNC; // OVERRUNS
+ int L2URUNC; // UNDERRUNS
+ int L1DISCARD; // FRAMES DISCARDED (UNABLE TO TX DUE TO DCD)
+ int L2FRMRRX;
+ int L2FRMRTX;
+ int RXERRORS; // RECEIVE ERRORS
+ int L2REJCOUNT; // REJ FRAMES RECEIVED
+ int L2OUTOFSEQ; // FRAMES RECEIVED OUT OF SEQUENCE
+ int L2RESEQ; // FRAMES RESEQUENCED
+
+ USHORT SENDING; // LINK STATUS BITS
+ USHORT ACTIVE;
+
+ UCHAR AVSENDING; // LAST MINUTE
+ UCHAR AVACTIVE;
+
+ char PktFlags[64]; // Decode stts rom QtSM
+
+ char PORTTYPE; // H/W TYPE
+ // 0 = ASYNC, 2 = PC120, 4 = DRSI
+ // 6 = TOSH, 8 = QUAD, 10 = RLC100
+ // 12 = RLC400 14 = INTERNAL 16 = EXTERNAL
+
+
+ USHORT IOBASE; // CONFIG PARAMS FOR HARDWARE DRIVERS
+
+ char INTLEVEL; // NEXT 4 SAME FOR ALL H/W TYPES
+ int BAUDRATE; // SPEED
+ char CHANNELNUM; // ON MULTICHANNEL H/W
+ struct PORTCONTROL * INTCHAIN; // POINTER TO NEXT PORT USING THIS LEVEL
+ UCHAR PORTWINDOW; // L2 WINDOW FOR THIS PORT
+ USHORT PORTTXDELAY; // TX DELAY FOR THIS PORT
+ UCHAR PORTPERSISTANCE; // PERSISTANCE VALUE FOR THIS PORT
+ UCHAR FULLDUPLEX; // FULL DUPLEX IF SET
+ UCHAR SOFTDCDFLAG; // IF SET USE 'SOFT DCD' - IF MODEM CANT GIVE A REAL ONE
+ UCHAR PORTSLOTTIME; // SLOT TIME
+ UCHAR PORTTAILTIME; // TAIL TIME
+ UCHAR PORTT1; // L2 TIMEOUT
+ UCHAR PORTT2; // L2 DELAYED ACK TIMER
+ UCHAR PORTN2; // RETRIES
+ UCHAR PORTPACLEN; // DEFAULT PACLEN FOR INCOMING SESSIONS
+
+ UINT * PORTINTERRUPT; // ADDRESS OF INTERRUPT HANDLER
+
+ UCHAR QUAL_ADJUST; // % REDUCTION IN QUALITY IF ON SAME PORT
+
+ char * PERMITTEDCALLS; // POINTER TO PERMITED CALLS LIST
+ char * PORTUNPROTO; // POINTER TO UI DEST AND DIGI LIST
+ UCHAR PORTDISABLED; // PORT TX DISABLE FLAG
+ char DIGIFLAG; // ENABLE/DISABLE/UI ONLY
+ UCHAR DIGIPORT; // CROSSBAND DIGI PORT
+ USHORT DIGIMASK; // CROSSBAND DIGI MASK
+ UCHAR USERS; // MAX USERS ON PORT
+ USHORT KISSFLAGS; // KISS SPECIAL MODE BITS
+ UCHAR PORTINTERLOCK; // TO DEFINE PORTS WHICH CANT TX AT SAME TIME
+ //
+ // PORTINTERLOCK was also used to prevent use of Attach'able ports at the same time
+ // and was the RADIO number used in rig control
+ //
+ // Now a port can have both a rx and tx radio definition, so Attach now
+ // Checks them (Transferred to TNC->TXRADIO and RXRADIO
+ //
+ // For backward compatibility of configs, if INTERLOCK is set on an Attachable
+ // port it is set as a default tx and rx radio
+ //
+ // Acually may not need the following PORT fields
+
+ UCHAR PORTXRADIO; // TO DEFINE PORTS WHICH CANT TX Attach AT SAME TIME
+ UCHAR PORRXRADIO; // TO DEFINE PORTS WHICH CANT TX Attach AT SAME TIME
+ UCHAR NODESPACLEN; // MAX LENGTH OF 'NODES' MSG
+ UCHAR TXPORT; // PORT FOR SHARED TX OPERATION
+ MHSTRUC * PORTMHEARD; // POINTER TO MH DATA
+
+ USHORT PARAMTIMER; // MOVED FROM HW DATA FOR SYSOPH
+ UCHAR PORTMAXDIGIS; // DIGIS ALLOWED ON THIS PORT
+ UCHAR PORTALIAS2[7]; // 2ND ALIAS FOR DIGIPEATING FOR APRS
+ UCHAR PORTBCALL[7]; // Source call for Beacon
+ char PortNoKeepAlive; // Default to no Keepalives
+ char PortUIONLY; // UI only port - no connects
+ char UICAPABLE; // Pactor-style port that can do UI
+
+ struct WL2KInfo WL2KInfo; // WL2K Report for this Port
+ struct in_addr PORTIPADDR; // IP address for "KISS over UDP"
+ int ListenPort; // For KISS over UDP, if Different TX and RX Ports needed
+ BOOL KISSTCP; // TCP instead of UDP for KISS
+ BOOL KISSSLAVE; // TCP KISS is Slave
+
+ char * SerialPortName; // Serial Port Name for Unix
+ struct XDIGI * XDIGIS; // Cross port digi setup
+
+ BOOL NormalizeQuality; // Normalise Node Qualities
+ BOOL IgnoreUnlocked; // Ignore Unlocked routes
+ BOOL INP3ONLY; // Default to INP3 and disallow NODES
+ BOOL ALLOWINP3;
+
+ void (* UIHook)(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); // Used for KISSARQ
+ struct PORTCONTROL * HookPort;
+ int PortSlot; // Index in Port Table
+ struct TNCINFO * TNC; // Associated TNC record
+ int HWType; // Hardware type of Driver. In here as external apps don't have access to TNC record
+ int RIGPort; // Linked port for freq resporting
+ unsigned int PERMITTEDAPPLS; // Appls allowed on this port (generalisation of BBSBANNED)
+ char * CTEXT; // Port Specific CText
+ char Hide; // Hide from port display and AGW connect menu
+ TRANSPORTENTRY * Session; // For Response to KISS command
+ time_t LastKISSCmdTime;
+ time_t LastSmartIDTime; // For SmartID - ID only if packets sent recently
+ time_t SmartIDNeeded; // Time to send next smart ID
+ time_t SmartIDInterval; // Smart ID Interval (Secs)
+ int SendtoM0LTEMap;
+ uint64_t PortFreq; // Configured freq
+ char * M0LTEMapInfo;
+ int QtSMPort;
+ BOOL QtSMConnected;
+
+ int StatsPointer;
+ UCHAR * TX; // % Sending
+ UCHAR * BUSY; // % Active (Normally DCD active or TX)
+
+ int Hardware; // TNC H_TYPE. Copied here for access from application context
+
+
+} PORTCONTROLX, *PPORTCONTROL;
+
+typedef struct FULLPORTDATA
+{
+ struct PORTCONTROL PORTCONTROL;
+ UCHAR HARDWAREDATA[300]; // WORK AREA FOR HARDWARE DRIVERS
+} *PFULLPORTDATA;
+
+// KISS Mapping of HARDWAREDATA
+
+typedef struct KISSINFO
+{
+ struct PORTCONTROL PORT;
+
+ int LINKSTS; // CURRENT STATE
+ UINT * CURALP; // CURRENT BUFFER
+ UINT * NEXTCHR; //
+ void * ASYNCMSG_Q; // RECEIVED MESSAGES
+ void * KISSTX_Q ; // MESSAGES TO SEND
+ int ESCFLAG ; // ; SET IF LAST RX CHAR WAS DLE
+ int ESCTXCHAR; // ; CHAR TO SEND FOLLOWING DLE IF NZ
+
+ struct KISSINFO * FIRSTPORT; // ; FIRST PORT DEFINED FOR THIS IO ADDR
+ struct KISSINFO * SUBCHAIN; // ; NEXT SUBCHANNEL FOR SAME PHYSICAL PORT
+
+ int OURCTRL; // ; CONTROL BYTE FOR THIS PORT
+
+ int XCTRL; // CONTROL BYTE TO SEND
+ int REALKISSFLAGS; // ; KISS FLAGS FOR ACTIVE SUBPORT
+
+ USHORT TXCCC; // ; NETROM/BPQKISS CHECKSUMS
+ USHORT RXCCC; //
+
+ int TXACTIVE; // TIMER TO DETECT 'HUNG' SENDS
+
+ int POLLFLAG; // POLL OUTSTANDING FOR MULTIKISS
+
+ struct KISSINFO * POLLPOINTER; // LAST GROUP POLLED
+ int POLLED; // SET WHEN POLL RECEIVED
+
+ UCHAR * KISSCMD; // Commands to be sent when port opened
+ int KISSCMDLEN;
+
+ int PTTMode; // PTT Mode Flags
+ int PTTState; // Current State
+ uint64_t PTTActivemS; // For Stats
+ uint64_t PTTonTime; //
+
+ uint64_t BusyActivemS; // For channel busy stats
+ uint64_t BusyonTime;
+
+ char * QtSMModem;
+ int QtSMFreq;
+ int QtSMStats; // Set if stats received as KISS Command
+
+// UCHAR WIN32INFO[16]; // FOR WINDOWS DRIVER
+} *PKISSINFO;
+
+// EXT Driver Mapping of HARDWAREDATA
+
+
+typedef struct _EXTPORTDATA
+{
+ struct PORTCONTROL PORTCONTROL ; // REMAP HARDWARE INFO
+
+ void * (* PORT_EXT_ADDR) (int fn, int port, PDATAMESSAGE buff); // ADDR OF RESIDENT ROUTINE
+ char PORT_DLL_NAME[16];
+ UCHAR EXTRESTART; // FLAG FOR DRIVER REINIT
+ HINSTANCE DLLhandle;
+ int MAXHOSTMODESESSIONS; // Max Host Sessions supported (Used for KAM Pactor + ax.25 support)
+ struct _TRANSPORTENTRY * ATTACHEDSESSIONS[27]; // For PACTOR. etc
+ BOOL PERMITGATEWAY; // Set if ax.25 ports can change callsign (ie SCS, not KAM
+ int SCANCAPABILITIES; //Type of scan control Controller supports (None, Simple, Connect Lock)
+#define NONE 0
+#define SIMPLE 1
+#define CONLOCK 2
+
+ void * UI_Q; // Unproto Frames for Session Mode Drivers (TRK, etc)
+ int FramesQueued; // TX Frames queued in Driver
+
+} EXTPORTDATA, *PEXTPORTDATA;
+
+typedef struct _HDLCDATA
+{
+ struct PORTCONTROL PORTCONTROL ; // REMAP HARDWARE INFO
+//
+// Mapping of VXD fields (mainly to simplify debugging
+//
+
+ ULONG ASIOC; // A CHAN ADDRESSES
+ ULONG SIO; // OUR ADDRESSES (COULD BE A OR B)
+ ULONG SIOC;
+ ULONG BSIOC; // B CHAN CONTROL
+
+ struct _HDLCDATA * A_PTR; // PORT ENTRY FOR A CHAN
+ struct _HDLCDATA * B_PTR; // PORT ENTRY FOR B CHAN
+
+ VOID (FAR * VECTOR[4]) (); // INTERRUPT VECTORS
+
+// UINT * IOTXCA; // INTERRUPT VECTORS
+// UINT * IOTXEA;
+// UINT * IORXCA;
+// UINT * IORXEA;
+
+ UCHAR LINKSTS;
+
+ UINT * SDRNEXT;
+ UINT * SDRXCNT;
+ UINT * CURALP;
+ UCHAR OLOADS; // LOCAL COUNT OF BUFFERS SHORTAGES
+ USHORT FRAMELEN;
+ UINT * SDTNEXT; // POINTER to NEXT BYTE to TRANSMIT
+ USHORT SDTXCNT; // CHARS LEFT TO SEND
+ UCHAR RR0; // CURRENT RR0
+ UINT * TXFRAME; // ADDRESS OF FRAME BEING SENT
+
+ UCHAR SDFLAGS; // GENERAL FLAGS
+
+ void * PCTX_Q; // HDLC HOLDING QUEUE
+ void * RXMSG_Q; // RX INTERRUPT TO SDLC BG
+
+
+//;SOFTDCD DB 0 ; RX ACTIVE FLAG FOR 'SOFT DC
+ UCHAR TXDELAY; // TX KEYUP DELAY TIMER
+ UCHAR SLOTTIME; // TIME TO WAIT IF WE DONT SEND
+ UCHAR FIRSTCHAR; // CHAR TO SEND FOLLOWING TXDELAY
+ USHORT L1TIMEOUT; // UNABLE TO TX TIMEOUT
+ UCHAR PORTSLOTIMER;
+
+ USHORT TXBRG; // FOR CARDS WITHOUT /32 DIVIDER
+ USHORT RXBRG;
+
+ UCHAR WR10 ; // NRZ/NRZI FLAG
+
+ int IRQHand;
+ int fd; // file descriptor for LKM
+
+ ULONG IOLEN; // Number of bytes in IO Space
+
+ struct PORTCONTROL * DRIVERPORTTABLE; // ADDR OF PORT TABLE ENTRY IN VXD
+ // Used in NT Driver for Kernel Device Pointer
+
+}HDLCDATA, * PHDLCDATA;
+
+
+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;
+/*
+L4TABLE DD 0
+MAXCIRCUITS DW 50 ; NUMBER OF L4 CIRCUITS
+
+NUMBEROFPORTS DW 0
+
+TNCTABLE DD 0
+NUMBEROFSTREAMS DW 0
+
+ENDDESTLIST DD 0 ; NODE LIST+1
+*/
+
+typedef struct _LINKTABLE
+{
+//;
+//; LEVEL 2 LINK CONTROL TABLE
+//;
+
+ UCHAR LINKCALL[7]; // CALLSIGN OF STATION
+ UCHAR OURCALL[7]; // CALLSIGN OF OUR END
+ UCHAR DIGIS[56]; // LEVEL 2 DIGIS IN PATH
+
+ char callingCall[10]; // for reporting. Link and Our calls depand on which end connected
+ char receivingCall[10]; // for reporting. Link and Our calls depand on which end connected
+
+ char Direction[4]; // In or Out
+
+ PPORTCONTROL LINKPORT; // PORT NUMBER
+ UCHAR LINKTYPE; // 1 = UP, 2= DOWN, 3 = INTERNODE
+
+ UCHAR LINKNR;
+ UCHAR LINKNS; // LEV 2 SEQUENCE COUNTS
+ UCHAR LINKWS; // WINDOW START
+ UCHAR LINKOWS; // OLD (LAST ACKED) WINDOW START
+ UCHAR LINKWINDOW; // LEVEL 2 WINDOW SIZE
+
+ UCHAR L2FLAGS; // CONTROL BITS
+ UCHAR VER1FLAG; // SET IF OTHER END RUNNING VERSION 1
+
+ VOID * RX_Q; // PACKETS RECEIVED ON THIS LINK
+ VOID * TX_Q; // PACKETS TO SEND
+ VOID * FRAMES[8]; // FRAMES WAITING ACK
+ VOID * RXFRAMES[8]; // Frames received out of sequence
+
+ UCHAR L2STATE; // PROCESSING STATE
+ UCHAR Ver2point2; // Set if running 2.2
+ USHORT L2TIMER; // FRAME RETRY TIMER
+ UCHAR L2TIME; // RETRY TIMER INITIAL VALUE
+ USHORT L2SLOTIM; // DELAY FOR LINK VALIDATION POLL
+ UCHAR L2ACKREQ; // DELAYED TEXT ACK TIMER
+ UCHAR REJTIMER; // TO TIME OUT REJ IN VERSION 1
+ USHORT LAST_F_TIME; // TIME LAST R(F) SENT
+ UCHAR SDREJF; // FLAGS FOR FRMR
+ UCHAR SDRBYTE; // SAVED CONTROL BYTE FOR FRMR
+
+ UCHAR SDTSLOT ; // POINTER TO NEXT TXSLOT TO USE
+
+ UCHAR L2RETRIES; // RETRY COUNTER
+
+ UCHAR SESSACTIVE; // SET WHEN WE ARE SURE SESSION IS UP
+
+ UINT APPLMASK; // Used when XIR processed
+ VOID * ALIASPTR;
+
+ USHORT KILLTIMER; // TIME TO KILL IDLE LINK
+
+ VOID * CIRCUITPOINTER; // POINTER TO L4 CIRCUIT TABLE ENTRY
+ // (IF UP/DOWN)
+ PROUTE NEIGHBOUR; // POINTER TO NEIGHBOUR (IF CROSSLINK)
+
+ VOID * L2FRAG_Q; // DEFRAGMENTATION QUEUE
+
+ int IFrameRetryCounter; // Number of times an I frame in repeated without a frame being acked
+
+ time_t ConnectTime; // For session stats
+ int bytesRXed; // Info bytes only
+ int bytesTXed;
+
+ // Now support compressing L2 Sessions.
+ // We collect as much data as possible before compressing and re-packetizing
+
+ int AllowCompress;
+
+ unsigned char * unCompress; // Data being saved to uncompress
+ int unCompressLen;
+
+ int Sent;
+ int SentAfterCompression;
+
+ int Received;
+ int ReceivedAfterExpansion;
+
+
+} LINKTABLE;
+
+#pragma pack(1)
+
+struct myin_addr {
+ union {
+ struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+ struct { u_short s_w1,s_w2; } S_un_w;
+ uint32_t addr;
+ };
+};
+
+typedef struct _IPMSG
+{
+// FORMAT OF IP HEADER
+//
+// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT)
+
+ UCHAR VERLEN; // 4 BITS VERSION, 4 BITS LENGTH
+ UCHAR TOS; // TYPE OF SERVICE
+ USHORT IPLENGTH; // DATAGRAM LENGTH
+ USHORT IPID; // IDENTIFICATION
+ USHORT FRAGWORD; // 3 BITS FLAGS, 13 BITS OFFSET
+ UCHAR IPTTL;
+ UCHAR IPPROTOCOL; // HIGHER LEVEL PROTOCOL
+ USHORT IPCHECKSUM; // HEADER CHECKSUM
+ struct myin_addr IPSOURCE;
+ struct myin_addr IPDEST;
+
+ UCHAR Data;
+
+} IPMSG, *PIPMSG;
+
+typedef struct _PSEUDOHEADER
+{
+ struct myin_addr IPSOURCE;
+ struct myin_addr IPDEST;
+ UCHAR Reserved;
+ UCHAR IPPROTOCOL; // HIGHER LEVEL PROTUDP/TCP Length
+ USHORT LENGTH; // DATAGRAM LENGTH
+
+} PHEADER;
+
+typedef struct _TCPMSG
+{
+
+// FORMAT OF TCP HEADER WITHIN AN IP DATAGRAM
+
+// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT)
+
+ USHORT SOURCEPORT;
+ USHORT DESTPORT;
+
+ uint32_t SEQNUM;
+ uint32_t ACKNUM;
+
+ UCHAR TCPCONTROL; // 4 BITS DATA OFFSET 4 RESERVED
+ UCHAR TCPFLAGS; // (2 RESERVED) URG ACK PSH RST SYN FIN
+
+ USHORT WINDOW;
+ USHORT CHECKSUM;
+ USHORT URGPTR;
+
+
+} TCPMSG, *PTCPMSG;
+
+typedef struct _UDPMSG
+{
+
+// FORMAT OF UDP HEADER WITHIN AN IP DATAGRAM
+
+// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT)
+
+ USHORT SOURCEPORT;
+ USHORT DESTPORT;
+ USHORT LENGTH;
+ USHORT CHECKSUM;
+ UCHAR UDPData[0];
+
+} UDPMSG, *PUDPMSG;
+
+// ICMP MESSAGE STRUCTURE
+
+typedef struct _ICMPMSG
+{
+ // FORMAT OF ICMP HEADER WITHIN AN IP DATAGRAM
+
+ // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT)
+
+ UCHAR ICMPTYPE;
+ UCHAR ICMPCODE;
+ USHORT ICMPCHECKSUM;
+
+ USHORT ICMPID;
+ USHORT ICMPSEQUENCE;
+ UCHAR ICMPData[0];
+
+} ICMPMSG, *PICMPMSG;
+
+#pragma pack()
+
+struct SEM
+{
+ UINT Flag;
+ int Clashes;
+ int Gets;
+ int Rels;
+ DWORD SemProcessID;
+ DWORD SemThreadID;
+ int Line; // caller file and line
+ char File[MAX_PATH];
+};
+
+
+#define TNCBUFFLEN 8192
+#define MAXSTREAMS 32
+
+// DED Emulator Stream Info
+
+struct StreamInfo
+{
+ UCHAR * Chan_TXQ; // !! Leave at front so ASM Code Finds it
+ // FRAMES QUEUED TO NODE
+ int BPQStream;
+ BOOL Connected; // Set if connected to Node
+ int CloseTimer; // Used to close session after connect failure
+ UCHAR MYCall[30];
+ char OutgoingCall[16];
+};
+
+struct TNC2StreamInfo
+{
+ // Not sure which of these is session specific
+
+ UCHAR VMSR; // VIRTUAL MSR (Only Connected Bit)
+ int BPQPort;
+ BOOL MODEFLAG; // COMMAND/DATA MODE
+ int TPACLEN; // MAX PACKET SIZE FOR TNC GENERATED PACKETS
+ char RemoteCall[10]; // FOr Stream Changed Message
+
+/* I suspect only the above are needed
+
+ int TRANSTIMER; // TRANPARENT MODE SEND TIMOUT
+ BOOL AUTOSENDFLAG; // SET WHEN TRANSMODE TIME EXPIRES
+
+ int CMDTMR; // TRANSARENT MODE ESCAPE TIMER
+ int COMCOUNT; // NUMBER OF COMMAND CHARS RECEIVED
+ int CMDTIME ; // GUARD TIME FOR TRANS MODE EACAPE
+ int CMSG; // Enable CTEXT flag
+ int COMCHAR; // CHAR TO LEAVE CONV MODE
+ int PASSCHAR; // Escape char
+ char CTEXT[120];
+ char ECHOFLAG; // ECHO ENABLED
+ BOOL TRACEFLAG; // MONITOR ON/OFF
+ BOOL FLOWFLAG; // FLOW OFF/ON
+
+ BOOL CONOK;
+ BOOL CBELL;
+ BOOL NOMODE; // MODE CHANGE FLAGS
+ BOOL NEWMODE;
+ BOOL CONMODEFLAG; // CONNECT MODE - CONV OR TRANS
+ BOOL LFIGNORE;
+ BOOL MCON; // TRACE MODE FLAGS
+ BOOL MCOM;
+ BOOL MALL;
+ BOOL AUTOLF; // Add LF after CR
+ BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS
+ BOOL MTX; // MONITOR TRANSMITTED FRAMES
+ BOOL MTXFORCE; // MONITOR TRANSMITTED FRAMES EVEN IF M OFF
+ UINT MMASK; // MONITOR PORT MASK
+ BOOL HEADERLN; // PUT MONITORED DATA ON NEW LINE FLAG
+ BOOL InEscape; // PASS Char received (treat next char as normal char not ctrl char)
+
+ UINT APPLICATION; // APPLMASK
+ UINT APPLFLAGS; // FLAGS TO CONTROL APPL SYSTEM
+
+ UINT SENDPAC; // SEND PACKET CHAR
+ BOOL CPACTIME; // USE PACTIME IN CONV MODE
+ BOOL CRFLAG ; // APPEND SENDPAC FLAG
+
+ int TPACLEN ; // MAX PACKET SIZE FOR TNC GENERATED PACKETS
+ UCHAR UNPROTO[64]; // UNPROTO DEST AND DIGI STRING
+
+ char MYCALL[10];
+*/
+
+};
+
+
+struct TNCDATA
+{
+ struct TNCDATA * Next;
+ unsigned int Mode; // 0 = TNC2, others may follow
+
+ UCHAR TOUSERBUFFER[TNCBUFFLEN]; // BUFFER TO USER
+ UCHAR TONODEBUFFER[300]; // BUFFER TO NODE
+ UCHAR FROMUSERBUFFER[TNCBUFFLEN];
+
+ char PORTNAME[80]; // for Linux Port Names
+ int ComPort;
+ BOOL VCOM;
+ int RTS;
+ int CTS;
+ int DCD;
+ int DTR;
+ int DSR;
+ int BPQPort;
+ int Speed;
+ char PortLabel[20];
+ char TypeFlag[2];
+ BOOL PortEnabled;
+ HANDLE hDevice;
+ BOOL NewVCOM; // Set if User Mode VCOM Port
+
+ UCHAR VMSR; // VIRTUAL MSR
+
+// BIT 7 - Receive Line Signal Detect (DCD)
+// 6 - Ring Indicator
+// 5 - Data Set Ready
+// 4 - Clear To Send
+// 3 - Delta RLSD ( ie state has changed since last
+// access)
+// 2 - Trailing Edge Ring Detect
+// 1 - Delta DSR
+// 0 - Delta CTS
+
+
+ BOOL RTSFLAG; // BIT 0 SET IF RTS/DTR UP
+ UCHAR VLSR; // LAST RECEIVED LSR VALUE
+
+ int RXCOUNT; // BYTES IN RX BUFFER
+ UCHAR * PUTPTR; // POINTER FOR LOADING BUFFER
+ UCHAR * GETPTR; // POINTER FOR UNLOADING BUFFER
+ UCHAR * CURSOR; // POSTION IN KEYBOARD BUFFER
+
+ int MSGLEN;
+ int TRANSTIMER; // TRANPARENT MODE SEND TIMOUT
+ BOOL AUTOSENDFLAG; // SET WHEN TRANSMODE TIME EXPIRES
+
+ int CMDTMR; // TRANSARENT MODE ESCAPE TIMER
+ int COMCOUNT; // NUMBER OF COMMAND CHARS RECEIVED
+ int CMDTIME ; // GUARD TIME FOR TRANS MODE EACAPE
+ int CMSG; // Enable CTEXT flag
+ int COMCHAR; // CHAR TO LEAVE CONV MODE
+ int PASSCHAR; // Escape char
+ int StreamSW; // Stream Switch Char
+ int StreamDbl;
+ int StreamCall; // Send call with stream switch char
+ int LCStream; // Stream is case independant
+ int Users; // Number of streams allowed
+
+ char CTEXT[120];
+ char ECHOFLAG; // ECHO ENABLED
+ BOOL TRACEFLAG; // MONITOR ON/OFF
+ BOOL FLOWFLAG; // FLOW OFF/ON
+
+ BOOL CONOK;
+ BOOL CBELL;
+ BOOL NOMODE; // MODE CHANGE FLAGS
+ BOOL NEWMODE;
+ BOOL CONMODEFLAG; // CONNECT MODE - CONV OR TRANS
+ BOOL LFIGNORE;
+ BOOL MCON; // TRACE MODE FLAGS
+ BOOL MCOM;
+ BOOL MALL;
+ BOOL MUIONLY;
+ BOOL AUTOLF; // Add LF after CR
+ BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS
+ BOOL MTX; // MONITOR TRANSMITTED FRAMES
+ BOOL MTXFORCE; // MONITOR TRANSMITTED FRAMES EVEN IF M OFF
+ uint64_t MMASK; // MONITOR PORT MASK
+ BOOL HEADERLN; // PUT MONITORED DATA ON NEW LINE FLAG
+ BOOL InEscape; // PASS Char received (treat next char as normal char not ctrl char)
+ BOOL InStreamSW; // StreamSW Char received (treat next char as new stream)
+
+// BOOL MODEFLAG; // TNC2 COMMAND/DATA MODE
+
+ UINT APPLICATION; // APPLMASK
+
+ UINT APPLFLAGS; // FLAGS TO CONTROL APPL SYSTEM
+
+ UINT SENDPAC; // SEND PACKET CHAR
+ BOOL CPACTIME; // USE PACTIME IN CONV MODE
+ BOOL CRFLAG ; // APPEND SENDPAC FLAG
+
+ int TPACLEN ; // MAX PACKET SIZE FOR TNC GENERATED PACKETS
+ UCHAR UNPROTO[64]; // UNPROTO DEST AND DIGI STRING
+
+ char MYCALL[10];
+
+ // TNC2 Stream Fields
+
+ int TXStream; // Currently Selected Stream
+ int RXStream;
+
+ struct TNC2StreamInfo * TNC2Stream[26]; // For StreamSW support
+
+ // DED Mode Fields
+
+ int PollDelay; // Used by VCOM to slow down continuous reads on real port
+
+ struct StreamInfo * Channels[MAXSTREAMS+1];
+ char MODE; // INITIALLY TERMINAL MODE
+ char HOSTSTATE; // HOST STATE MACHINE
+ int MSGCOUNT; // LENGTH OF MESSAGE EXPECTED
+ int MSGLENGTH;
+ char MSGTYPE;
+ unsigned char MSGCHANNEL;
+ char DEDMODE; // CLUSTER MODE - DONT ALLOW DUP CONNECTS
+ int HOSTSTREAMS; // Default Streams
+
+ UCHAR DEDTXBUFFER[256];
+ UCHAR * DEDCURSOR;
+
+ unsigned char MONBUFFER[258]; //="\x6";
+ int MONLENGTH;
+ int MONFLAG;
+
+ time_t LastDEDPollTime; // To detect lost host program
+
+ // Kantronics Fields
+
+ int RXBPtr;
+ char nextMode; // Mode after RESET
+
+ // SCS Fields
+
+ int FROMUSERLEN;
+
+ BOOL Term4Mode; // Used by Airmail
+ BOOL PACMode; // SCS in Packet Mode
+ BOOL Toggle; // SCS Sequence Toggle
+
+ char MyCall[10];
+
+};
+
+// Emulatiom mode equates
+
+#define TNC2 0
+#define DED 1
+#define KANTRONICS 2 // For future use
+#define SCS 3
+
+
+#define MAX_ENTRIES 128
+#define MaxMHEntries 100
+#define MAX_BROADCASTS 8
+#define MAXUDPPORTS 30
+
+#ifndef MAXGETHOSTSTRUCT
+#define MAXGETHOSTSTRUCT 1024
+#endif
+
+
+struct arp_table_entry
+{
+ unsigned char callsign[7];
+ unsigned char len; // bytes to compare (6 or 7)
+ BOOL IPv6;
+
+// union
+// {
+// struct in_addr in_addr;
+// unsigned int ipaddr;
+// struct in6_addr in6_addr;
+// };
+
+ unsigned short port;
+ unsigned char hostname[64];
+ unsigned int error;
+ BOOL ResolveFlag; // True if need to resolve name
+ unsigned int keepalive;
+ unsigned int keepaliveinit;
+ BOOL BCFlag; // True if we want broadcasts to got to this call
+ BOOL AutoAdded; // Set if Entry created as a result of AUTOADDMAP
+ SOCKET TCPListenSock; // Listening socket if slave
+ SOCKET TCPSock;
+ int TCPMode; // TCPMaster ot TCPSlave
+ UCHAR * TCPBuffer; // Area for building TCP message from byte stream
+ int InputLen; // Bytes in TCPBuffer
+
+ union
+ {
+ struct sockaddr_in6 destaddr6;
+ struct sockaddr_in destaddr;
+ };
+
+ BOOL TCPState;
+ pthread_t TCPThreadID; // Thread ID if TCP Master
+ UINT TCPOK; // Cleared when Message RXed . Incremented by timer
+ int SourcePort; // Used to select socket, hence from port.
+// SOCKET SourceSocket;
+ struct AXIPPORTINFO * PORT;
+ BOOL noUpdate; // Don't update dest address from incoming packet
+ BOOL replytoSourcePort; // Update map entry dest port from source port of each packet.
+ time_t LastHeard; // Last Packet received from this ststiom
+};
+
+
+struct broadcast_table_entry
+{
+ unsigned char callsign[7];
+ unsigned char len; // bytes to compare (6 or 7)
+};
+
+
+struct MHTableEntry
+{
+ unsigned char callsign[7];
+ char proto;
+ short port;
+ union
+ {
+ struct in_addr ipaddr;
+ struct in6_addr ipaddr6;
+ };
+ time_t LastHeard; // Time last packet received
+ int Keepalive;
+ BOOL IPv6;
+};
+
+
+struct AXIPPORTINFO
+{
+ int Port;
+
+ struct MHTableEntry MHTable[MaxMHEntries];
+ struct broadcast_table_entry BroadcastAddresses[MAX_BROADCASTS];
+
+ int NumberofBroadcastAddreses;
+ BOOL Checkifcanreply;
+
+ int arp_table_len;
+ int ResolveIndex; // pointer to entry being resolved
+
+ struct arp_table_entry arp_table[MAX_ENTRIES];
+
+ struct arp_table_entry default_arp;
+
+ BOOL MHEnabled;
+ BOOL MHAvailable; // Enabled with config file directive
+
+ BOOL AutoAddARP;
+ BOOL AutoAddBC; // Broadcast flag for autoaddmap
+
+ unsigned char hostaddr[64];
+
+ HWND hResWnd, hMHWnd, ConfigWnd;
+
+ BOOL GotMsg;
+
+ int udpport[MAXUDPPORTS+2];
+ BOOL IPv6[MAXUDPPORTS+2];
+
+ BOOL PortIPv6; // Set if any MAPS for IPv6
+
+ int NumberofUDPPorts;
+
+ BOOL needip;
+ BOOL NeedResolver;
+ BOOL NeedTCP;
+
+ SOCKET sock; // IP 93 Sock
+ SOCKET udpsock[MAXUDPPORTS+2];
+
+ time_t ltime,lasttime;
+ int baseline;
+ int mhbaseline;
+ int CurrentMHEntries;
+
+ char buf[MAXGETHOSTSTRUCT];
+
+ int MaxMHWindowlength;
+ int MaxResWindowlength;
+
+ BOOL ResMinimized;
+ BOOL MHMinimized;
+
+ HMENU hResMenu;
+ HMENU hMHMenu;
+
+ pthread_t ResolveNamesThreadId;
+
+};
+
+
+#define Disconnect(stream) SessionControl(stream,2,0)
+#define Connect(stream) SessionControl(stream,1,0)
+
+#endif
+
+
+
diff --git a/.svn/pristine/3a/3a85a28efa92a9f216ca7b20c078d512b27c7651.svn-base b/.svn/pristine/3a/3a85a28efa92a9f216ca7b20c078d512b27c7651.svn-base
new file mode 100644
index 0000000..181a6ab
--- /dev/null
+++ b/.svn/pristine/3a/3a85a28efa92a9f216ca7b20c078d512b27c7651.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,75
+#define KVerstring "6.0.24.75\0"
+
+
+#ifdef CKernel
+
+#define Vers KVers
+#define Verstring KVerstring
+#define Datestring "June 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/3b/3bea880be9dd27b9f65d60e4740c6fdcd97b0f64.svn-base b/.svn/pristine/3b/3bea880be9dd27b9f65d60e4740c6fdcd97b0f64.svn-base
new file mode 100644
index 0000000..2aab885
--- /dev/null
+++ b/.svn/pristine/3b/3bea880be9dd27b9f65d60e4740c6fdcd97b0f64.svn-base
@@ -0,0 +1,3743 @@
+// Mail and Chat Server for BPQ32 Packet Switch
+//
+//
+
+// Version 1.0.0.17re
+
+// Split Messasge, User and BBS Editing from Main Config.
+// Add word wrap to Console input and output
+// Flash Console on chat user connect
+// Fix processing Name response in chat mode
+// Fix processing of *RTL from station not defined as a Chat Node
+// Fix overlength lines ln List responses
+// Housekeeping expires BIDs
+// Killing a message removes it from the forwarding counts
+
+// Version 1.0.0.18
+
+// Save User Database when name is entered or updated so it is not lost on a crash
+// Fix Protocol Error in Compressed Forwarding when switching direction
+// Add Housekeeping results dialog.
+
+// Version 1.0.0.19
+
+// Allow PACLEN in forward scripts.
+// Store and forward messages with CRLF as line ends
+// Send Disconnect after FQ ( for LinFBB)
+// "Last Listed" is saved if MailChat is closed without closing Console
+// Maximum acceptable message length can be specified (in Forwarding Config)
+
+// Version 1.0.0.20
+
+// Fix error in saving forwarding config (introduced in .19)
+// Limit size of FBB forwarding block.
+// Clear old connection (instead of new) if duplicate connect on Chat Node-Node link
+// Send FA for Compressed Mail (was sending FB for both Compressed and Uncompressed)
+
+// Version 1.0.0.21
+
+// Fix Connect Script Processing (wasn't waiting for CONNECTED from last step)
+// Implement Defer
+// Fix MBL-style forwarding
+// Fix Add User (Params were not saved)
+// Add SC (Send Copy) Command
+// Accept call@bbs as well as call @ bbs
+
+// Version 1.0.0.22
+
+// Implement RB RP LN LR LF LN L$ Commands.
+// Implement QTH and ZIP Commands.
+// Entering an empty Title cancels the message.
+// Uses HomeBBS field to set @ field for local users.
+// Creates basic WP Database.
+// Uses WP to lookup @ field for non-local calls.
+// Console "Actions" Menu renamed "Options".
+// Excluded flag is actioned.
+// Asks user to set HomeBBS if not already set.
+// Fix "Shrinking Message" problem, where message got shorter each time it was read Initroduced in .19).
+// Flash Server window when anyone connects to chat (If Console Option "Flash on Chat User Connect" set).
+
+// Version 1.0.0.23
+
+// Fix R: line scan bug
+
+// Version 1.0.0.24
+
+// Fix closing console window on 'B'.
+// Fix Message Creation time.
+// Enable Delete function in WP edit dialog
+
+// Version 1.0.0.25
+
+// Implement K< and K> commands
+// Experimental support for B1 and B2 forwarding
+// Experimental UI System
+// Fix extracting QTH from WP updates
+
+// Version 1.0.0.26
+
+// Add YN etc responses for FBB B1/B2
+
+// Version 1.0.0.27
+
+// Fix crash if NULL received as start of a packet.
+// Add Save WP command
+// Make B2 flag BBS-specific.
+// Implement B2 Send
+
+// Version 1.0.0.28
+
+// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net
+// Flag messages as Held if smtp server rejects from or to addresses
+// Fix Kill to (K> Call)
+// Edit Message dialog shows latest first
+// Add chat debug window to try to track down occasional chat connection problems
+
+// Version 1.0.0.29
+
+// Add loads of try/excspt
+
+// Version 1.0.0.30
+
+// Writes Debug output to LOG_DEBUG_X and Monitor Window
+
+// Version 1.0.0.32
+
+// Allow use of GoogleMail for ISP functions
+// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc
+// Email Housekeeping Results to SYSOP
+
+// Version 1.0.0.33
+
+// Housekeeping now runs at Maintenance Time. Maintenance Interval removed.
+// Allow multiple numbers on R and K commands
+// Fix L command with single number
+// Log if Forward count is out of step with messages to forward.
+// UI Processing improved and F< command implemented
+
+// Version 1.0.0.34
+
+// Semaphore Chat Messages
+// Display Semaphore Clashes
+// More Program Error Traps
+// Kill Messages more than BIDLifetime old
+
+// Version 1.0.0.35
+
+// Test for Mike - Remove B1 check from Parse_SID
+
+// Version 1.0.0.36
+
+// Fix calculation of Housekeeping Time.
+// Set dialog box background explicitly.
+// Remove tray entry for chat debug window.
+// Add date to log file name.
+// Add Actions Menu option to disable logging.
+// Fix size of main window when it changes between versions.
+
+// Version 1.0.0.37
+
+// Implement Paging.
+// Fix L< command (was giving no messages).
+// Implement LR LR mmm-nnn LR nnn- (and L nnn-)
+// KM should no longer kill SYSOP bulls.
+// ISP interfaces allows SMTP Auth to be configured
+// SMTP Client would fail to send any more messages if a connection failed
+
+// Version 1.0.0.38
+
+// Don't include killed messages in L commands (except LK!)
+// Implement l@
+// Add forwarding timebands
+// Allow resizing of main window.
+// Add Ver command.
+
+// Version 1.0.1.1
+
+// First Public Beta
+
+// Fix part line handling in Console
+// Maintenance deletes old log files.
+// Add option to delete files to the recycle bin.
+
+// Version 1.0.2.1
+
+// Allow all Node SYSOP commands in connect scripts.
+// Implement FBB B1 Protocol with Resume
+// Make FBB Max Block size settable for each BBS.
+// Add extra logging when Chat Sessions refused.
+// Fix Crash on invalid housekeeping override.
+// Add Hold Messages option.
+// Trap CRT Errors
+// Sort Actions/Start Forwarding List
+
+// Version 1.0.2.2
+
+// Fill in gaps in BBS Number sequence
+// Fix PE if ctext contains }
+// Run Houskeeping at startup if previous Housekeeping was missed
+
+// Version 1.0.2.3
+
+// Add configured nodes to /p listing
+
+// Version 1.0.2.4
+
+// Fix RMS (it wanted B2 not B12)
+// Send messages if available after rejecting all proposals
+// Dont try to send msg back to originator.
+
+// Version 1.0.2.5
+
+// Fix timeband processing when none specified.
+// Improved Chat Help display.
+// Add helpful responses to /n /q and /t
+
+// Version 1.0.2.6
+
+// Kill Personal WP messages after processing
+// Make sure a node doesnt try to "join" or "leave" a node as a user.
+// More tracing to try to track down lost topic links.
+// Add command recall to Console
+// Show users in new topic when changing topic
+// Add Send From Clipboard" Action
+
+// Version 1.0.2.7
+
+// Hold messages from the future, or with invalid dates.
+// Add KH (kill held) command.
+// Send Message to SYSOP when a new user connects.
+
+// Version 1.0.2.8
+
+// Don't reject personal message on Dup BID unless we already have an unforwarded copy.
+// Hold Looping messages.
+// Warn SYSOP of held messages.
+
+// Version 1.0.2.9
+
+// Close connecton on receipt of *** DONE (MBL style forwarding).
+// Improved validation in link_drop (Chat Node)
+// Change to welcome prompt and Msg Header for Outpost.
+// Fix Connect Script processing for KA Nodes
+
+// Version 1.0.3.1
+
+// Fix incorrect sending of NO - BID.
+// Fix problems caused by a user being connected to more than one chat node.
+// Show idle time on Chat /u display.
+// Rewrite forwarding by HA.
+// Add "Bad Words" Test.
+// Add reason for holding to SYSOP "Message Held" Message.
+// Make topics case-insensitive.
+// Allow SR for smtp mail.
+// Try to fix some user's "Add User" problem.
+
+
+// Version 1.0.3.2
+
+// Fix program error when prcessing - response in FBB forwarding.
+// Fix code to flag messages as sent.
+
+
+// Version 1.0.3.3
+
+// Attempt to fix message loop on topic_change
+// Fix loop if compressed size is greater than 32K when receiving with B1 protocol.
+// Fix selection of B1
+
+// Version 1.0.3.4
+
+// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above)
+// Add Basic NNTP Interface
+// Fix possible loop in lzhuf encode
+
+// Version 1.0.3.5
+
+// Fix forwarding of Held Messages
+// More attempts to fix Chat crashes.
+// Limit join/leave problem with mismatched nodes.
+// Add Chat Node Monitoring System.
+// Change order of elements in nntp addresses (now to.at, was at.to)
+
+// Version 1.0.3.6
+
+// Restart and Exit if too many errors
+// Fix forwarding of killed messages.
+// Fix Forwarding to PaKet.
+// Fix problem if BBS signon contains words from the "Fail" list
+
+// Version 1.0.3.7
+
+// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4
+// Add last message to edit users
+// Change Console and Monitor Buffer sizes
+// Don't flag msg as 'Y' on read if it was Held or Killed
+
+// Version 1.0.3.8
+
+// Don't connect if all messages for a BBS are held.
+// Hold message if From or To are missing.
+// Fix parsing of /n and /q commands
+// fix possible loop on changing name or qth
+
+// Version 1.0.3.9
+
+// More Chat fixes and monitoring
+// Added additional console for chat
+
+// Version 1.0.3.10
+
+// Fix for corruption of CIrcuit-Node chain.
+
+// Version 1.0.3.11
+
+// Fix flow control for SMTP and NNTP
+
+// Version 1.0.3.12
+
+// Fix crash in SendChatStatus if no Chat Links Defined.
+// Disable Chat Mode if there is no ApplCall for ChatApplNum,
+// Add Edit Message to Manage Messages Dialog
+// NNTP needs authentication
+
+
+// Version 1.0.3.13
+
+// Fix Chat ApplCall warning when ChatAppl = 0
+// Add NNTP NEWGROUPS Command
+// Fix MBL Forwarding (remove extra > prompt after SP)
+
+// Version 1.0.3.14
+
+// Fix topic switch code.
+// Send SYSOP messages on POP3 interface if User SYSOP flag is set.
+// NNTP only needs Authentication for posting, not reading.
+
+// Version 1.0.3.15
+
+// Fix reset of First to Forward after househeeping
+
+// Version 1.0.3.16
+
+// Fix check of HA for terminating WW
+// MBL Mode remove extra > prompts
+// Fix program error if WP record has unexpected format
+// Connect Script changes for WINMOR
+// Fix typo in unconfigured node has connected message
+
+// Version 1.0.3.17
+
+// Fix forwarding of Personals
+
+// Version 1.0.3.18
+
+// Fix detection of misconfigured nodes to work with new nodes.
+// Limit connection attempt rate when a chat node is unavailable.
+// Fix Program Error on long input lines (> ~250 chars).
+
+// Version 1.0.3.19
+
+// Fix Restart of B2 mode transfers.
+// Fix error if other end offers B1 and you are configured for B2 only.
+
+
+// Version 1.0.3.20
+
+// Fix Paging in Chat Mode.
+// Report Node Versions.
+
+// Version 1.0.3.21
+
+// Check node is not already known when processing OK
+// Add option to suppress emailing of housekeeping results
+
+// Version 1.0.3.22
+
+// Correct Version processing when user connects via the network
+// Add time controlled forwarding scripts
+
+// Version 1.0.3.23
+
+// Changes to RMS forwarding
+
+// Version 1.0.3.24
+
+// Fix RMS: from SMTP interface
+// Accept RMS/ instead of RMS: for Thunderbird
+
+// Version 1.0.3.25
+
+// Accept smtp: addresses from smtp client, and route to ISP gateway.
+// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS.
+
+// Version 1.0.3.26
+
+// Improve display of rms and smtp messages in message lists and message display.
+
+// Version 1.0.3.27
+
+// Correct code that prevents mail being retured to originating BBS.
+// Tidy stuck Nodes and Topics when all links close
+// Fix B2 handling of @ to TO Address.
+
+// Version 1.0.3.28
+
+// Ensure user Record for the BBS Call has BBS bit set.
+// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set.
+// Add user configurable welcome messages.
+
+// Version 1.0.3.29
+
+// Add AUTH feature to Rig Control
+
+// Version 1.0.3.30
+
+// Process Paclink Header (;FW:)
+
+// Version 1.0.3.31
+
+// Process Messages with attachments.
+// Add inactivity timeout to Chat Console sessions.
+
+// Version 1.0.3.32
+
+// Fix for Paclink > BBS Addresses
+
+// Version 1.0.3.33
+
+// Fix multiple transfers per session for B2.
+// Kill messages eent to paclink.
+// Add option to forward messages on arrival.
+
+// Version 1.0.3.34
+
+// Fix bbs addresses to winlink.
+// Fix adding @winlink.org to imcoming paclink msgs
+
+// Version 1.0.3.35
+
+// Fix bbs addresses to winlink. (Again)
+
+// Version 1.0.3.36
+
+// Restart changes for RMS/paclink
+
+// Version 1.0.3.37
+
+// Fix for RMS Express forwarding
+
+// Version 1.0.3.38
+
+// Fixes for smtp and lower case packet addresses from Airmail
+// Fix missing > afer NO - Bid in MBL mode
+
+// Version 1.0.3.39
+
+// Use ;FW: for RMS polling.
+
+// Version 1.0.3.40
+
+// Add ELSE Option to connect scripts.
+
+// Version 1.0.3.41
+
+// Improved handling of Multiple Addresses
+// Add user colours to chat.
+
+// Version 1.0.3.42
+
+// Poll multiple SSID's for RMS
+// Colour support for BPQTEerminal
+// New /C chat command to toggle colour on or off.
+
+// Version 1.0.3.43
+
+// Add SKIPPROMPT command to forward scripts
+
+// Version 1.0.4.1
+
+// Non - Beta Release
+// Fix possible crash/corruption with long B2 messages
+
+// Version 1.0.4.2
+
+// Add @winlink.org to the B2 From addresss if it is just a callsign
+// Route Flood Bulls on TO as well as @
+
+// Version 1.0.4.3
+
+// Handle Packet Addresses from RMS Express
+// Fix for Housekeeping B$ messages
+
+// Version 1.0.4.4
+
+// Remove B2 header and all but the Body part from messages forwared using MBL
+// Fix handling of ;FW: from RMS Express
+
+// Version 1.0.4.5
+
+// Disable Paging on forwarding sessions.
+// Kill Msgs sent to RMS Exxpress
+// Add Name to Chat *** Joined msg
+
+// Version 1.0.4.6
+
+// Pass smtp:winlink.org messages from Airmail to local user check
+// Only apply local user check to RMS: messages @winlink.org
+// Check locally input smtp: messages for local winlink.org users
+// Provide facility to allow only one connect on a port
+
+// Version 1.0.4.8
+
+// Only reset last listed on L or LR commands.
+
+// Version 1.0.4.9
+
+// Fix error in handling smtp: messages to winlink.org addresses from Airmail
+
+// Version 1.0.4.10
+
+// Fix Badwords processing
+// Add Connect Script PAUSE command
+
+// Version 1.0.4.11
+
+// Suppress display and listing of held messages
+// Add option to exclude SYSOP messages from LM, KM, etc
+// Fix crash whan receiving messages with long lines via plain text forwarding
+
+// Version 1.0.4.12 Jul 2010
+
+// Route P messages on AT
+// Allow Applications above 8
+
+// Version 1.0.4.13 Aug 2010
+
+// Fix TidyString for addresses of form John Wiseman
+// Add Try/Except around socket routines
+
+// Version 1.0.4.14 Aug 2010
+
+// Trap "Error - TNC Not Ready" in forward script response
+// Fix restart after program error
+// Add INFO command
+// Add SYSOP-configurable HELP Text.
+
+// Version 1.0.4.15 Aug 2010
+
+// Semaphore Connect/Disconnect
+// Semaphore RemoveTempBIDS
+
+// Version 1.0.4.16 Aug 2010
+
+// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS)
+
+// Version 1.0.4.17 Aug 2010
+
+// Fix receiving multiple messages in FBB Uncompressed Mode
+// Try to trap phantom chat node connections
+// Add delay to close
+
+
+// Version 1.0.4.18 Aug 2010
+
+// Add "Send SYSTEM messages to SYSOP Call" Option
+// set fwd bit on local winlink.org msgs if user is a BBS
+// add winlink.org to from address of messages from WL2K that don't already have an @
+
+// Version 1.0.4.19 Sept 2010
+
+// Build a B2 From: address if possible, so RMS Express can reply to packet messages.
+// Fix handling of addresses from WL2K with SSID's
+// L@ now only matches up to length of input string.
+// Remove "Type H for help" from login prompt.
+
+// Version 1.0.4.20 Sept 2010
+
+// Process FBB 'E' response
+// Handle FROM addresses with an @BBS
+// Fix FROM addresses with @ on end.
+// Extend delay before close after sending FQ on winmor/pactor sessions.
+
+// Version 1.0.4.21 Sept 2010
+
+// Fix handling B2 From: with an HA
+// Add "Expert User" welcome message.
+
+// Version 1.0.4.22 Sept 2010
+
+// Version 1.0.4.23 Oct 2010
+
+// Add Dup message supression
+// Dont change B2 from if going to RMS
+
+// Version 1.0.4.24 Oct 2010
+
+// Add "Save Registry Config" command
+// Add forwarding on wildcarded TO for NTS
+// Add option to force text mode forwarding
+// Define new users as a temporaty BBS if SID received in reply to Name prompt
+// Reduce delay before sending close after sending FQ on pactor sessions
+// Fix processing of MIME boundary from GMail
+
+// Send /ex instead of ctrl/z for text mode forwarding
+// Send [WL2K-BPQ... SID if user flagged as RMS Express
+// Fix Chat Map reporting when more than one AXIP port
+// Add Message State D for NTS Messages
+// Forward messages in priority order - T, P, B
+// Add Reject and Hold Filters
+// Fix holding messages to local RMS users when received as part of a multiple addressee message
+
+// Version 1.0.4.25 Nov 2010
+
+// Renumbered for release
+// Add option to save Registry Config during Housekeeping
+
+// Version 1.0.4.26 Nov 2010
+
+// Fix F> loop when doing MBL forwarding between BPQ BBSes
+// Allow multiple To: addresses, separated by ;
+// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages.
+// Set Unforwarded Bulls to status '$'
+// Accept MARS and USA as continent codes for MARS Packet Addresses
+// Add option to send Non-delivery notifications.
+
+// Version 1.0.4.27 Dec 2010
+
+// Add MSGTYPES fwd file option
+
+// Version 1.0.4.28 Dec 2010
+
+// Renumbered to for release
+
+// Version 1.0.4.30 Dec 2010
+
+// Fix rescan requeuing where bull was rejected by a BBS
+// Fiz flagging bulls received by NNTP with $ if they need to be forwarded.
+// Add Chat Keepalive option.
+// Fix bug in non-delivery notification.
+
+// Version 1.0.4.32 Jan 2011
+
+// Allow "Send from Clipboard" to send to rms: or smtp:
+// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS)
+// Fix corruption of messages converted to B2 if body contains binary data
+// Fix occasional program error when forwarding B2 messages
+// Limit FBB protocol data blocks to 250 to try to fix restart problem.
+// Add F2 to F5 to open windows.
+
+// Version 1.0.4.33 Jan 2011
+
+// Fix holding old bulls with forwarding info.
+
+// Version 1.0.4.33 Jan 2011
+
+// Prevent transfer restarting after a program error.
+// Allow Housekeeping to kill held messages.
+
+// Version 1.0.4.35 Jan 2011
+
+// Add Size limits for P and T messages to MSGTYPES command
+// Fix Error in MBL processing when blank lines received (introduced in .33)
+// Trap possible PE in Send_MON_Datagram
+// Don't use paging on chat sessions
+
+// Version 1.0.4.36 Jan 2011
+
+// Fix error after handling first FBB block.
+// Add $X and $x welcome message options.
+
+// Version 1.0.4.37 Jan 2011
+
+// Change L command not to list the last message if no new ones are available
+// Add LC I I@ IH IZ commands
+// Add option to send warning to sysop if forwarded P or T message has nowhere to go
+// Fixes for Winpack Compressed Download
+// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set.
+// Add console copy/paste.
+// Add "No Bulls" Option.
+// Add "Mail For" Beacon.
+// Tidied up Tab order in config dialogs to help text-to-speech programs.
+// Limit MaxMsgno to 99000.
+
+// Version 1.0.4.38 Feb 2011
+
+// Renumbered for release
+
+// Version 1.0.4.40 April 2011
+
+// Add POLLRMS command
+
+// Changes for Vista/Win7 (registry key change)
+// Workaround for changes to RMS Express
+// Fix AUTH bug in SMTP server
+// Add filter to Edit Messages dialog
+
+// Version 1.0.4.41 April 2011
+
+// Extend B2 proposals to other BPQMail systems so Reject Filter will work.
+// Add Edit User Command
+// Use internal Registry Save routine instead of Regedit
+// Fix Start Forward/All
+// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag)
+// Add FWD SYSOP command
+// Fix security on POLLRMS command
+// Add AUTH command
+// Leave selection in same place after Delete User
+// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k
+// Add option to show name as well as call on Chat messages
+// Fix program error if you try to define more than 80 BBS's
+
+// Version 1.0.4.45 October 2011
+
+// Changes to program error reporting.
+// BBS "Returh to Node" command added
+// Move config to "Standard" location (BPQ Directory/BPQMailChat) .
+// Fix crash if "Edit Message" clicked with no message selected.
+
+// Version 1.0.4.46 October 2011
+
+// Fix BaseDir test when BaseDir ends with \ or /
+// Fix long BaseDir values (>50 chars)
+
+// Version 1.4.47.1 January 2012
+
+// Call CloseBPQ32 on exit
+// Add option to flash window instead of sounding bell on Chat Connects
+// Add ShowRMS SYSOP command
+// Update WP with I records from R: lines
+// Send WP Updates
+// Fix Paclen on Pactor-like sessions
+// Fix SID and Prompt when RMS Express User is set
+// Try to stop loop in Program Error/Restarting code
+// Trap "UNABLE TO CONNECT" response in connect script
+// Add facility to print messages or save them to a text file
+
+// Version 1.4.48.1 January 2012
+
+// Add Send Message (as well as Send from Clipboard)
+// Fix Email From: Address when forwaring using B2
+// Send WP from BBSCALL not SYSOPCALL
+// Send Chat Map reports via BPQ32.dll
+
+
+// Version 1.4.49.1 February 2012
+
+
+// Fix Setting Paclink mode on SNOS connects
+// Remove creation of debugging file for each message
+// Add Message Export and Import functions
+// All printing of more than one message at a time
+// Add command to toggle "Expert" status
+
+// Version 1.4.50.1 February 2012
+
+// Fix forwarding to RMS Express users
+// Route messages received via B2 to an Internet email address to RMS
+// Add Reverse Poll interval
+// Add full FROM address to POP3 messages
+// Include HOMEBBS command in Help
+
+
+// Version 1.4.51.1 June 2012
+
+// Allow bulls to be sent from RMS Express.
+// Handle BASE64 and Quoted-printable encoding of single part messages
+// Work round for RMS Express "All proposals rejected" Bug.
+
+// Version 1.4.52.1 August 2012
+
+// Fix size limit on B2 To List when sending to multiple dests
+// Fix initialisation of DIRMES.SYS control record
+// Allow use of Tracker and UZ7HO ports for UI messages
+
+// Version 1.4.53.1 September 2012
+
+// Fix crash if R: line with out a CR found.
+
+// Version 1.4.54.1 ?? 2012
+
+// Add configurable prompts
+// Fix KISS-Only Test
+// Send EHLO instead of HELO when Authentication is needed on SMTP session
+// Add option to use local tome for bbs forwarding config
+// Allow comment lines (; or @) or single space in fwd scripts
+// Fix loss of forwarding info if SAVE is clicked before selecting a call
+
+// Version 1.4.55.1 June 2013
+
+// Add option to remove users that have not connected for a long time.
+// Add l@ smtp:
+// Fix From: sent to POP3 Client when meaages is from RMS
+// Display Email From on Manage Messages
+
+// Version 1.4.56.1 July 2013
+
+// Add timeout
+// Verify prompts
+// Add IDLETIME command
+
+
+
+// Version 1.4.57.1
+
+// Change default IDLETIME
+// Fix display of BBS's in Web "Manage Messages"
+// Add separate househeeping lifetines for T messages
+// Don't change flag on forwarded or delivered messages if they sre subsequently read
+// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet
+// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured
+// Fix receiving NTS messages via B2
+// Add option to send "Mail For", but not FBB Headers
+// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems
+// Fix Endian bug in FBB Compression code
+
+
+// Version 1.4.58.1
+
+// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag
+// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink
+// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink
+// Collect stats by message type
+// Fix Non-Delivery notifications to SMTP messages
+// Add Message Type Stats to BBS Trafic Report
+// Add "Batch forward to email"
+// Add EXPORT command
+// Allow more BBS records
+// Allow lower case connect scripts
+// Fix POP3 LIST command
+// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding
+// Fix duplicates of SP SYSOP@WW Messages
+// Add command line option (tidymail) to delete redundant Mail files
+// Add command line option (nohomebbs) to suppress HomeBBS prompt
+
+// 59 April 2014
+
+// Add FLARQ Mail Mode
+// Fix possible crash saving restart data
+// Add script command ADDLF for connect scripts over Telnet
+// Add recogniton of URONODE connected message
+// Add option to stop Name prompt
+// Add new RMS Express users with "RMS Express User" flag set
+// Validate HTML Pages
+// Add NTS swap file
+// Add basic File list and read functions
+// Fix Traffic report
+
+// 60
+
+// Fix security hole in readfile
+
+// 61 August 2014
+// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS
+// Dont treat "Attempting downlink" as a failure
+// Add option to read messages during a list
+// Fix crash during message renumber on MAC
+// Timeout response to SID to try to avoid hang on an incomplete connection.
+// Save config in file instead of registry
+// Fix Manage Messages "EXPORT" option and check filename on EXPORT command
+// Fix reverse forward prompt in MBL mode.
+// Fix From address in POP3 messages where path is @winlink.org
+// Fix possible program error in T message procesing
+// Add MaxAge param (for incoming Bulls)
+
+
+//62 November 2014
+// Add ZIP and Permit Bulls flag to Manage Users
+// Allow users to kill their own B and anyone to kill T messages
+// Improve saving of "Last Listed"
+// Fix LL when paging
+// Send Date received in R: Line (should fix B2 message restarts)
+// Fix occasional crash in terminal part line processing
+// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT
+// Fix possible retry loop when message is deferred (FBB '=' response);
+// Don't remove Attachments from received bulls.
+
+//63 Feb 2015
+
+// Fix creating Bulls from RMS Express messages.
+// Fix PE if message with no To: received.
+// Fix setting "RMS Express User" flag on new connects from RMS Express
+// Fix deleting 'T' messages downloaded by RMS Express
+// Include MPS messages in count of messages to forward.
+// Add new Welcome Message variable $F for messages to forward
+// Fix setting Type in B2 header when usong NTS: or BULL:
+// Remove trailing spaces from BID when Creating Message from Clipboard.
+// Improved handling of FBB B1/B2 Restarts.
+
+//64 September 2015
+
+// Fix Message Type in msgs from RMS Express to Internet
+// Reopen Monitor window if open when program list closed
+// Only apply NTS alias file to NTS Messages
+// Fix failure to store some encrypted ISP passwords
+// Allow EDITUSER to change "RMS Express User" flag
+// Fix reporting of Config File errors
+// Fix Finding MPS Messages (First to Forward was being used incorrectly)
+// Add "Save Attachment" to Web Mgmt Interface
+// Support Secure Signon on Forwarding sessions to CMS
+// Save Forwarding config when BBS flag on user is cleared
+// Pass internally generated SYSOP messages through routing process
+// Add POP3 TOP command.
+// Don't set 'T' messages to 'Y' when read.
+// Add optional temporary connect script on "FWD NOW" command
+// Add automatic import facility
+// Accept RMS mail to BBS Call even if "Poll RMS" not set.
+
+// 65 November 2015
+
+// Fix loading Housekeeping value for forwarded bulls.
+// Fix re-using Fwd script override in timer driven forwarding.
+// Add ampr.org handling
+// Add "Dont forward" match on TO address for NTS
+// Allow listing a combinatiom of state and type, such as LNT or LPF
+// Fix handling ISP messages from gmail without a '+'
+// Add basic WebMail support
+
+// 66
+
+// Autoimport messages as Dummy Call, not SYSOP Call
+// Add "My Messages" display option to WebMail
+// Create .csv extract of User List during hourekeeping.
+// Fix processing of NTS Alising of @ Addresses
+// Don't reroute Delivered NTS Messages
+// Add option to stop users killing T messages
+// Add multicast Receive
+// Fix initialising new message database format field
+// Fix "Forward Messages to BBS Call" option.
+// Add Filter WP Bulls option and allow multiple WP "TO" addresses
+// Fix deleting P WP messages for other stations
+// Fix saving blank lines in forwarding config
+// Fix paging on L@ and l<
+// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script
+// Run DeleteRedundantMessages before renumbering messages
+// Connect script now tries ELSE lines if prompt not received from remote BBS
+// Send connecting call instead of BBS Name when connecting to CMS server.
+// Add BID filter to Manage Messages
+// Fix handling of over long suject lines in IMPORT
+// Allow comments before ELSE in connect script
+// Add Copy and Clear to Multicast Window
+// Fix possible duplicate messages with MBL forwarding
+// Set "Permit EMail" on IMPORT dummy User.
+// Fix repeated running of housekeeping if clock is stepped forward.
+// Fix corruption of CMS Pass field by Web interface
+// Kill B2 WP bulls if FilterWPBulls set
+// Include Message Type in BPQ B2 proposal extensions
+
+// 6.0.14.1 July 2017
+
+// Fix corruption of BBSNumber if RMS Ex User and BBS both checked
+// Tread B messages without an AT as Flood.
+// Make sure Message headers are always saved to disk when a message status changes
+// Reject message instead of failing session if TO address too long in FBB forwarding
+// Fix error when FBB restart data exactly fills a packet.
+// Fix possible generation of msg number zero in send nondlivery notification
+// Fix problem with Web "Manage Messages" when stray message number zero appears
+// Fix Crash in AMPR forward when host missing from VIA
+// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS
+// Fix test for Status "D" in forward check.
+// Don't cancel AUTH on SMTP RSET
+// Fix "nowhere to go" message on some messages sent to smtp addresses
+// Add @ from Home BBS or WP is not spcified in "Send from Clipboard"
+
+// 6.0.15.1 Feb 2018
+
+// Fix PE if Filename missing from FILE connect script command
+// Suppress reporting errors after receiving FQ
+// Fix problem caused by trailing spaces on callsign in WP database
+// Support mixed case WINLINK Passwords
+
+// 6.0.16.1 March 2018
+
+// Make sure messages sent to WL2K don;'t have @ on from: address
+// If message to saildocs add R: line as an X header instead of to body
+// Close session if more than 4 Invalid Commmad responses sent
+// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client
+
+// 6.0.17.1 November 2018
+
+// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq
+// Accept an internet email address without rms: or smtp:
+// Fix "Forward messages for BBS Call" when TO isn't BBS Call
+// Accept NNTP commands in either case
+// Add NNTP BODY command
+// Timeout POP or SMTP TCP connections that are open too long
+// Add YAPP support
+// Fix connect script when Node CTEXT contains "} BBS "
+// Fix handling null H Route
+// Detect and correct duplicate BBS Numbers
+// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B)
+// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages
+// Add support for Winlink HTML Forms to WebMail interface
+// Update B2 header when using NTS alias file with B2 messages
+
+// 6.0.18.1 January 2019
+
+// Ensure callsigns in WP database are upper case.
+// Various fixes for Webmail
+// Fix sending direct to ampr.org addresses
+// Use SYSOP Call as default for Webmail if set
+// Preparations for 64 bit version
+
+
+// 6.0.19.1 September 2019
+
+// Trap missing HTML reply Template or HTML files
+// Fix case problems in HTML Templates
+// Fix setting To call on reply to HTML messages
+// More preparations for 64 bit including saving WP info as a text file.
+// Set "RMS Express User" when a new user connects using PAT
+// Increace maximum length on Forwarding Alias string in Web interface
+// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS
+// Fix program error if READ used without a filename
+// Trap reject messages from Winlink CMS
+// Fix "delete to recycle bin" on Linux
+// Handle Radio Only Messages (-T or -R suffix on calling station)
+// Fix program error on saving empty Alias list on Web Forwarding page
+// Add REQDIR and REQFIL
+// Experimental Blocked Uncompressed forwarding
+// Security fix for YAPP
+// Fix WebMail Cancel Send Message
+// Fix processing Hold Message response from Winlink Express
+
+// 6.0.20.1 April 2020
+
+// Improvments to YAPP
+// Add Copy forwarding config
+// Add Next and Previous buttons to Webmail message read screen
+// Move HTML templates from HTMLPages to inline code.
+// Fix Paclen on YAPP send
+// Fix bug in handling "RMS Express User"
+// Fix WINPACK compressed forwarding
+// Add option to send P messages to more than one BBS
+// Add "Default to Don't Add WINLINK.ORG" Config option
+// Re-read Badwords.sys during Housekeeping
+// Add BID Hold and Reject Filters
+// On SMTP Send try HELO if EHLO rejected
+// Allow SID response timeout to be configured per BBS
+// Fix sending bulls with PAT
+// Set "Forward Messages to BBS Call" when routing Bulls on TO
+// Add option to send Mail For Message to APRS
+// Fix WP update
+// Fix Holding messages from Webmail Interface
+// Add RMR command
+// Add REROUTEMSGS BBS SYSOP command
+// Disable null passwords and check Exclude flag in Webmail Signin
+// Add basic Webmail logging
+
+// 6.0.21.1 December 2020
+
+// Remove nulls from displayed messages.
+// Fix Holding messages from SMTP and POP3 Interfaces
+// Various fixes for handling messages to/from Internet email addresses
+// Fix saving Email From field in Manage Messages
+// Fix sending WL2K traffic reports via TriMode.
+// Fix removing successive CR from Webmail Message display
+// Fix Wildcarded @ forwarding
+// Fix message type when receiving NTS Msgs form Airmail
+// Fix address on SERVICE messages from Winlink
+// Add multiple TO processing to Webmail non-template messages
+// Don't backup config file if reading it fails
+// Include Port and Freq on Connected log record
+// Make sure welcome mesages don't end in >
+// Allow flagging unread T messages as Delivered
+// Replace \ with # in forward script so commands starting with # can be sent
+// Fix forwarding NTS on TO field
+// Fix possible crash in text mode forwarding
+// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured
+// Add DOHOUSEKEEPING sysop command
+// Add MARS continent code
+// Try to trap 'zombie' BBS Sessions
+// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory.
+// Fix corruption of message length when reading R2 message via Read command
+// Fix paging on List command and add new combinations of List options
+// Fix NNTP list and LC command when bulls are killed
+
+// 6.0.22.1 August 2021
+
+// Fix flagging messages with attachments as read.
+// Fix possible corruption of WP database and subsequent crash on reloading.
+// Fix format of Web Manage Messages display
+// Include SETNEXTMESSAGENUMBER in SYSOP Help Message
+// Fix occasional "Incoming Connect from SWITCH"
+// Fix L> with numeric dests
+// Improved diagnostic for MailTCP select() error.
+// Clear "RMS Express User" if user is changed to a BBS
+// Fix saving Window positions on exit
+// Fix parsing ReplyTemplate name in Webmail
+// Handle multiple addressees for WebMail Forms messages to packet stations
+// Add option to allow only known users to connect
+// Add basic callsign validation to From address
+// Add option to forward a user's messages to Winlink
+// Move User config to main config file.
+// Update message status whne reading a Forms Webmail message
+// Speed up killing multiple messages
+// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command
+
+// 6.0.23.1 June 2022
+
+// Fix crash when ; added to call in send commands
+// Allow smtp/ override for messages from RMS Express to send via ISP gateway
+// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured
+// Recompiled for Web Interface changes in Node
+// Add RMS Relay SYNC Mode (.17)
+// Add Protocol changes for Relay RO forwarding
+// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay
+// Fix B2 Header Date in Webmail message with sttachments.
+// Fix bug when using YAPP with VARA (.27)
+// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command
+// Add mechsnism to send bbs log records to qttermtcp. (32)
+// Add MFJ forwarding Mode (No @BBS on send)
+// Fix handling CR/LF split over packet boundaries
+// Add Header and Footers for Webmail Send (42)
+// Fix Maintenance Interval in LinBPQ (53)
+// Add RMS: to valid from addresses (.56)
+// Fix Web management on Android deviced (.58)
+// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70)
+// Check Badword and Reject filters before processing WP Messages
+
+// 6.0.24.1 August 2023
+
+// Fix ' in Webmail subject (8)
+// Change web buttons to white on black when pressed (10)
+// Add auto-refresh option to Webmail index page (25)
+// Fix displaying help and info files with crlf line endings on Linux (28)
+// Improve validation of extended FC message (32)
+// Improve WP check for SYSTEM as a callsign (33)
+// Improvements to RMS Relay SYNC mode (47)
+// Fix BID Hold and Reject filters
+// Fix Webmail auto-refresh when page exceeds 64K bytes (54)
+// Fix Webmail send when using both headers/footers and attachmonts (55)
+// Fix R: line corruption on some 64 bit builds
+// Dont drop empty lines in TEXTFORWARDING (61)
+// Dont wait for body prompt for TEXTFORWARDING for SID [PMS-3.2-C$] (62)
+// Add forwarding mode SETCALLTOSENDER for PMS Systems that don't accept < in SP (63)
+// QtTerm Monitoring fixed for 63 port version of BPQ (69)
+// Fix to UI system to support up to 63 ports (79)
+// Fix recently introduced crash when "Don't allow new users" is set (81)
+// Skip comments before TIMES at start of Connect Script (83)
+
+// 6.0.25.1 ??
+
+// Add FBB reject.sys style filters (3)
+// Improve Webmail on 64 bit builds
+// Fix setting status '$' on Bulls sent via WebMail (22)
+// Implement New Message and Message Read Events (23)
+// Start adding json api (25)
+// Fix reading nested directories when loading Standard Templates and other template bugs (25)
+// Add TO and AT to "Message has nowhere to go" message (28)
+// Add My Sent and My Received filter options to Webmail (30)
+// Add Send P to multiple BBS's when routing on HR (30)
+// Fix Traffic stats for T messages received via B2 forwarding (31)
+// Fix possible failure to update last listed count when user disconnects without using B command
+// Add short random delay (<30 secs) when forward new Messages immediately is enabled (35)
+// Fix Connect Script IDLETIME (38)
+// Add "Mail Mgmt" to Webmail menu bar and "WebMail" to Mail Mgmt Menu (39)
+// Improve "New User" frequency determination (39)
+// Allow selection of 2 or 4 character country codes for forward processing (39)
+// Fix Send P to multiple BBS's when routing on HR (40)
+// Rewrite PG server code on Lunux (41)
+// Fix SendPToMultiple not stopping at Implied AT match (45)
+// Log Our HA when checking for flood bulls (45)
+// Semaphore calls to SaveConfig
+// Include SERVIC as valid from call (for Winlink Service messages) (49)
+// Attempt to detect line draw characters in Webmail (50)
+// Fix sending ampr.org mail when RMS is not enabled (51)
+// Send forwarding info to packetnodes.spots.radio database (51)
+// Fix bug in WP Message processing (56)
+// Fix treating addresses ending in WW as Internet (57)
+// Run sending to packetnodes.spots.radio in a separate thread (61)
+// Fix loading ISP Account Name from config file (67)
+// Fixes to using {FormFolder} in Webmail Templates (68)
+// Save FBB transfer restart data over program restarts (69)
+// Add Send and Receive byte counts to status displays (69)
+// Validate Mode and Frequency and fix formatting in Connected Message (71)
+// Fix using OpenBCM on other than Telnet connections (75)
+
+#include "bpqmail.h"
+#include "winstdint.h"
+#define MAIL
+#include "Versions.h"
+
+#include "GetVersion.h"
+
+#define MAX_LOADSTRING 100
+
+typedef int (WINAPI FAR *FARPROCX)();
+typedef int (WINAPI FAR *FARPROCZ)();
+
+FARPROCX pDllBPQTRACE;
+FARPROCZ pGetLOC;
+FARPROCX pRefreshWebMailIndex;
+FARPROCX pRunEventProgram;
+FARPROCX pGetPortFrequency;
+FARPROCX pSendWebRequest;
+FARPROCX pGetLatLon;
+FARPROCX pGetPortHardwareType;
+
+BOOL WINE = FALSE;
+
+INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+
+VOID SetupNTSAliases(char * FN);
+
+HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default
+char * REGTREETEXT = "HKEY_LOCAL_MACHINE";
+
+// Global Variables:
+HINSTANCE hInst; // current instance
+TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
+TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
+
+extern int LastVer[4]; // In case we need to do somthing the first time a version is run
+
+UINT BPQMsg;
+
+HWND MainWnd;
+HWND hWndSess;
+RECT MainRect;
+HMENU hActionMenu;
+static HMENU hMenu;
+HMENU hDisMenu; // Disconnect Menu Handle
+HMENU hFWDMenu; // Forward Menu Handle
+
+int SessX, SessY, SessWidth; // Params for Session Window
+
+char szBuff[80];
+
+#define MaxSockets 64
+
+int _MYTIMEZONE = 0;
+
+ConnectionInfo Connections[MaxSockets+1];
+
+//struct SEM AllocSemaphore = {0, 0};
+//struct SEM ConSemaphore = {0, 0};
+//struct SEM OutputSEM = {0, 0};
+
+//struct UserInfo ** UserRecPtr=NULL;
+//int NumberofUsers=0;
+
+//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes
+
+//struct MsgInfo ** MsgHddrPtr=NULL;
+//int NumberofMessages=0;
+
+//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search
+
+//BIDRec ** BIDRecPtr=NULL;
+//int NumberofBIDs=0;
+
+extern BIDRec ** TempBIDRecPtr;
+//int NumberofTempBIDs=0;
+
+//WPRec ** WPRecPtr=NULL;
+//int NumberofWPrecs=0;
+
+extern char ** BadWords;
+//int NumberofBadWords=0;
+extern char * BadFile;
+
+//int LatestMsg = 0;
+//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg
+//int HighestBBSNumber = 0;
+
+//int MaxMsgno = 60000;
+//int BidLifetime = 60;
+//int MaintInterval = 24;
+//int MaintTime = 0;
+//int UserLifetime = 0;
+
+
+BOOL cfgMinToTray;
+
+BOOL DisconnectOnClose;
+
+extern char PasswordMsg[100];
+
+char cfgHOSTPROMPT[100];
+
+char cfgCTEXT[100];
+
+char cfgLOCALECHO[100];
+
+char AttemptsMsg[];
+char disMsg[];
+
+char LoginMsg[];
+
+char BlankCall[];
+
+
+ULONG BBSApplMask;
+ULONG ChatApplMask;
+
+int BBSApplNum;
+
+//int StartStream=0;
+int NumberofStreams;
+int MaxStreams;
+
+extern char BBSSID[];
+extern char ChatSID[];
+
+extern char NewUserPrompt[100];
+
+extern char * WelcomeMsg;
+extern char * NewWelcomeMsg;
+extern char * ExpertWelcomeMsg;
+
+extern char * Prompt;
+extern char * NewPrompt;
+extern char * ExpertPrompt;
+
+extern BOOL DontNeedHomeBBS;
+
+char BBSName[100];
+char MailForText[100];
+
+char SignoffMsg[100];
+
+char AbortedMsg[100];
+
+extern char UserDatabaseName[MAX_PATH];
+extern char UserDatabasePath[MAX_PATH];
+
+extern char MsgDatabasePath[MAX_PATH];
+extern char MsgDatabaseName[MAX_PATH];
+
+extern char BIDDatabasePath[MAX_PATH];
+extern char BIDDatabaseName[MAX_PATH];
+
+extern char WPDatabasePath[MAX_PATH];
+extern char WPDatabaseName[MAX_PATH];
+
+extern char BadWordsPath[MAX_PATH];
+extern char BadWordsName[MAX_PATH];
+
+char NTSAliasesPath[MAX_PATH];
+extern char NTSAliasesName[MAX_PATH];
+
+char BaseDir[MAX_PATH];
+char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME%
+
+char MailDir[MAX_PATH];
+
+char RlineVer[50];
+
+extern BOOL KISSOnly;
+
+extern BOOL OpenMon;
+
+extern struct ALIAS ** NTSAliases;
+
+extern int EnableUI;
+extern int RefuseBulls;
+extern int SendSYStoSYSOPCall;
+extern int SendBBStoSYSOPCall;
+extern int DontHoldNewUsers;
+extern int ForwardToMe;
+
+extern int MailForInterval;
+
+char zeros[NBMASK]; // For forward bitmask tests
+
+time_t MaintClock; // Time to run housekeeping
+
+struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List.
+
+// Filter Params
+
+char ** RejFrom; // Reject on FROM Call
+char ** RejTo; // Reject on TO Call
+char ** RejAt; // Reject on AT Call
+char ** RejBID; // Reject on BID
+
+char ** HoldFrom; // Hold on FROM Call
+char ** HoldTo; // Hold on TO Call
+char ** HoldAt; // Hold on AT Call
+char ** HoldBID; // Hold on BID
+
+
+// Send WP Params
+
+BOOL SendWP;
+char SendWPVIA[81];
+char SendWPTO[11];
+int SendWPType;
+
+
+int ProgramErrors = 0;
+
+UCHAR BPQDirectory[260] = "";
+
+
+// Forward declarations of functions included in this code module:
+ATOM MyRegisterClass(HINSTANCE hInstance);
+ATOM RegisterMainWindowClass(HINSTANCE hInstance);
+BOOL InitInstance(HINSTANCE, int);
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+
+unsigned long _beginthread( void( *start_address )(VOID * DParam),
+ unsigned stack_size, VOID * DParam);
+
+VOID SendMailForThread(VOID * Param);
+BOOL CreatePipeThread();
+int DeleteRedundantMessages();
+VOID BBSSlowTimer();
+VOID CopyConfigFile(char * ConfigName);
+BOOL CreateMulticastConsole();
+char * CheckToAddress(CIRCUIT * conn, char * Addr);
+BOOL CheckifPacket(char * Via);
+int GetHTMLForms();
+VOID GetPGConfig();
+void SendBBSDataToPktMap();
+
+struct _EXCEPTION_POINTERS exinfox;
+
+CONTEXT ContextRecord;
+EXCEPTION_RECORD ExceptionRecord;
+
+DWORD Stack[16];
+
+BOOL Restarting = FALSE;
+
+void Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg)
+{
+ unsigned int SPPtr;
+ unsigned int SPVal;
+
+ memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord));
+ memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord));
+
+ SPPtr = ContextRecord.Esp;
+
+ Debugprintf("BPQMail *** Program Error %x at %x in %s",
+ ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg);
+
+
+ __asm{
+
+ mov eax, SPPtr
+ mov SPVal,eax
+ lea edi,Stack
+ mov esi,eax
+ mov ecx,64
+ rep movsb
+
+ }
+
+ Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x",
+ ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx,
+ ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal);
+
+ Debugprintf("Stack:");
+
+ Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ",
+ SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]);
+
+ Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ",
+ SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]);
+
+}
+
+
+
+void myInvalidParameterHandler(const wchar_t* expression,
+ const wchar_t* function,
+ const wchar_t* file,
+ unsigned int line,
+ uintptr_t pReserved)
+{
+ Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called");
+
+ if (expression && function && file)
+ {
+ Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression);
+ Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function);
+ Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line);
+ }
+}
+
+// If program gets too many program errors, it will restart itself and shut down
+
+VOID CheckProgramErrors()
+{
+ STARTUPINFO SInfo; // pointer to STARTUPINFO
+ PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION
+ char ProgName[256];
+
+ if (Restarting)
+ exit(0); // Make sure can't loop in restarting
+
+ ProgramErrors++;
+
+ if (ProgramErrors > 25)
+ {
+ Restarting = TRUE;
+
+ Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing");
+
+ if (cfgMinToTray)
+ {
+ DeleteTrayMenuItem(MainWnd);
+ if (ConsHeader[0]->hConsole)
+ DeleteTrayMenuItem(ConsHeader[0]->hConsole);
+ if (ConsHeader[1]->hConsole)
+ DeleteTrayMenuItem(ConsHeader[1]->hConsole);
+ if (hMonitor)
+ DeleteTrayMenuItem(hMonitor);
+ }
+
+ SInfo.cb=sizeof(SInfo);
+ SInfo.lpReserved=NULL;
+ SInfo.lpDesktop=NULL;
+ SInfo.lpTitle=NULL;
+ SInfo.dwFlags=0;
+ SInfo.cbReserved2=0;
+ SInfo.lpReserved2=NULL;
+
+ GetModuleFileName(NULL, ProgName, 256);
+
+ Debugprintf("Attempting to Restart %s", ProgName);
+
+ CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo);
+
+ exit(0);
+ }
+}
+
+
+VOID WriteMiniDump()
+{
+#ifdef WIN32
+
+ HANDLE hFile;
+ BOOL ret;
+ char FN[256];
+
+ sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL));
+
+ hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
+ {
+ // Create the minidump
+
+ ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
+ hFile, MiniDumpNormal, 0, 0, 0 );
+
+ if(!ret)
+ Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError());
+ else
+ Debugprintf("Minidump %s created.", FN);
+ CloseHandle(hFile);
+ }
+#endif
+}
+
+
+
+#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
+ //
+#ifdef WIN32
+ if (Semaphore->Flag != 0)
+ {
+ Semaphore->Clashes++;
+ }
+loop1:
+
+ while (Semaphore->Flag != 0)
+ {
+ Sleep(10);
+ }
+
+ //
+ // try to get semaphore
+ //
+
+ _asm{
+
+ mov eax,1
+ mov ebx, Semaphore
+ xchg [ebx],eax // this instruction is locked
+
+ cmp eax,0
+ jne loop1 // someone else got it - try again
+;
+; ok, weve got the semaphore
+;
+ }
+#else
+
+ while (Semaphore->Flag)
+ usleep(10000);
+
+ Semaphore->Flag = 1;
+
+#endif
+ return;
+}
+
+void FreeSemaphore(struct SEM * Semaphore)
+{
+ Semaphore->Flag = 0;
+
+ return;
+}
+
+char * CmdLine;
+
+extern int configSaved;
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPTSTR lpCmdLine,
+ int nCmdShow)
+{
+ MSG msg;
+ HACCEL hAccelTable;
+ int BPQStream, n;
+ struct UserInfo * user;
+ struct _EXCEPTION_POINTERS exinfo;
+ _invalid_parameter_handler oldHandler, newHandler;
+ char Msg[100];
+ int i = 60;
+ struct NNTPRec * NNTPREC;
+ struct NNTPRec * SaveNNTPREC;
+
+ CmdLine = _strdup(lpCmdLine);
+ _strlwr(CmdLine);
+
+ if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs
+ {
+ hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0,
+ CW_USEDEFAULT, 100, 550, 70,
+ NULL, NULL, hInstance, NULL);
+
+ ShowWindow(hWnd, nCmdShow);
+
+ while (i-- > 0)
+ {
+ sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i);
+ SetWindowText(hWnd, Msg);
+
+ Sleep(1000);
+ }
+
+ DestroyWindow(hWnd);
+ }
+
+ __try {
+
+ // Trap CRT Errors
+
+ newHandler = myInvalidParameterHandler;
+ oldHandler = _set_invalid_parameter_handler(newHandler);
+
+ // Initialize global strings
+ LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+ LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING);
+ MyRegisterClass(hInstance);
+
+ // Perform application initialization:
+
+ if (!InitInstance (hInstance, nCmdShow))
+ {
+ return FALSE;
+ }
+
+ hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat));
+
+ // Main message loop:
+
+ Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting");
+ Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting");
+ Debugprintf("BPQMail Starting");
+
+ if (pDllBPQTRACE == 0)
+ Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable");
+
+
+ } My__except_Routine("Init");
+
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ __try
+ {
+ if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ #define EXCEPTMSG "GetMessageLoop"
+ #include "StdExcept.c"
+
+ CheckProgramErrors();
+ }
+ }
+
+ __try
+ {
+ for (n = 0; n < NumberofStreams; n++)
+ {
+ BPQStream=Connections[n].BPQStream;
+
+ if (BPQStream)
+ {
+ SetAppl(BPQStream, 0, 0);
+ Disconnect(BPQStream);
+ DeallocateStream(BPQStream);
+ }
+ }
+
+
+ hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0,
+ 150, 200, 350, 40, NULL, NULL, hInstance, NULL);
+
+ ShowWindow(hWnd, nCmdShow);
+
+ Sleep(1000); // A bit of time for links to close
+
+ DestroyWindow(hWnd);
+
+ if (ConsHeader[0]->hConsole)
+ DestroyWindow(ConsHeader[0]->hConsole);
+ if (ConsHeader[1]->hConsole)
+ DestroyWindow(ConsHeader[1]->hConsole);
+ if (hMonitor)
+ {
+ DestroyWindow(hMonitor);
+ hMonitor = (HWND)1; // For status Save
+ }
+
+
+// SaveUserDatabase();
+ SaveMessageDatabase();
+ SaveBIDDatabase();
+ SaveRestartData();
+
+ configSaved = 1;
+ SaveConfig(ConfigName);
+
+ if (cfgMinToTray)
+ {
+ DeleteTrayMenuItem(MainWnd);
+ if (ConsHeader[0]->hConsole)
+ DeleteTrayMenuItem(ConsHeader[0]->hConsole);
+ if (ConsHeader[1]->hConsole)
+ DeleteTrayMenuItem(ConsHeader[1]->hConsole);
+ if (hMonitor)
+ DeleteTrayMenuItem(hMonitor);
+ }
+
+ // Free all allocated memory
+
+ for (n = 0; n <= NumberofUsers; n++)
+ {
+ user = UserRecPtr[n];
+
+ if (user->ForwardingInfo)
+ {
+ FreeForwardingStruct(user);
+ free(user->ForwardingInfo);
+ }
+ /* ---------- TAJ --PG Server------*/
+
+ if (user->Temp && user->Temp->RUNPGPARAMS ) {
+
+ printf("Also freeing RUNPGARGS\n");
+ free(user->Temp->RUNPGPARAMS);
+ }
+ /* --------------------------------*/
+
+ free(user->Temp);
+
+ free(user);
+ }
+
+ free(UserRecPtr);
+
+ for (n = 0; n <= NumberofMessages; n++)
+ free(MsgHddrPtr[n]);
+
+ free(MsgHddrPtr);
+
+ for (n = 0; n <= NumberofWPrecs; n++)
+ free(WPRecPtr[n]);
+
+ free(WPRecPtr);
+
+ for (n = 0; n <= NumberofBIDs; n++)
+ free(BIDRecPtr[n]);
+
+ free(BIDRecPtr);
+
+ if (TempBIDRecPtr)
+ free(TempBIDRecPtr);
+
+ NNTPREC = FirstNNTPRec;
+
+ while (NNTPREC)
+ {
+ SaveNNTPREC = NNTPREC->Next;
+ free(NNTPREC);
+ NNTPREC = SaveNNTPREC;
+ }
+
+ if (BadWords) free(BadWords);
+ if (BadFile) free(BadFile);
+
+ n = 0;
+
+ if (Aliases)
+ {
+ while(Aliases[n])
+ {
+ free(Aliases[n]->Dest);
+ free(Aliases[n]);
+ n++;
+ }
+
+ free(Aliases);
+ FreeList(AliasText);
+ }
+
+ n = 0;
+
+ if (NTSAliases)
+ {
+ while(NTSAliases[n])
+ {
+ free(NTSAliases[n]->Dest);
+ free(NTSAliases[n]);
+ n++;
+ }
+
+ free(NTSAliases);
+ }
+
+ FreeOverrides();
+
+ FreeList(RejFrom);
+ FreeList(RejTo);
+ FreeList(RejAt);
+ FreeList(RejBID);
+ FreeList(HoldFrom);
+ FreeList(HoldTo);
+ FreeList(HoldAt);
+ FreeList(HoldBID);
+ FreeList(SendWPAddrs);
+
+ Free_UI();
+
+ for (n=1; n<20; n++)
+ {
+ if (MyElements[n]) free(MyElements[n]);
+ }
+
+ free(WelcomeMsg);
+ free(NewWelcomeMsg);
+ free(ExpertWelcomeMsg);
+
+ free(Prompt);
+ free(NewPrompt);
+ free(ExpertPrompt);
+
+ FreeWebMailMallocs();
+
+ free(CmdLine);
+
+ _CrtDumpMemoryLeaks();
+
+ }
+ My__except_Routine("Close Processing");
+
+ CloseBPQ32(); // Close Ext Drivers if last bpq32 process
+
+ return (int) msg.wParam;
+}
+
+
+
+//
+// FUNCTION: MyRegisterClass()
+//
+// PURPOSE: Registers the window class.
+//
+// COMMENTS:
+//
+// This function and its usage are only necessary if you want this code
+// to be compatible with Win32 systems prior to the 'RegisterClassEx'
+// function that was added to Windows 95. It is important to call this function
+// so that the application will get 'well formed' small icons associated
+// with it.
+//
+//
+#define BGCOLOUR RGB(236,233,216)
+//#define BGCOLOUR RGB(245,245,245)
+
+HBRUSH bgBrush;
+
+ATOM MyRegisterClass(HINSTANCE hInstance)
+{
+ WNDCLASSEX wcex;
+
+ bgBrush = CreateSolidBrush(BGCOLOUR);
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = DLGWINDOWEXTRA;
+ wcex.hInstance = hInstance;
+ wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON));
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.hbrBackground = bgBrush;
+ wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat);
+ wcex.lpszClassName = szWindowClass;
+ wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON));
+
+ return RegisterClassEx(&wcex);
+}
+
+
+//
+// FUNCTION: InitInstance(HINSTANCE, int)
+//
+// PURPOSE: Saves instance handle and creates main window
+//
+// COMMENTS:
+//
+// In this function, we save the instance handle in a global variable and
+// create and display the main program window.
+//
+
+HWND hWnd;
+
+int AXIPPort = 0;
+
+char LOC[7] = "";
+
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+ char Title[80];
+ WSADATA WsaData;
+ HMENU hTopMenu; // handle of menu
+ HKEY hKey=0;
+ int retCode;
+ RECT InitRect;
+ RECT SessRect;
+ struct _EXCEPTION_POINTERS exinfo;
+
+ HMODULE ExtDriver = LoadLibrary("bpq32.dll");
+
+ if (ExtDriver)
+ {
+ pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8");
+ pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0");
+ pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0");
+ pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8");
+ pGetPortFrequency = GetProcAddress(ExtDriver,"_GetPortFrequency@8");
+ pSendWebRequest = GetProcAddress(ExtDriver,"_SendWebRequest@16");
+ pGetLatLon = GetProcAddress(ExtDriver,"_GetLatLon@8");
+ pGetPortHardwareType = GetProcAddress(ExtDriver,"_GetPortHardwareType@4");
+
+
+
+ if (pGetLOC)
+ {
+ char * pLOC = (char *)pGetLOC();
+ memcpy(LOC, pLOC, 6);
+ }
+ }
+
+ // See if running under WINE
+
+ retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey);
+
+ if (retCode == ERROR_SUCCESS)
+ {
+ RegCloseKey(hKey);
+ WINE =TRUE;
+ Debugprintf("Running under WINE");
+ }
+
+
+ REGTREE = GetRegistryKey();
+ REGTREETEXT = GetRegistryKeyText();
+
+ Sleep(1000);
+
+ {
+ int n;
+ struct _EXTPORTDATA * PORTVEC;
+
+ KISSOnly = TRUE;
+
+ for (n=1; n <= GetNumberofPorts(); n++)
+ {
+ PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n);
+
+ if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL
+ {
+ if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0)
+ KISSOnly = FALSE;
+
+ if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR
+ KISSOnly = FALSE;
+
+ if (AXIPPort == 0)
+ {
+ if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0)
+ {
+ AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER;
+ KISSOnly = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ hInst = hInstance;
+
+ hWnd=CreateDialog(hInst,szWindowClass,0,NULL);
+
+ if (!hWnd)
+ {
+ return FALSE;
+ }
+
+ MainWnd = hWnd;
+
+ GetVersionInfo(NULL);
+
+ sprintf(Title,"G8BPQ Mail Server Version %s", VersionString);
+
+ sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]);
+
+ SetWindowText(hWnd,Title);
+
+ hWndSess = GetDlgItem(hWnd, 100);
+
+ GetWindowRect(hWnd, &InitRect);
+ GetWindowRect(hWndSess, &SessRect);
+
+ SessX = SessRect.left - InitRect.left ;
+ SessY = SessRect.top -InitRect.top;
+ SessWidth = SessRect.right - SessRect.left;
+
+ // Get handles for updating menu items
+
+ hTopMenu=GetMenu(MainWnd);
+ hActionMenu=GetSubMenu(hTopMenu,0);
+
+ hFWDMenu=GetSubMenu(hActionMenu,0);
+ hMenu=GetSubMenu(hActionMenu,1);
+ hDisMenu=GetSubMenu(hActionMenu,2);
+
+ CheckTimer();
+
+ cfgMinToTray = GetMinimizetoTrayFlag();
+
+ if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE))
+ if (cfgMinToTray)
+ {
+ ShowWindow(hWnd, SW_HIDE);
+ }
+ else
+ {
+ ShowWindow(hWnd, nCmdShow);
+ }
+ else
+ ShowWindow(hWnd, nCmdShow);
+
+ UpdateWindow(hWnd);
+
+ WSAStartup(MAKEWORD(2, 0), &WsaData);
+
+ __try {
+
+ return Initialise();
+
+ }My__except_Routine("Initialise");
+
+ return FALSE;
+}
+
+//
+// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
+//
+// PURPOSE: Processes messages for the main window.
+//
+//
+
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ int wmId, wmEvent;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ int state,change;
+ ConnectionInfo * conn;
+ struct _EXCEPTION_POINTERS exinfo;
+
+
+ if (message == BPQMsg)
+ {
+ if (lParam & BPQMonitorAvail)
+ {
+ __try
+ {
+ DoBBSMonitorData(wParam);
+ }
+ My__except_Routine("DoMonitorData");
+
+ return 0;
+
+ }
+ if (lParam & BPQDataAvail)
+ {
+ // Dont trap error at this level - let Node error handler pick it up
+// __try
+// {
+ DoReceivedData(wParam);
+// }
+// My__except_Routine("DoReceivedData")
+ return 0;
+ }
+ if (lParam & BPQStateChange)
+ {
+ // Get current Session State. Any state changed is ACK'ed
+ // automatically. See BPQHOST functions 4 and 5.
+
+ __try
+ {
+ SessionState(wParam, &state, &change);
+
+ if (change == 1)
+ {
+ if (state == 1) // Connected
+ {
+ GetSemaphore(&ConSemaphore, 0);
+ __try {Connected(wParam);}
+ My__except_Routine("Connected");
+ FreeSemaphore(&ConSemaphore);
+ }
+ else
+ {
+ GetSemaphore(&ConSemaphore, 0);
+ __try{Disconnected(wParam);}
+ My__except_Routine("Disconnected");
+ FreeSemaphore(&ConSemaphore);
+ }
+ }
+ }
+ My__except_Routine("DoStateChange");
+
+ }
+
+ return 0;
+ }
+
+
+ switch (message)
+ {
+
+ case WM_KEYUP:
+
+ switch (wParam)
+ {
+ case VK_F2:
+ CreateConsole(-1);
+ return 0;
+
+ case VK_F3:
+ CreateMulticastConsole();
+ return 0;
+
+ case VK_F4:
+ CreateMonitor();
+ return 0;
+
+ case VK_TAB:
+ return TRUE;
+
+ break;
+
+
+
+ }
+ return 0;
+
+ case WM_TIMER:
+
+ if (wParam == 1) // Slow = 10 secs
+ {
+ __try
+ {
+ time_t NOW = time(NULL);
+ struct tm * tm;
+ RefreshMainWindow();
+ CheckTimer();
+ TCPTimer();
+ BBSSlowTimer();
+ 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;
+ }
+ }
+ }
+ My__except_Routine("Slow Timer");
+ }
+ if (wParam == 3) // Forward (2 Secs)
+ {
+ __try
+ {
+ FWDTimerProc();
+ }
+ My__except_Routine("Fwd Timer");
+ }
+ else
+ __try
+ {
+ TrytoSend();
+ TCPFastTimer();
+ }
+ My__except_Routine("TrytoSend");
+
+ return (0);
+
+
+ case WM_CTLCOLORDLG:
+ return (LONG)bgBrush;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ HDC hdcStatic = (HDC)wParam;
+ SetTextColor(hdcStatic, RGB(0, 0, 0));
+ SetBkMode(hdcStatic, TRANSPARENT);
+ return (LONG)bgBrush;
+ }
+
+ case WM_INITMENUPOPUP:
+
+ if (wParam == (WPARAM)hActionMenu)
+ {
+ if (IsClipboardFormatAvailable(CF_TEXT))
+ EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED);
+ else
+ EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED );
+
+ return TRUE;
+ }
+
+ if (wParam == (WPARAM)hFWDMenu)
+ {
+ // Set up Forward Menu
+
+ struct UserInfo * user;
+ char MenuLine[30];
+
+ for (user = BBSChain; user; user = user->BBSNext)
+ {
+ sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user));
+
+ if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber,
+ MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0)
+
+ AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine);
+ }
+ return TRUE;
+ }
+
+ if (wParam == (WPARAM)hDisMenu)
+ {
+ // Set up Disconnect Menu
+
+ CIRCUIT * conn;
+ char MenuLine[30];
+ int n;
+
+ for (n = 0; n <= NumberofStreams-1; n++)
+ {
+ conn=&Connections[n];
+
+ RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND);
+
+ if (conn->Active)
+ {
+ sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign);
+ AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine);
+ }
+ }
+ return TRUE;
+ }
+ break;
+
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+ // Parse the menu selections:
+
+ if (wmEvent == LBN_DBLCLK)
+
+ break;
+
+ if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1)
+ {
+ // disconnect user
+
+ conn=&Connections[wmId-IDM_DISCONNECT];
+
+ if (conn->Active)
+ {
+ Disconnect(conn->BPQStream);
+ }
+ }
+
+ if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100)
+ {
+ StartForwarding(wmId - IDM_FORWARD_ALL, NULL);
+ return 0;
+ }
+
+ switch (wmId)
+ {
+ case IDM_LOGBBS:
+
+ ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS);
+ break;
+
+ case IDM_LOGCHAT:
+
+ ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT);
+ break;
+
+ case IDM_LOGTCP:
+
+ ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP);
+ break;
+
+ case IDM_HOUSEKEEPING:
+
+ DoHouseKeeping(TRUE);
+
+ break;
+
+ case IDM_CONSOLE:
+
+ CreateConsole(-1);
+ break;
+
+ case IDM_MCMONITOR:
+
+ CreateMulticastConsole();
+ break;
+
+ case IDM_MONITOR:
+
+ CreateMonitor();
+ break;
+
+ case RESCANMSGS:
+
+ ReRouteMessages();
+ break;
+
+ case IDM_IMPORT:
+
+ ImportMessages(NULL, "", FALSE);
+ break;
+
+ case IDM_ABOUT:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
+ break;
+
+ case ID_HELP_ONLINEHELP:
+
+ ShellExecute(hWnd,"open",
+ "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html",
+ "", NULL, SW_SHOWNORMAL);
+
+ break;
+
+ case IDM_CONFIG:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc);
+ break;
+
+ case IDM_USERS:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc);
+ break;
+
+ case IDM_FWD:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc);
+ break;
+
+ case IDM_MESSAGES:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc);
+ break;
+
+ case IDM_WP:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc);
+ break;
+
+ case ID_ACTIONS_SENDMSGFROMCLIPBOARD:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc);
+ break;
+
+ case ID_ACTIONS_SENDMESSAGE:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc);
+ break;
+
+ case ID_MULTICAST:
+
+ MulticastRX = !MulticastRX;
+ CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED);
+ break;
+
+ case IDM_EXIT:
+ DestroyWindow(hWnd);
+ break;
+
+
+
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ break;
+
+ case WM_SIZE:
+
+ if (wParam == SIZE_MINIMIZED)
+ if (cfgMinToTray)
+ return ShowWindow(hWnd, SW_HIDE);
+
+ return (0);
+
+
+ case WM_SIZING:
+ {
+ LPRECT lprc = (LPRECT) lParam;
+ int Height = lprc->bottom-lprc->top;
+ int Width = lprc->right-lprc->left;
+
+ MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE);
+
+ return TRUE;
+ }
+
+
+ case WM_PAINT:
+ hdc = BeginPaint(hWnd, &ps);
+ // TODO: Add any drawing code here...
+ EndPaint(hWnd, &ps);
+ break;
+
+ case WM_DESTROY:
+
+ GetWindowRect(MainWnd, &MainRect); // For save soutine
+ if (ConsHeader[0]->hConsole)
+ GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine
+ if (ConsHeader[1]->hConsole)
+ GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine
+ if (hMonitor)
+ GetWindowRect(hMonitor, &MonitorRect); // For save soutine
+
+ KillTimer(hWnd,1);
+ KillTimer(hWnd,2);
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_INITDIALOG:
+
+ SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B");
+ SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P");
+ SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T");
+
+ SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0);
+
+ return TRUE;
+
+ case WM_SIZING:
+ {
+ HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1);
+
+ LPRECT lprc = (LPRECT) lParam;
+ int Height = lprc->bottom-lprc->top;
+ int Width = lprc->right-lprc->left;
+
+ MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE);
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+
+ if (LOWORD(wParam) == IDSEND)
+ {
+ char status [3];
+ struct MsgInfo * Msg;
+ char * via = NULL;
+ char BID[13];
+ char FileList[32768];
+ BIDRec * BIDRec;
+ int MsgLen;
+ char * MailBuffer;
+ char MsgFile[MAX_PATH];
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ int WriteLen=0;
+ char HDest[61];
+ char Destcopy[61];
+ char * Vptr;
+ char * FileName[100];
+ int FileLen[100];
+ char * FileBody[100];
+ int n, Files = 0;
+ int TotalFileSize = 0;
+ char * NewMsg;
+
+ GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60);
+ strcpy(Destcopy, HDest);
+
+ GetDlgItemText(hDlg, IDC_MSGBID, BID, 13);
+ strlop(BID, ' ');
+
+ GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767);
+
+ // if there are attachments, check that they can be opened ane read
+
+ n = 0;
+
+ if (FileList[0])
+ {
+ FILE * Handle;
+ struct stat STAT;
+ char * ptr1 = FileList, * ptr2;
+
+ while(ptr1 && ptr1[0])
+ {
+ ptr2 = strchr(ptr1, ';');
+
+ if (ptr2)
+ *(ptr2++) = 0;
+
+ FileName[n++] = ptr1;
+
+ ptr1 = ptr2;
+ }
+
+ FileName[n] = 0;
+
+ // read the files
+
+ Files = n;
+ n = 0;
+
+ while (FileName[n])
+ {
+ if (stat(FileName[n], &STAT) == -1)
+ {
+ char ErrorMessage[512];
+ sprintf(ErrorMessage,"Can't find file %s", FileName[n]);
+ MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR);
+ return TRUE;
+ }
+
+ FileLen[n] = STAT.st_size;
+
+ Handle = fopen(FileName[n], "rb");
+
+ if (Handle == NULL)
+ {
+ char ErrorMessage[512];
+ sprintf(ErrorMessage,"Can't open file %s", FileName[n]);
+ MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR);
+ return TRUE;
+ }
+
+ FileBody[n] = malloc(FileLen[n]+1);
+
+ fread(FileBody[n], 1, FileLen[n], Handle);
+
+ fclose(Handle);
+
+ TotalFileSize += FileLen[n];
+ n++;
+ }
+ }
+
+ if (strlen(HDest) == 0)
+ {
+ MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR);
+ return TRUE;
+ }
+
+ if (strlen(BID))
+ {
+ if (LookupBID(BID))
+ {
+ // Duplicate bid
+
+ MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR);
+ return TRUE;
+ }
+ }
+
+ Msg = AllocateMsgRecord();
+
+ // Set number here so they remain in sequence
+
+ Msg->number = ++LatestMsg;
+ MsgnotoMsg[Msg->number] = Msg;
+
+ strcpy(Msg->from, SYSOPCall);
+
+ Vptr = strlop(Destcopy, '@');
+
+ if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @
+ {
+ Vptr = strchr(Destcopy, '!');
+ strcpy(Msg->via, Vptr);
+ strlop(Destcopy, '!');
+
+ if (strlen(Destcopy) > 6)
+ memcpy(Msg->to, Destcopy, 6);
+ else
+ strcpy(Msg->to, Destcopy);
+ goto gotAddr;
+ }
+
+ if (strlen(Destcopy) > 6)
+ memcpy(Msg->to, Destcopy, 6);
+ else
+ strcpy(Msg->to, Destcopy);
+
+ _strupr(Msg->to);
+
+ if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0)
+ {
+ Vptr = HDest;
+ memmove(HDest, &HDest[4], strlen(HDest));
+ strcpy(Msg->to, "RMS");
+
+ }
+ else if (_memicmp(HDest, "smtp:", 5) == 0)
+ {
+ if (ISP_Gateway_Enabled)
+ {
+ Vptr = HDest;
+ memmove(HDest, &HDest[5], strlen(HDest));
+ Msg->to[0] = 0;
+ }
+ }
+ else if (Vptr)
+ {
+ // If looks like a valid email address, treat as such
+
+ int tolen = (Vptr - Destcopy) - 1;
+
+ if (tolen > 6 || !CheckifPacket(Vptr))
+ {
+ // Assume Email address
+
+ Vptr = HDest;
+
+ if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route
+ strcpy(Msg->to, "RMS");
+ else if (ISP_Gateway_Enabled)
+ Msg->to[0] = 0;
+ else
+ {
+ MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR);
+ return TRUE;
+ }
+ }
+ }
+ if (Vptr)
+ {
+ if (strlen(Vptr) > 40)
+ Vptr[40] = 0;
+
+ strcpy(Msg->via, Vptr);
+ }
+gotAddr:
+ GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61);
+ GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2);
+ Msg->type = status[0];
+ Msg->status = 'N';
+
+ if (strlen(BID) == 0)
+ sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName);
+
+ strcpy(Msg->bid, BID);
+
+ Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
+
+ BIDRec = AllocateBIDRecord();
+
+ strcpy(BIDRec->BID, Msg->bid);
+ BIDRec->mode = Msg->type;
+ BIDRec->u.msgno = LOWORD(Msg->number);
+ BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
+
+ MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0);
+
+ MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments
+
+ if (Files)
+ {
+ char DateString[80];
+ struct tm * tm;
+
+ char Type[16] = "Private";
+
+ // Get Type
+
+ if (Msg->type == 'B')
+ strcpy(Type, "Bulletin");
+ else if (Msg->type == 'T')
+ strcpy(Type, "Traffic");
+
+ // Create a B2 Message
+
+ // B2 Header
+
+ NewMsg = MailBuffer + 1000;
+
+ tm = gmtime((time_t *)&Msg->datecreated);
+
+ sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+
+ // Remove last Source Route
+
+ if (strchr(HDest, '!'))
+ {
+ char * bang = HDest + strlen(HDest);
+
+ while (*(--bang) != '!'); // Find last !
+
+ *(bang) = 0; // remove it;
+ }
+
+ NewMsg += sprintf(NewMsg,
+ "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n",
+ Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName);
+
+
+ NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen);
+
+ for (n = 0; n < Files; n++)
+ {
+ char * p = FileName[n], * q;
+
+ // Remove any path
+
+ q = strchr(p, '\\');
+
+ while (q)
+ {
+ if (q)
+ *q++ = 0;
+ p = q;
+ q = strchr(p, '\\');
+ }
+
+ NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p);
+ }
+
+ NewMsg += sprintf(NewMsg, "\r\n");
+ GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1);
+ NewMsg += MsgLen;
+ NewMsg += sprintf(NewMsg, "\r\n");
+
+ for (n = 0; n < Files; n++)
+ {
+ memcpy(NewMsg, FileBody[n], FileLen[n]);
+ NewMsg += FileLen[n];
+ free(FileBody[n]);
+ NewMsg += sprintf(NewMsg, "\r\n");
+ }
+
+ Msg->length = NewMsg - (MailBuffer + 1000);
+ NewMsg = MailBuffer + 1000;
+ Msg->B2Flags = B2Msg | Attachments;
+ }
+
+ else
+ {
+ GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1);
+ Msg->length = MsgLen;
+ NewMsg = MailBuffer;
+ }
+
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
+
+ hFile = CreateFile(MsgFile,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL);
+ CloseHandle(hFile);
+ }
+
+ free(MailBuffer);
+
+ MatchMessagetoBBSList(Msg, 0);
+
+ BuildNNTPList(Msg); // Build NNTP Groups list
+
+ SaveMessageDatabase();
+ SaveBIDDatabase();
+
+ EndDialog(hDlg, LOWORD(wParam));
+
+#ifndef NOMQTT
+ if (MQTT)
+ MQTTMessageEvent(Msg);
+#endif
+
+
+ return TRUE;
+ }
+
+
+ if (LOWORD(wParam) == IDSelectFiles)
+ {
+ char FileNames[2048];
+ char FullFileNames[32768];
+ OPENFILENAME Ofn;
+ int err;
+
+ FileNames[0] = 0;
+
+ memset(&Ofn, 0, sizeof(Ofn));
+
+ Ofn.lStructSize = sizeof(OPENFILENAME);
+ Ofn.hInstance = hInst;
+ Ofn.hwndOwner = hDlg;
+ Ofn.lpstrFilter = NULL;
+ Ofn.lpstrFile= FileNames;
+ Ofn.nMaxFile = 2048;
+ Ofn.lpstrFileTitle = NULL;
+ Ofn.nMaxFileTitle = 0;
+ Ofn.lpstrInitialDir = (LPSTR)NULL;
+ Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
+ Ofn.lpstrTitle = NULL;//;
+
+ if (GetOpenFileName(&Ofn))
+ {
+ // if one is selected, a single string is returned, if more than one, a single
+ // path, followed by all the strings, duuble null terminated.
+
+ char * Names[101]; // Allow up to 100 names
+ int n = 0;
+ char * ptr = FileNames;
+
+ while (*ptr)
+ {
+ Names[n++] = ptr;
+ ptr += strlen(ptr);
+ ptr++;
+ }
+
+ GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768);
+
+ if (strlen(FullFileNames))
+ strcat(FullFileNames, ";");
+
+ if (n == 1)
+ {
+ // Single Select
+
+ strcat(FullFileNames, FileNames);
+ }
+ else
+ {
+ int i = 1;
+
+ while(i < n)
+ {
+ strcat(FullFileNames, Names[0]);
+ strcat(FullFileNames, "\\");
+ strcat(FullFileNames, Names[i]);
+ i++;
+ if (i < n)
+ strcat(FullFileNames, ";");
+ }
+ }
+ SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames);
+ }
+ else
+ err = GetLastError();
+ return (INT_PTR)TRUE;
+ }
+
+
+ if (LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+ }
+
+ return (INT_PTR)TRUE;
+
+ break;
+ }
+ return (INT_PTR)FALSE;
+}
+
+INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ HGLOBAL hglb;
+ LPTSTR lptstr;
+
+ switch (message)
+ {
+ case WM_INITDIALOG:
+
+ SetWindowText(hDlg, "Send Message from Clipboard");
+
+ if (!IsClipboardFormatAvailable(CF_TEXT))
+ break;
+
+ if (!OpenClipboard(hDlg))
+ break;
+
+ hglb = GetClipboardData(CF_TEXT);
+
+ if (hglb != NULL)
+ {
+ lptstr = GlobalLock(hglb);
+
+ if (lptstr != NULL)
+ {
+ SetDlgItemText(hDlg, IDC_EDIT1, lptstr);
+ GlobalUnlock(hglb);
+ }
+ }
+ CloseClipboard();
+ }
+
+ return SendMsgDialogProc(hDlg, message, wParam, lParam);
+
+}
+
+// Message handler for about box.
+INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ return (INT_PTR)TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+ }
+ return (INT_PTR)TRUE;
+
+ break;
+ }
+ return (INT_PTR)FALSE;
+}
+
+SMTPMsgs = 0;
+
+int RefreshMainWindow()
+{
+ char msg[80];
+ CIRCUIT * conn;
+ int i,n, SYSOPMsgs = 0, HeldMsgs = 0;
+ time_t now;
+ struct tm * tm;
+ char tim[20];
+
+ SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0);
+
+ SMTPMsgs = 0;
+
+ for (n = 0; n < NumberofStreams; n++)
+ {
+ conn=&Connections[n];
+
+ if (!conn->Active)
+ {
+ strcpy(msg,"Idle");
+ }
+ else
+ {
+ {
+ if (conn->UserPointer == 0)
+ strcpy(msg,"Logging in");
+ else
+ {
+ i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d %5d %5d",
+ conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream,
+ "BBS", conn->OutputQueueLength - conn->OutputGetPointer, conn->bytesSent, conn->bytesRxed);
+ }
+ }
+ }
+ SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg);
+ }
+
+ SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE);
+
+ n = 0;
+
+ for (i=1; i <= NumberofMessages; i++)
+ {
+ if (MsgHddrPtr[i]->status == 'N')
+ {
+ if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0)
+ SYSOPMsgs++;
+ else
+ if (MsgHddrPtr[i]->to[0] == 0)
+ SMTPMsgs++;
+ }
+ else
+ {
+ if (MsgHddrPtr[i]->status == 'H')
+ HeldMsgs++;
+ }
+ }
+
+ SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE);
+ SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE);
+ SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE);
+
+ SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE);
+ SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE);
+ SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE);
+
+ now = time(NULL);
+
+ tm = gmtime(&now);
+ sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min);
+ SetDlgItemText(hWnd, IDC_UTC, tim);
+
+ tm = localtime(&now);
+ sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min);
+ SetDlgItemText(hWnd, IDC_LOCAL, tim);
+
+
+ return 0;
+}
+
+#define MAX_PENDING_CONNECTS 4
+
+#define VERSION_MAJOR 2
+#define VERSION_MINOR 0
+
+static SOCKADDR_IN local_sin; /* Local socket - internet style */
+
+static PSOCKADDR_IN psin;
+
+SOCKET sock;
+
+void GetRestartData();
+
+BOOL Initialise()
+{
+ int i, len;
+ ConnectionInfo * conn;
+ struct UserInfo * user = NULL;
+ HKEY hKey=0;
+ char * ptr1;
+ int Attrs, ret;
+ char msg[500];
+ TIME_ZONE_INFORMATION TimeZoneInformation;
+ struct stat STAT;
+
+ GetTimeZoneInformation(&TimeZoneInformation);
+
+ Debugprintf("%d", sizeof(struct MsgInfo));
+
+ _tzset();
+ _MYTIMEZONE = timezone;
+ _MYTIMEZONE = TimeZoneInformation.Bias * 60;
+
+ // Register message for posting by BPQDLL
+
+ BPQMsg = RegisterWindowMessage(BPQWinMsg);
+
+ // See if we need to warn of possible problem with BaseDir moved by installer
+
+ strcpy(BPQDirectory, GetBPQDirectory());
+
+ sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory);
+
+ len = strlen(BaseDir);
+ ptr1 = BaseDir;
+
+ while (*ptr1)
+ {
+ if (*(ptr1) == '/') *(ptr1) = '\\';
+ ptr1++;
+ }
+
+ // Make Sure BASEDIR Exists
+
+ Attrs = GetFileAttributes(BaseDir);
+
+ if (Attrs == -1)
+ {
+ sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir);
+ ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO);
+
+ if (ret == IDYES)
+ {
+ ret = CreateDirectory(BaseDir, NULL);
+ if (ret == 0)
+ {
+ MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP);
+ return FALSE;
+ }
+ }
+ else
+ {
+ MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir);
+ ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP);
+
+ return FALSE;
+ }
+ }
+
+ initUTF8();
+
+ // Set up file and directory names
+
+ strcpy(UserDatabasePath, BaseDir);
+ strcat(UserDatabasePath, "\\");
+ strcat(UserDatabasePath, UserDatabaseName);
+
+ strcpy(MsgDatabasePath, BaseDir);
+ strcat(MsgDatabasePath, "\\");
+ strcat(MsgDatabasePath, MsgDatabaseName);
+
+ strcpy(BIDDatabasePath, BaseDir);
+ strcat(BIDDatabasePath, "\\");
+ strcat(BIDDatabasePath, BIDDatabaseName);
+
+ strcpy(WPDatabasePath, BaseDir);
+ strcat(WPDatabasePath, "\\");
+ strcat(WPDatabasePath, WPDatabaseName);
+
+ strcpy(BadWordsPath, BaseDir);
+ strcat(BadWordsPath, "\\");
+ strcat(BadWordsPath, BadWordsName);
+
+ strcpy(NTSAliasesPath, BaseDir);
+ strcat(NTSAliasesPath, "/");
+ strcat(NTSAliasesPath, NTSAliasesName);
+
+ strcpy(MailDir, BaseDir);
+ strcat(MailDir, "\\");
+ strcat(MailDir, "Mail");
+
+ CreateDirectory(MailDir, NULL); // Just in case
+
+ strcpy(ConfigName, BaseDir);
+ strcat(ConfigName, "\\");
+ strcat(ConfigName, "BPQMail.cfg");
+
+ UsingingRegConfig = FALSE;
+
+ // if config file exists use it else try to get from Registry
+
+ if (stat(ConfigName, &STAT) == -1)
+ {
+ UsingingRegConfig = TRUE;
+
+ if (GetConfigFromRegistry())
+ {
+ SaveConfig(ConfigName);
+ }
+ else
+ {
+ int retCode;
+
+ strcpy(BBSName, GetNodeCall());
+ strlop(BBSName, '-');
+ strlop(BBSName, ' ');
+
+ sprintf(msg, "No configuration found - Dummy Config created");
+
+ retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL);
+
+ if (retCode == IDCANCEL)
+ return FALSE;
+
+ SaveConfig(ConfigName);
+ }
+ }
+
+ if (GetConfig(ConfigName) == EXIT_FAILURE)
+ {
+ ret = MessageBox(NULL,
+ "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP);
+ return FALSE;
+ }
+
+ // Got a Config File
+
+ if (MainRect.right < 100 || MainRect.bottom < 100)
+ {
+ GetWindowRect(MainWnd, &MainRect);
+ }
+
+ MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE);
+
+ if (OpenMon)
+ CreateMonitor();
+
+ BBSApplMask = 1<<(BBSApplNum-1);
+
+ ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE);
+ ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE);
+ ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE);
+
+ // Make backup copies of Databases
+
+ CopyBIDDatabase();
+ CopyMessageDatabase();
+ CopyUserDatabase();
+ CopyWPDatabase();
+
+ SetupMyHA();
+ SetupFwdAliases();
+ SetupNTSAliases(NTSAliasesPath);
+
+ GetWPDatabase();
+ GetMessageDatabase();
+ GetUserDatabase();
+ GetBIDDatabase();
+ GetBadWordFile();
+ GetHTMLForms();
+
+ GetRestartData();
+
+ UsingingRegConfig = FALSE;
+
+ // Make sure SYSOPCALL is set
+
+ if (SYSOPCall[0] == 0)
+ strcpy(SYSOPCall, BBSName);
+
+ // Make sure there is a user record for the BBS, with BBS bit set.
+
+ user = LookupCall(BBSName);
+
+ if (user == NULL)
+ {
+ user = AllocateUserRecord(BBSName);
+ user->Temp = zalloc(sizeof (struct TempUserInfo));
+ }
+
+ if ((user->flags & F_BBS) == 0)
+ {
+ // Not Defined as a BBS
+
+ if (SetupNewBBS(user))
+ user->flags |= F_BBS;
+ }
+
+ // if forwarding AMPR mail make sure User/BBS AMPR exists
+
+ if (SendAMPRDirect)
+ {
+ BOOL NeedSave = FALSE;
+
+ user = LookupCall("AMPR");
+
+ if (user == NULL)
+ {
+ user = AllocateUserRecord("AMPR");
+ user->Temp = zalloc(sizeof (struct TempUserInfo));
+ NeedSave = TRUE;
+ }
+
+ if ((user->flags & F_BBS) == 0)
+ {
+ // Not Defined as a BBS
+
+ if (SetupNewBBS(user))
+ user->flags |= F_BBS;
+ NeedSave = TRUE;
+ }
+
+ if (NeedSave)
+ SaveUserDatabase();
+ }
+
+ // Allocate Streams
+
+ for (i=0; i < MaxStreams; i++)
+ {
+ conn = &Connections[i];
+ conn->BPQStream = FindFreeStream();
+
+ if (conn->BPQStream == 255) break;
+
+ NumberofStreams++;
+
+ BPQSetHandle(conn->BPQStream, hWnd);
+
+ SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask);
+ Disconnect(conn->BPQStream);
+ }
+
+ InitialiseTCP();
+
+ InitialiseNNTP();
+
+ SetupListenSet(); // Master set of listening sockets
+
+ if (BBSApplNum)
+ {
+ SetupUIInterface();
+ if (MailForInterval)
+ _beginthread(SendMailForThread, 0, 0);
+ }
+
+ if (cfgMinToTray)
+ {
+ AddTrayMenuItem(MainWnd, "Mail Server");
+ }
+
+ SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs)
+ SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms)
+ SetTimer(hWnd,3,2000,NULL); // Forward Check (2 secs)
+
+ // Calulate time to run Housekeeping
+ {
+ struct tm *tm;
+ time_t now;
+
+ now = time(NULL);
+
+ tm = gmtime(&now);
+
+ tm->tm_hour = MaintTime / 100;
+ tm->tm_min = MaintTime % 100;
+ tm->tm_sec = 0;
+
+ MaintClock = _mkgmtime(tm);
+
+ while (MaintClock < now)
+ MaintClock += MaintInterval * 3600;
+
+ Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now));
+
+ if (LastHouseKeepingTime)
+ {
+ if ((now - LastHouseKeepingTime) > MaintInterval * 3600)
+ {
+ DoHouseKeeping(FALSE);
+ }
+ }
+ }
+
+ if (strstr(CmdLine, "tidymail"))
+ DeleteRedundantMessages();
+
+ if (strstr(CmdLine, "nohomebbs"))
+ DontNeedHomeBBS = TRUE;
+
+ if (strstr(CmdLine, "DontCheckFromCall"))
+ DontCheckFromCall = TRUE;
+
+ CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED);
+
+ RefreshMainWindow();
+
+// CreateWPReport();
+
+ CreatePipeThread();
+ GetPGConfig();
+
+ APIClock = 0;
+
+ return TRUE;
+}
+
+int ConnectState(Stream)
+{
+ int state;
+
+ SessionStateNoAck(Stream, &state);
+ return state;
+}
+UCHAR * EncodeCall(UCHAR * Call)
+{
+ static char axcall[10];
+
+ ConvToAX25(Call, axcall);
+ return &axcall[0];
+
+}
+
+/*
+VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo)
+{
+ struct UserInfo * user;
+
+ int i = FWDInfo->UserIndex;
+
+ if (i == -1)
+ {
+ FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users
+ }
+
+ for (i++; i <= NumberofUsers; i++)
+ {
+ user = UserRecPtr[i];
+
+ if (user->flags & F_POLLRMS)
+ {
+ FWDInfo->UserIndex = i;
+ strcpy(FWDInfo->UserCall, user->Call);
+ FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20;
+ return ;
+ }
+ }
+
+ // Finished Scan
+
+ FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0;
+}
+*/
+
+#ifndef NEWROUTING
+
+VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo)
+{
+}
+VOID SetupMyHA()
+{
+}
+VOID SetupFwdAliases()
+{
+}
+
+int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn)
+{
+ struct UserInfo * bbs;
+ struct BBSForwardingInfo * ForwardingInfo;
+ char ATBBS[41];
+ char * HRoute;
+ int Count =0;
+
+ strcpy(ATBBS, Msg->via);
+ HRoute = strlop(ATBBS, '.');
+
+ if (Msg->type == 'P')
+ {
+ // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA
+
+ for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
+ {
+ ForwardingInfo = bbs->ForwardingInfo;
+
+ if (CheckBBSToList(Msg, bbs, ForwardingInfo))
+ {
+ if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here!
+ {
+ if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back
+ {
+ set_fwd_bit(Msg->fbbs, bbs->BBSNumber);
+ ForwardingInfo->MsgCount++;
+ }
+ }
+ return 1;
+ }
+ }
+
+ for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
+ {
+ ForwardingInfo = bbs->ForwardingInfo;
+
+ if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS))
+ {
+ if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here!
+ {
+ if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back
+ {
+ set_fwd_bit(Msg->fbbs, bbs->BBSNumber);
+ ForwardingInfo->MsgCount++;
+ }
+ }
+ return 1;
+ }
+ }
+
+ for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
+ {
+ ForwardingInfo = bbs->ForwardingInfo;
+
+ if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute))
+ {
+ if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here!
+ {
+ if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back
+ {
+ set_fwd_bit(Msg->fbbs, bbs->BBSNumber);
+ ForwardingInfo->MsgCount++;
+ }
+ }
+ return 1;
+ }
+ }
+
+ return FALSE;
+ }
+
+ // Bulls go to all matching BBSs, so the order of checking doesn't matter
+
+ for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
+ {
+ ForwardingInfo = bbs->ForwardingInfo;
+
+ if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute))
+ {
+ if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here!
+ {
+ if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back
+ {
+ set_fwd_bit(Msg->fbbs, bbs->BBSNumber);
+ ForwardingInfo->MsgCount++;
+ }
+ }
+ Count++;
+ }
+ }
+
+ return Count;
+}
+BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute)
+{
+ char ** Calls;
+ char ** HRoutes;
+ int i, j;
+
+ if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS
+ return TRUE;
+
+ // Check TO distributions
+
+ if (ForwardingInfo->TOCalls)
+ {
+ Calls = ForwardingInfo->TOCalls;
+
+ while(Calls[0])
+ {
+ if (strcmp(Calls[0], Msg->to) == 0)
+ return TRUE;
+
+ Calls++;
+ }
+ }
+
+ // Check AT distributions
+
+ if (ForwardingInfo->ATCalls)
+ {
+ Calls = ForwardingInfo->ATCalls;
+
+ while(Calls[0])
+ {
+ if (strcmp(Calls[0], ATBBS) == 0)
+ return TRUE;
+
+ Calls++;
+ }
+ }
+ if ((HRoute) && (ForwardingInfo->Haddresses))
+ {
+ // Match on Routes
+
+ HRoutes = ForwardingInfo->Haddresses;
+
+ while(HRoutes[0])
+ {
+ i = strlen(HRoutes[0]) - 1;
+ j = strlen(HRoute) - 1;
+
+ while ((i >= 0) && (j >= 0)) // Until one string rus out
+ {
+ if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards
+ goto next;
+ }
+
+ return TRUE;
+ next:
+ HRoutes++;
+ }
+ }
+
+
+ return FALSE;
+
+}
+
+BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo)
+{
+ char ** Calls;
+
+ // Check TO distributions
+
+ if (ForwardingInfo->TOCalls)
+ {
+ Calls = ForwardingInfo->TOCalls;
+
+ while(Calls[0])
+ {
+ if (strcmp(Calls[0], Msg->to) == 0)
+ return TRUE;
+
+ Calls++;
+ }
+ }
+ return FALSE;
+}
+
+BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS)
+{
+ char ** Calls;
+
+ // Check AT distributions
+
+ if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS
+ return TRUE;
+
+ if (ForwardingInfo->ATCalls)
+ {
+ Calls = ForwardingInfo->ATCalls;
+
+ while(Calls[0])
+ {
+ if (strcmp(Calls[0], ATBBS) == 0)
+ return TRUE;
+
+ Calls++;
+ }
+ }
+ return FALSE;
+}
+
+BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute)
+{
+ char ** HRoutes;
+ int i, j;
+
+ if ((HRoute) && (ForwardingInfo->Haddresses))
+ {
+ // Match on Routes
+
+ HRoutes = ForwardingInfo->Haddresses;
+
+ while(HRoutes[0])
+ {
+ i = strlen(HRoutes[0]) - 1;
+ j = strlen(HRoute) - 1;
+
+ while ((i >= 0) && (j >= 0)) // Until one string rus out
+ {
+ if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards
+ goto next;
+ }
+
+ return TRUE;
+ next:
+ HRoutes++;
+ }
+ }
+ return FALSE;
+}
+
+#endif
+
+char * strlop(char * buf, char delim)
+{
+ // Terminate buf at delim, and return rest of string
+
+ char * ptr;
+
+ if (buf == NULL) return NULL; // Protect
+
+ ptr = strchr(buf, delim);
+
+ if (ptr == NULL) return NULL;
+
+ *(ptr)++=0;
+
+ return ptr;
+}
diff --git a/.svn/pristine/90/90a09ec7bae8af6d7cc5bf04448872bfcbeea6ef.svn-base b/.svn/pristine/90/90a09ec7bae8af6d7cc5bf04448872bfcbeea6ef.svn-base
new file mode 100644
index 0000000..604db8f
--- /dev/null
+++ b/.svn/pristine/90/90a09ec7bae8af6d7cc5bf04448872bfcbeea6ef.svn-base
@@ -0,0 +1,825 @@
+/*
+Copyright 2001-2018 John Wiseman G8BPQ
+
+This file is part of LinBPQ/BPQ32.
+
+LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+LinBPQ/BPQ32 is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
+*/
+
+
+#include "bpqchat.h"
+
+extern char OurNode[10];
+
+// Flags Equates
+
+#define GETTINGUSER 1
+#define GETTINGBBS 2
+#define CHATMODE 4
+#define GETTINGTITLE 8
+#define GETTINGMESSAGE 16
+#define CHATLINK 32 // Link to another Chat Node
+#define SENDTITLE 64
+#define SENDBODY 128
+#define WAITPROMPT 256 // Waiting for prompt after message
+
+
+extern char PassError[];
+extern char BusyError[];
+extern int chatPaclen;
+
+extern char NodeTail[];
+extern BOOL APRSApplConnected;
+
+extern char ChatConfigName[250];
+
+extern char OtherNodesList[1000];
+
+extern char ChatWelcomeMsg[1000];
+
+extern USER *user_hd;
+extern LINK *link_hd;
+
+extern UCHAR BPQDirectory[260];
+
+#define MaxSockets 64
+
+extern ChatCIRCUIT ChatConnections[MaxSockets+1];
+
+extern int NumberofChatStreams;
+
+extern int SMTPMsgs;
+
+extern int ChatApplNum;
+extern int MaxChatStreams;
+
+extern char Position[81];
+extern char PopupText[251];
+extern int PopupMode;
+extern int reportChatEvents;
+
+#include "httpconnectioninfo.h"
+
+static struct HTTPConnectionInfo * SessionList; // active bbs config sessions
+
+static struct HTTPConnectionInfo * AllocateSession(char Appl);
+static struct HTTPConnectionInfo * FindSession(char * Key);
+VOID ProcessUserUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID ProcessMsgFwdUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key);
+VOID ProcessConfUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID ProcessUIUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID SendUserSelectPage(char * Reply, int * ReplyLen, char * Key);
+VOID SendFWDSelectPage(char * Reply, int * ReplyLen, char * Key);
+int EncryptPass(char * Pass, char * Encrypt);
+VOID ProcessFWDUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID SendStatusPage(char * Reply, int * ReplyLen, char * Key);
+VOID SendChatStatusPage(char * Reply, int * ReplyLen, char * Key);
+VOID SendUIPage(char * Reply, int * ReplyLen, char * Key);
+static VOID GetParam(char * input, char * key, char * value);
+BOOL GetConfig(char * ConfigName);
+VOID ProcessChatDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+int APIENTRY SessionControl(int stream, int command, int param);
+int APIENTRY GetNumberofPorts();
+int APIENTRY GetPortNumber(int portslot);
+UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc);
+struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot);
+VOID SendHouseKeeping(char * Reply, int * ReplyLen, char * Key);
+VOID SendWelcomePage(char * Reply, int * ReplyLen, char * Key);
+VOID SaveWelcome(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
+VOID GetMallocedParam(char * input, char * key, char ** value);
+VOID SaveMessageText(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID SaveHousekeeping(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
+VOID SaveWP(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
+int SetupNodeMenu(char * Buff);
+VOID SendFwdSelectPage(char * Reply, int * ReplyLen, char * Key);
+VOID SendFwdDetails(struct HTTPConnectionInfo * Session, char * Reply, int * ReplyLen, char * Key);
+VOID SendFwdMainPage(char * Reply, int * ReplyLen, char * Key);
+VOID SaveFwdCommon(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID SaveFwdDetails(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+char ** SeparateMultiString(char * MultiString);
+VOID SendChatConfigPage(char * Reply, int * ReplyLen, char * Key);
+VOID SaveChatInfo(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
+int rtlink (char * Call);
+char * APIENTRY GetBPQDirectory();
+VOID SaveChatConfigFile(char * Config);
+BOOL GetChatConfig(char * Config);
+char * GetTemplateFromFile(int Version, char * FN);
+
+static char UNC[] = "";
+static char CHKD[] = "checked=checked ";
+static char sel[] = "selected";
+
+
+char ChatSignon[] = "BPQ32 Chat Server Access"
+ "BPQ32 Chat Server %s Access
"
+ "Please enter Callsign and Password to access the Chat Server
"
+ "";
+
+
+char ChatPage[] = "%s's Chat Server"
+ "BPQ32 Chat Node %s
"
+ "
";
+
+
+
+static char LostSession[] = ""
+"";
+
+char * ChatConfigTemplate = NULL;
+char * ChatStatusTemplate = NULL;
+
+static int compare(const void *arg1, const void *arg2)
+{
+ // Compare Calls. Fortunately call is at start of stuct
+
+ return _stricmp(*(char**)arg1 , *(char**)arg2);
+}
+
+int SendChatHeader(char * Reply, char * Key)
+{
+ return sprintf(Reply, ChatPage, OurNode, OurNode, Key, Key);
+}
+
+
+void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen)
+{
+ char * Conxtext = 0, * NodeURL;
+ int ReplyLen;
+ char * Key;
+ char Appl = 'M';
+
+ NodeURL = strtok_s(URL, "?", &Conxtext);
+ Key = Session->Key;
+
+
+ if (strcmp(Method, "POST") == 0)
+ {
+ if (_stricmp(NodeURL, "/Chat/Header") == 0)
+ {
+ *RLen = SendChatHeader(Reply, Session->Key);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/Chat/ChatConfig") == 0)
+ {
+ if (ChatConfigTemplate)
+ free(ChatConfigTemplate);
+
+ ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt");
+
+ NodeURL[strlen(NodeURL)] = ' '; // Undo strtok
+ SaveChatInfo(Session, input, Reply, RLen, Key);
+ return ;
+ }
+
+ if (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0)
+ {
+ ProcessChatDisUser(Session, input, Reply, RLen, Key);
+ return ;
+ }
+
+
+ // End of POST section
+ }
+
+ if ((_stricmp(NodeURL, "/chat/Chat.html") == 0) || (_stricmp(NodeURL, "/chat/Header") == 0))
+ {
+ *RLen = SendChatHeader(Reply, Session->Key);
+ return;
+ }
+
+ if ((_stricmp(NodeURL, "/Chat/ChatStatus") == 0) || (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0))
+ {
+ if (ChatStatusTemplate)
+ free(ChatStatusTemplate);
+
+ ChatStatusTemplate = GetTemplateFromFile(1, "ChatStatus.txt");
+ SendChatStatusPage(Reply, RLen, Key);
+
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/Chat/ChatConf") == 0)
+ {
+ if (ChatConfigTemplate)
+ free(ChatConfigTemplate);
+
+ ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt");
+
+ SendChatConfigPage(Reply, RLen, Key);
+ return;
+ }
+
+ ReplyLen = sprintf(Reply, ChatSignon, OurNode, OurNode);
+ *RLen = ReplyLen;
+
+}
+
+
+static VOID GetParam(char * input, char * key, char * value)
+{
+ char * ptr = strstr(input, key);
+ char Param[2048];
+ char * ptr1, * ptr2;
+ char c;
+
+ if (ptr)
+ {
+ ptr2 = strchr(ptr, '&');
+ if (ptr2) *ptr2 = 0;
+ strcpy(Param, ptr + strlen(key));
+ if (ptr2) *ptr2 = '&'; // Restore string
+
+ // Undo any % transparency
+
+ ptr1 = Param;
+ ptr2 = Param;
+
+ c = *(ptr1++);
+
+ while (c)
+ {
+ if (c == '%')
+ {
+ int n;
+ int m = *(ptr1++) - '0';
+ if (m > 9) m = m - 7;
+ n = *(ptr1++) - '0';
+ if (n > 9) n = n - 7;
+
+ *(ptr2++) = m * 16 + n;
+ }
+ else if (c == '+')
+ *(ptr2++) = ' ';
+ else
+ *(ptr2++) = c;
+
+ c = *(ptr1++);
+ }
+
+ *(ptr2++) = 0;
+
+ strcpy(value, Param);
+ }
+}
+
+static VOID GetCheckBox(char * input, char * key, int * value)
+{
+ char * ptr = strstr(input, key);
+ if (ptr)
+ *value = 1;
+ else
+ *value = 0;
+}
+
+
+VOID SaveChatInfo(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key)
+{
+ int ReplyLen = 0;
+ char * input;
+ struct UserInfo * USER = NULL;
+ char Temp[80];
+ char Nodes[10000] = "";
+ char * ptr1, * ptr2;
+
+ input = strstr(MsgPtr, "\r\n\r\n"); // End of headers
+
+ if (input)
+ {
+ if (strstr(input, "Cancel=Cancel"))
+ {
+ *RLen = SendChatHeader(Reply, Session->Key);
+ return;
+ }
+
+
+ GetParam(input, "ApplNum=", Temp);
+ ChatApplNum = atoi(Temp);
+ GetParam(input, "Streams=", Temp);
+ MaxChatStreams = atoi(Temp);
+ GetParam(input, "Paclen=", Temp);
+ chatPaclen = atoi(Temp);
+ if (chatPaclen < 60)
+ chatPaclen = 60;
+
+ GetCheckBox(input, "Events=", &reportChatEvents);
+
+ GetParam(input, "nodes=", Nodes);
+
+ ptr1 = Nodes;
+ ptr2 = OtherNodesList;
+
+ // Now we just save with crlf in place
+
+ strcpy(OtherNodesList, Nodes);
+ /*
+ while (*ptr1)
+ {
+ if ((*ptr1) == 13)
+ {
+ *(ptr2++) = ' ';
+ ptr1 += 2;
+ }
+ else
+ *(ptr2++) = *(ptr1++);
+ }
+ *ptr2 = 0;
+*/
+ GetParam(input, "Posn=", Position);
+ GetParam(input, "MapText=", PopupText);
+ GetParam(input, "welcome=", ChatWelcomeMsg);
+
+ // Replace cr lf in string with $W
+
+ ptr1 = ChatWelcomeMsg;
+
+ scan2:
+
+ ptr1 = strstr(ptr1, "\r\n");
+
+ if (ptr1)
+ {
+ *(ptr1++)='$'; // put in cr
+ *(ptr1++)='W'; // put in lf
+
+ goto scan2;
+ }
+
+ GetCheckBox(input, "PopType=Click", &PopupMode);
+
+ if (strstr(input, "Restart=Restart+Links"))
+ {
+ char * ptr1, * ptr2, * Context;
+
+ node_close();
+
+ Sleep(2);
+
+ // Dont call removelinks - they may still be attached to a circuit. Just clear header
+
+ link_hd = NULL;
+
+ // Set up other nodes list. rtlink messes with the string so pass copy
+
+ ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context);
+
+ while (ptr1)
+ {
+ rtlink(ptr1);
+ ptr1 = strtok_s(NULL, "\r\n", &Context);
+ }
+
+ free(ptr2);
+
+ if (user_hd) // Any Users?
+ makelinks(); // Bring up links
+ }
+
+ if (strstr(input, "UpdateMap=Update+Map"))
+ {
+ char Msg[500];
+ int len;
+
+ len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode);
+
+ if (len < 256)
+ Send_MON_Datagram(Msg, len);
+
+ }
+ SaveChatConfigFile(ChatConfigName);
+ GetChatConfig(ChatConfigName);
+ }
+
+ SendChatConfigPage(Reply, RLen, Key);
+ return;
+}
+
+VOID ProcessChatDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest)
+{
+ char * input;
+ char * ptr;
+
+ input = strstr(MsgPtr, "\r\n\r\n"); // End of headers
+
+ if (input)
+ {
+ ptr = strstr(input, "Stream=");
+ if (ptr)
+ {
+ int Stream = atoi(ptr + 7);
+ SessionControl(Stream, 2, 0);
+ }
+ }
+ SendChatStatusPage(Reply, RLen, Rest);
+}
+
+VOID SendChatConfigPage(char * Reply, int * ReplyLen, char * Key)
+{
+ int Len;
+ char Nodes[10000];
+ char Text[1000];
+ char * ptr1, * ptr2;
+
+ // Replace spaces in Node List with CR/LF
+
+ ptr1 = OtherNodesList;
+ ptr2 = Nodes;
+
+ if (strchr(OtherNodesList, 13)) // New format maybe with connect scripts
+ {
+ // OtherNodesList alresdy has crlf
+
+ strcpy(Nodes, OtherNodesList);
+ }
+ else
+ {
+ while (*ptr1)
+ {
+ if ((*ptr1) == ' ')
+ {
+ *(ptr2++) = 13;
+ *(ptr2++) = 10;
+ ptr1++ ;
+ }
+ else
+ *(ptr2++) = *(ptr1++);
+ }
+
+ *ptr2 = 0;
+ }
+
+ // Replace " in Text with "
+
+ ptr1 = PopupText;
+ ptr2 = Text;
+
+ while (*ptr1)
+ {
+ if ((*ptr1) == '"')
+ {
+ *(ptr2++) = '&';
+ *(ptr2++) = 'q';
+ *(ptr2++) = 'u';
+ *(ptr2++) = 'o';
+ *(ptr2++) = 't';
+ *(ptr2++) = ';';
+ ptr1++ ;
+ }
+ else
+ *(ptr2++) = *(ptr1++);
+ }
+
+ *ptr2 = 0;
+
+ // Replace $W in Welcome Message with cr lf
+
+ ptr2 = ptr1 = _strdup(ChatWelcomeMsg);
+
+scan:
+
+ ptr1 = strstr(ptr1, "$W");
+
+ if (ptr1)
+ {
+ *(ptr1++)=13; // put in cr
+ *(ptr1++)=10; // put in lf
+
+ goto scan;
+ }
+
+ Len = sprintf(Reply, ChatConfigTemplate,
+ OurNode, Key, Key, Key,
+ ChatApplNum, MaxChatStreams,
+ (reportChatEvents) ? CHKD : UNC,
+ Nodes, chatPaclen, Position,
+ (PopupMode) ? UNC : CHKD,
+ (PopupMode) ? CHKD : UNC, Text, ptr2);
+
+ free(ptr2);
+
+ *ReplyLen = Len;
+}
+
+VOID SendChatStatusPage(char * Reply, int * ReplyLen, char * Key)
+{
+ int Len = 0;
+ USER *user;
+ char * Alias;
+ char * Topic;
+ LINK *link;
+
+ char Streams[65536];
+ char Users[65536];
+ char Links[65536];
+
+ ChatCIRCUIT * conn;
+ int i = 0, n;
+
+ Users[0] = 0;
+
+ for (user = user_hd; user; user = user->next)
+ {
+ if ((user->node == 0) || (user->node->alias == 0))
+ Alias = "(Corrupt Alias)";
+ else
+ Alias = user->node->alias;
+
+ if ((user->topic == 0) || (user->topic->name == 0))
+ Topic = "(Corrupt Topic)";
+ else
+ Topic = user->topic->name;
+
+ Len += sprintf(&Users[Len], "| %s | %s | %s | %s | %d | %s |
",
+ user->call, Alias, user->name, Topic, (int)(time(NULL) - user->lastrealmsgtime), user->qth);
+
+ }
+
+ Links[0] = 0;
+
+ Len = 0;
+
+ for (link = link_hd; link; link = link->next)
+ {
+ if (link->flags & p_linked )
+ if (link->supportsPolls)
+ Len += sprintf(&Links[Len], "| %s | Open   RTT %d |
", link->call, link->RTT);
+ else
+ Len += sprintf(&Links[Len], "| %s | Open |
", link->call);
+ else if (link->flags & (p_linked | p_linkini))
+ Len += sprintf(&Links[Len], "| %s | Connecting |
", link->call);
+ else if (link->flags & p_linkfailed)
+ Len += sprintf(&Links[Len], "| %s | Connect failed |
", link->call);
+ else
+ Len += sprintf(&Links[Len], "| %s | Idle |
", link->call);
+ }
+
+ Len = 0;
+ Streams[0] = 0;
+
+ for (n = 0; n < NumberofChatStreams; n++)
+ {
+ conn=&ChatConnections[n];
+ i = conn->BPQStream;
+ if (!conn->Active)
+ {
+ Len += sprintf(&Streams[Len], "| Idle | | | | |
", i, i);
+ }
+ else
+ {
+ if (conn->Flags & CHATLINK)
+ {
+ if (conn->BPQStream > 64 || conn->u.link == 0 || conn->u.link->alias == 0)
+ Len += sprintf(&Streams[Len], "| ** Corrupt ChatLink ** | "
+ " | | | |
", i, i);
+ else
+ Len += sprintf(&Streams[Len], "| "
+ "%s | %s | %d | %s | %d |
",
+ i, i, "Chat Link", conn->u.link->alias, conn->BPQStream,
+ "", conn->OutputQueueLength - conn->OutputGetPointer);
+ }
+ else
+ if ((conn->Flags & CHATMODE) && conn->topic && conn->u.user && conn->u.user->call)
+ {
+ Len += sprintf(&Streams[Len], "| %s | %s | %d | %s | %d |
",
+ i, i, conn->u.user->name, conn->u.user->call, conn->BPQStream,
+ conn->topic->topic->name, conn->OutputQueueLength - conn->OutputGetPointer);
+ }
+ else
+ {
+ if (conn->UserPointer == 0)
+ Len += sprintf(&Streams[Len], "Logging in");
+ else
+ {
+ Len += sprintf(&Streams[Len], "| %s | %s | %d | %s | %d |
",
+ i, i, conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream,
+ "CHAT", conn->OutputQueueLength - conn->OutputGetPointer);
+ }
+ }
+ }
+ }
+
+ Len = sprintf(Reply, ChatStatusTemplate, OurNode, OurNode, Key, Key, Key, Streams, Users, Links);
+ *ReplyLen = Len;
+}
+
+
+static struct HTTPConnectionInfo * AllocateSession(char Appl)
+{
+ int KeyVal;
+ struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo));
+
+ if (Session == NULL)
+ return NULL;
+
+ KeyVal = (int)time(NULL);
+
+ sprintf(Session->Key, "%c%012X", Appl, KeyVal);
+
+ if (SessionList)
+ Session->Next = SessionList;
+
+ SessionList = Session;
+
+ return Session;
+}
+
+static struct HTTPConnectionInfo * FindSession(char * Key)
+{
+ struct HTTPConnectionInfo * Session = SessionList;
+
+ while (Session)
+ {
+ if (strcmp(Session->Key, Key) == 0)
+ return Session;
+
+ Session = Session->Next;
+ }
+
+ return NULL;
+}
+#ifdef WIN32
+
+static char PipeFileName[] = "\\\\.\\pipe\\BPQChatWebPipe";
+
+static DWORD WINAPI InstanceThread(LPVOID lpvParam)
+
+// This routine is a thread processing function to read from and reply to a client
+// via the open pipe connection passed from the main loop. Note this allows
+// the main loop to continue executing, potentially creating more threads of
+// of this procedure to run concurrently, depending on the number of incoming
+// client connections.
+{
+ DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
+ BOOL fSuccess = FALSE;
+ HANDLE hPipe = NULL;
+ char Buffer[4096];
+ char OutBuffer[100000];
+ char * MsgPtr;
+ int InputLen = 0;
+ int OutputLen = 0;
+ struct HTTPConnectionInfo Session;
+ char URL[4096];
+ char * Context, * Method;
+ int n;
+
+ char * ptr;
+
+// Debugprintf("InstanceThread created, receiving and processing messages.");
+
+// The thread's parameter is a handle to a pipe object instance.
+
+ hPipe = (HANDLE) lpvParam;
+
+ // Read client requests from the pipe. This simplistic code only allows messages
+ // up to BUFSIZE characters in length.
+
+ n = ReadFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL);
+ fSuccess = ReadFile(hPipe, Buffer, 4096, &InputLen, NULL);
+
+ if (!fSuccess || InputLen == 0)
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ Debugprintf("InstanceThread: client disconnected.", GetLastError());
+ else
+ Debugprintf("InstanceThread ReadFile failed, GLE=%d.", GetLastError());
+ }
+ else
+ {
+ Buffer[InputLen] = 0;
+
+ MsgPtr = &Buffer[0];
+
+ strcpy(URL, MsgPtr);
+
+ ptr = strstr(URL, " HTTP");
+
+ if (ptr)
+ *ptr = 0;
+
+ Method = strtok_s(URL, " ", &Context);
+
+ ProcessChatHTTPMessage(&Session, Method, Context, MsgPtr, OutBuffer, &OutputLen);
+
+ WriteFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL);
+ WriteFile(hPipe, OutBuffer, OutputLen, &cbWritten, NULL);
+
+ FlushFileBuffers(hPipe);
+ DisconnectNamedPipe(hPipe);
+ CloseHandle(hPipe);
+ }
+ return 1;
+}
+
+static DWORD WINAPI PipeThreadProc(LPVOID lpvParam)
+{
+ BOOL fConnected = FALSE;
+ DWORD dwThreadId = 0;
+ HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
+
+// The main loop creates an instance of the named pipe and
+// then waits for a client to connect to it. When the client
+// connects, a thread is created to handle communications
+// with that client, and this loop is free to wait for the
+// next client connect request. It is an infinite loop.
+
+ for (;;)
+ {
+ hPipe = CreateNamedPipe(
+ PipeFileName, // pipe name
+ PIPE_ACCESS_DUPLEX, // read/write access
+ PIPE_TYPE_BYTE | // message type pipe
+ PIPE_WAIT, // blocking mode
+ PIPE_UNLIMITED_INSTANCES, // max. instances
+ 4096, // output buffer size
+ 4096, // input buffer size
+ 0, // client time-out
+ NULL); // default security attribute
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ Debugprintf("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
+ return -1;
+ }
+
+ // Wait for the client to connect; if it succeeds,
+ // the function returns a nonzero value. If the function
+ // returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
+
+ fConnected = ConnectNamedPipe(hPipe, NULL) ?
+ TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
+
+ if (fConnected)
+ {
+ // Create a thread for this client.
+
+ hThread = CreateThread(
+ NULL, // no security attribute
+ 0, // default stack size
+ InstanceThread, // thread proc
+ (LPVOID) hPipe, // thread parameter
+ 0, // not suspended
+ &dwThreadId); // returns thread ID
+
+ if (hThread == NULL)
+ {
+ Debugprintf("CreateThread failed, GLE=%d.\n", GetLastError());
+ return -1;
+ }
+ else CloseHandle(hThread);
+ }
+ else
+ // The client could not connect, so close the pipe.
+ CloseHandle(hPipe);
+ }
+
+ return 0;
+}
+
+BOOL CreateChatPipeThread()
+{
+ DWORD ThreadId;
+ CreateThread(NULL, 0, PipeThreadProc, 0, 0, &ThreadId);
+ return TRUE;
+}
+
+static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+
+static VOID FormatTime(char * Time, time_t cTime)
+{
+ struct tm * TM;
+ TM = gmtime(&cTime);
+
+ sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon],
+ TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec);
+
+}
+
+#endif
+
+
+
+
diff --git a/.svn/pristine/9a/9a51b1dceece14e8c1ac8486bc403a84d9d8f8d2.svn-base b/.svn/pristine/9a/9a51b1dceece14e8c1ac8486bc403a84d9d8f8d2.svn-base
new file mode 100644
index 0000000..9c0032d
--- /dev/null
+++ b/.svn/pristine/9a/9a51b1dceece14e8c1ac8486bc403a84d9d8f8d2.svn-base
@@ -0,0 +1,5678 @@
+/*
+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;
+
+extern int PoolBuilt;
+
+
+// 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("Releasebuffer Loop searching free chain - pointer = %p %p from %s Line %d", debug, pointer, File, Line);
+ return 0;
+ }
+ }
+
+ pointer = FREE_Q;
+
+ *BUFF = pointer;
+
+ FREE_Q = BUFF;
+
+ QCOUNT++;
+
+ if (PoolBuilt && QCOUNT > MAXBUFFS)
+ {
+ Debugprintf("Releasebuffer QCOUNT > MAXBUFFS - pointer = %p from %s Line %d", pointer, File, Line);
+ return 0;
+ }
+ 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;
+}
+
+DllExport int APIENTRY GetPortHardwareType(struct PORTCONTROL *PORT)
+{
+ if (PORT)
+ return PORT->Hardware;
+
+ return 0;
+}
+
+
+DllExport 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/a7/a780a3d27d90c9501bad9d7d68500e6575c6a013.svn-base b/.svn/pristine/a7/a780a3d27d90c9501bad9d7d68500e6575c6a013.svn-base
new file mode 100644
index 0000000..2c97f84
--- /dev/null
+++ b/.svn/pristine/a7/a780a3d27d90c9501bad9d7d68500e6575c6a013.svn-base
@@ -0,0 +1,6508 @@
+/*
+Copyright 2001-2018 John Wiseman G8BPQ
+
+This file is part of LinBPQ/BPQ32.
+
+LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+LinBPQ/BPQ32 is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
+*/
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+#include "cheaders.h"
+#include "bpqmail.h"
+
+#define MAIL
+#include "httpconnectioninfo.h"
+
+#ifdef WIN32
+//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h"
+#else
+#include
+#include
+#endif
+
+static struct HTTPConnectionInfo * FindSession(char * Key);
+int APIENTRY SessionControl(int stream, int command, int param);
+int SetupNodeMenu(char * Buff);
+VOID SetMultiStringValue(char ** values, char * Multi);
+char * GetTemplateFromFile(int Version, char * FN);
+VOID FormatTime(char * Time, time_t cTime);
+struct MsgInfo * GetMsgFromNumber(int msgno);
+BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP);
+BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg);
+int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen);
+struct HTTPConnectionInfo * AllocateWebMailSession();
+VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen);
+void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len);
+char *stristr (char *ch1, char *ch2);
+char * ReadTemplate(char * FormSet, char * DirName, char * FileName);
+VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile);
+BOOL CheckifPacket(char * Via);
+int GetHTMLFormSet(char * FormSet);
+void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen);
+char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End);
+struct HTTPConnectionInfo * FindWMSession(char * Key);
+int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert);
+char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys);
+char * FindXMLVariable(WebMailInfo * WebMail, char * Var);
+int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter);
+BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply);
+VOID UpdateFormAction(char * Template, char * Key);
+BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon);
+BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon);
+void FreeWebMailFields(WebMailInfo * WebMail);
+VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys);
+VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
+VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest);
+VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest);
+char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls);
+VOID FormatTime2(char * Time, time_t cTime);
+VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams);
+VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams);
+char * CheckFile(struct HtmlFormDir * Dir, char * FN);
+VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL);
+VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen);
+BOOL isAMPRMsg(char * Addr);
+char * doXMLTransparency(char * string);
+Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall);
+void SendMessageReadEvent(char * Call, struct MsgInfo * Msg);
+void SendNewMessageEvent(char * call, struct MsgInfo * Msg);
+void MQTTMessageEvent(void* message);
+
+extern char NodeTail[];
+extern char BBSName[10];
+
+extern char LTFROMString[2048];
+extern char LTTOString[2048];
+extern char LTATString[2048];
+
+ UCHAR BPQDirectory[260];
+
+int LineCount = 35; // Lines per page on message list
+
+// Forms
+
+struct HtmlFormDir ** HtmlFormDirs = NULL;
+int FormDirCount = 0;
+
+struct HtmlForm
+{
+ char * FileName;
+ BOOL HasInitial;
+ BOOL HasViewer;
+ BOOL HasReply;
+ BOOL HasReplyViewer;
+};
+
+struct HtmlFormDir
+{
+ char * FormSet;
+ char * DirName;
+ struct HtmlForm ** Forms;
+ int FormCount;
+ struct HtmlFormDir ** Dirs; // Nested Directories
+ int DirCount;
+};
+
+
+char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"};
+
+static char PassError[] = "Sorry, User or Password is invalid - please try again
";
+static char BusyError[] = "Sorry, No sessions available - please try later
";
+
+extern char MailSignon[];
+
+char WebMailSignon[] = "BPQ32 Mail Server Access"
+ "BPQ32 Mail Server %s Access
"
+ "Please enter Callsign and Password to access WebMail
"
+ "";
+
+static char MsgInputPage[] = ""
+ ""
+ ""
+ ""
+ ""
+ "Webmail Interface - Message Input Form
"
+ "";
+
+static char CheckFormMsgPage[] = ""
+ ""
+ ""
+ "Webmail Forms Interface - Check Message
"
+ "";
+
+
+extern char * WebMailTemplate;
+extern char * WebMailMsgTemplate;
+extern char * jsTemplate;
+
+static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"};
+
+static struct HTTPConnectionInfo * WebSessionList = NULL; // active WebMail sessions
+
+#ifdef LINBPQ
+UCHAR * GetBPQDirectory();
+#endif
+
+void UndoTransparency(char * input);
+
+#ifndef LINBPQ
+
+void UndoTransparency(char * input)
+{
+ char * ptr1, * ptr2;
+ char c;
+ int hex;
+
+ if (input == NULL)
+ return;
+
+ ptr1 = ptr2 = input;
+
+ // Convert any %xx constructs
+
+ while (1)
+ {
+ c = *(ptr1++);
+
+ if (c == 0)
+ break;
+
+ if (c == '%')
+ {
+ c = *(ptr1++);
+ if(isdigit(c))
+ hex = (c - '0') << 4;
+ else
+ hex = (tolower(c) - 'a' + 10) << 4;
+
+ c = *(ptr1++);
+ if(isdigit(c))
+ hex += (c - '0');
+ else
+ hex += (tolower(c) - 'a' + 10);
+
+ *(ptr2++) = hex;
+ }
+ else if (c == '+')
+ *(ptr2++) = 32;
+ else
+ *(ptr2++) = c;
+ }
+ *ptr2 = 0;
+}
+#endif
+
+void ReleaseWebMailStruct(WebMailInfo * WebMail)
+{
+ // release any malloc'ed resources
+
+ if (WebMail == NULL)
+ return;
+
+ FreeWebMailFields(WebMail);
+ free(WebMail);
+ return;
+}
+
+VOID FreeWebMailMallocs()
+{
+ // called when closing. Not really needed, but simplifies tracking down real memory leaks
+
+ struct HTTPConnectionInfo * Session, * SaveNext;
+ int i;
+ Session = WebSessionList;
+
+ while (Session)
+ {
+ SaveNext = Session->Next;
+
+ // Release amy malloc'ed resouces
+
+ ReleaseWebMailStruct(Session->WebMail);
+ free(Session);
+ Session = SaveNext;
+ }
+
+ for (i = 0; i < FormDirCount; i++)
+ {
+ struct HtmlFormDir * Dir = HtmlFormDirs[i];
+
+ int j;
+
+ for (j = 0; j < Dir->FormCount; j++)
+ {
+ free(Dir->Forms[j]->FileName);
+ free(Dir->Forms[j]);
+ }
+
+ if (Dir->DirCount)
+ {
+ struct HtmlFormDir * SubDir;
+
+ int k, l;
+
+ for (l = 0; l < Dir->DirCount; l++)
+ {
+ SubDir = Dir->Dirs[l];
+
+ for (k = 0; k < Dir->Dirs[l]->FormCount; k++)
+ {
+ free(SubDir->Forms[k]->FileName);
+ free(SubDir->Forms[k]);
+ }
+ free(SubDir->DirName);
+ free(SubDir->Forms);
+ free(SubDir->FormSet);
+
+ free(Dir->Dirs[l]);
+ }
+ }
+ free(Dir->DirName);
+ free(Dir->Forms);
+ free(Dir->FormSet);
+ free(Dir);
+ }
+
+ free(HtmlFormDirs);
+ return;
+}
+
+char * initMultipartUnpack(char ** Input)
+{
+ // Check if Multipart and return Boundary. Update Input to first part
+
+ // look through header for Content-Type line, and if multipart
+ // find boundary string.
+
+ char * ptr, * ptr2;
+ char Boundary[128];
+ BOOL Multipart = FALSE;
+
+ ptr = *Input;
+
+ while(*ptr != 13)
+ {
+ ptr2 = strchr(ptr, 10); // Find CR
+
+ while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line
+ ptr2 = strchr(&ptr2[1], 10); // Find CR
+
+ if (_memicmp(ptr, "Content-Type: ", 14) == 0)
+ {
+ char Line[256] = "";
+ char * ptr3;
+ size_t len = ptr2-ptr-14;
+
+ if (len >255)
+ return NULL;
+
+ memcpy(Line, &ptr[14], len);
+
+ if (_memicmp(Line, "Multipart/", 10) == 0)
+ {
+ ptr3 = stristr(Line, "boundary");
+ if (ptr3)
+ {
+ ptr3+=9;
+
+ if ((*ptr3) == '"')
+ ptr3++;
+
+ strcpy(Boundary, ptr3);
+ ptr3 = strchr(Boundary, '"');
+ if (ptr3) *ptr3 = 0;
+ ptr3 = strchr(Boundary, 13); // CR
+ if (ptr3) *ptr3 = 0;
+ break;
+ }
+ else
+ return NULL; // Can't do anything without a boundary ??
+ }
+ }
+ ptr = ptr2;
+ ptr++;
+ }
+
+ // Find First part - there is a boundary before it
+
+ ptr = strstr(ptr2, Boundary);
+
+ // Next should be crlf then part
+
+ ptr = strstr(ptr, "\r\n");
+
+ if (ptr)
+ ptr += 2; // Over CRLF
+
+ *Input = ptr; // Return first part or NULL
+ return _strdup(Boundary);
+}
+
+BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End)
+{
+ // Format seems to be
+/*
+ ------WebKitFormBoundaryABJaEbBWB5SuAHmq
+ Content-Disposition: form-data; name="Subj"
+
+ subj
+ ------WebKitFormBoundaryABJaEbBWB5SuAHmq
+ Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt"
+ Content-Type: text/plain
+
+ c:\exiftool "-filenameTo = _strdup(Value);
+ else if (strcmp(Name, "CC") == 0)
+ WebMail->CC = _strdup(Value);
+ else if (strcmp(Name, "Subj") == 0)
+ WebMail->Subject = _strdup(Value);
+ else if (strcmp(Name, "Type") == 0)
+ WebMail->Type = Value[0];
+ else if (strcmp(Name, "BID") == 0)
+ WebMail->BID = _strdup(Value);
+ else if (strcmp(Name, "Msg") == 0)
+ WebMail->Body = _strdup(Value);
+
+ else if (_memicmp(Name, "myFile[]", 8) == 0)
+ {
+ // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain
+
+ char * fn = strstr(Name, "filename=");
+ char * endfn;
+ if (fn)
+ {
+ fn += 10;
+
+ endfn = strchr(fn, '"');
+ if (endfn)
+ {
+ *endfn = 0;
+
+ if (strlen(fn))
+ {
+ WebMail->FileName[WebMail->Files] = _strdup(fn);
+ WebMail->FileBody[WebMail->Files] = malloc(ValLength);
+ memcpy(WebMail->FileBody[WebMail->Files], Value, ValLength);
+ WebMail->FileLen[WebMail->Files++] = ValLength;
+ }
+ }
+ }
+ }
+
+ else if (_memicmp(Name, "myFile2[]", 8) == 0)
+ {
+ // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain
+
+ char * fn = strstr(Name, "filename=");
+ char * endfn;
+ if (fn)
+ {
+ fn += 10;
+
+ endfn = strchr(fn, '"');
+ if (endfn)
+ {
+ *endfn = 0;
+
+ if (strlen(fn))
+ {
+ WebMail->Header = malloc(ValLength + 1);
+ memcpy(WebMail->Header, Value, ValLength + 1);
+ WebMail->HeaderLen = RemoveLF(WebMail->Header, ValLength);
+ }
+ }
+ }
+ }
+
+ else if (_memicmp(Name, "myFile3[]", 8) == 0)
+ {
+ // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain
+
+ char * fn = strstr(Name, "filename=");
+ char * endfn;
+ if (fn)
+ {
+ fn += 10;
+
+ endfn = strchr(fn, '"');
+ if (endfn)
+ {
+ *endfn = 0;
+
+ if (strlen(fn))
+ {
+ WebMail->Footer = malloc(ValLength + 1);
+ memcpy(WebMail->Footer, Value, ValLength + 1);
+ WebMail->FooterLen = RemoveLF(WebMail->Footer, ValLength);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+struct HTTPConnectionInfo * AllocateWebMailSession()
+{
+ int KeyVal;
+ struct HTTPConnectionInfo * Session, * SaveNext;
+ time_t NOW = time(NULL);
+
+ // First see if any session records havent been used for a while
+
+ Session = WebSessionList;
+
+ while (Session)
+ {
+ if (NOW - Session->WebMailLastUsed > 1200) // 20 Mins
+ {
+ SaveNext = Session->Next;
+
+ // Release amy malloc'ed resouces
+
+ ReleaseWebMailStruct(Session->WebMail);
+
+ memset(Session, 0, sizeof(struct HTTPConnectionInfo));
+
+ Session->Next = SaveNext;
+ goto UseThis;
+ }
+ Session = Session->Next;
+ }
+
+ Session = zalloc(sizeof(struct HTTPConnectionInfo));
+
+ if (Session == NULL)
+ return NULL;
+
+ if (WebSessionList)
+ Session->Next = WebSessionList;
+
+ WebSessionList = Session;
+
+UseThis:
+
+ Session->WebMail = zalloc(sizeof(WebMailInfo));
+
+ KeyVal = ((rand() % 100) + 1);
+
+ KeyVal *= (int)time(NULL);
+
+ sprintf(Session->Key, "%c%08X", 'W', KeyVal);
+
+ return Session;
+}
+
+struct HTTPConnectionInfo * FindWMSession(char * Key)
+{
+ struct HTTPConnectionInfo * Session = WebSessionList;
+
+ while (Session)
+ {
+ if (strcmp(Session->Key, Key) == 0)
+ {
+ Session->WebMailLastUsed = time(NULL);
+ return Session;
+ }
+ Session = Session->Next;
+ }
+
+ return NULL;
+}
+
+
+// Build list of available forms
+
+VOID ProcessFormDir(char * FormSet, char * DirName, struct HtmlFormDir *** xxx, int * DirCount)
+{
+ struct HtmlFormDir * FormDir;
+ struct HtmlFormDir ** FormDirs = *xxx;
+ struct HtmlForm * Form;
+ char Search[MAX_PATH];
+ int count = *DirCount;
+
+#ifdef WIN32
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA ffd;
+#else
+ DIR *dir;
+ struct dirent *entry;
+ char name[256];
+#endif
+
+ FormDir = zalloc(sizeof (struct HtmlFormDir));
+
+ FormDir->DirName = _strdup(DirName);
+ FormDir->FormSet = _strdup(FormSet);
+ FormDirs=realloc(FormDirs, (count + 1) * sizeof(void *));
+ FormDirs[count++] = FormDir;
+
+ *DirCount = count;
+ *xxx = FormDirs;
+
+
+ // Scan Directory for .txt files
+
+ sprintf(Search, "%s/%s/%s/*", GetBPQDirectory(), FormSet, DirName);
+
+ // Find the first file in the directory.
+
+#ifdef WIN32
+
+ hFind = FindFirstFile(Search, &ffd);
+
+ if (INVALID_HANDLE_VALUE == hFind)
+ return;
+
+ do
+ {
+ if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ char Dir[MAX_PATH];
+
+ if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
+ continue;
+
+ // Recurse in subdir
+
+ sprintf(Dir, "%s/%s", DirName, ffd.cFileName);
+
+ ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount);
+
+ continue;
+
+ }
+
+ // Add to list
+
+ Form = zalloc(sizeof (struct HtmlForm));
+
+ Form->FileName = _strdup(ffd.cFileName);
+
+ FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *));
+ FormDir->Forms[FormDir->FormCount++] = Form;
+ }
+
+ while (FindNextFile(hFind, &ffd) != 0);
+
+ FindClose(hFind);
+
+#else
+
+ sprintf(Search, "%s/%s/%s", GetBPQDirectory(), FormSet, DirName);
+
+ if (!(dir = opendir(Search)))
+ {
+ Debugprintf("%s %d %d", "cant open forms dir", errno, dir);
+ return ;
+ }
+ while ((entry = readdir(dir)) != NULL)
+ {
+ if (entry->d_type == DT_DIR)
+ {
+ char Dir[MAX_PATH];
+
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ // Recurse in subdir
+
+ sprintf(Dir, "%s/%s", DirName, entry->d_name);
+
+ ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount);
+ continue;
+ }
+
+ // Add to list
+
+ Form = zalloc(sizeof (struct HtmlForm));
+
+ Form->FileName = _strdup(entry->d_name);
+
+ FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *));
+ FormDir->Forms[FormDir->FormCount++] = Form;
+ }
+ closedir(dir);
+#endif
+ return;
+}
+
+int GetHTMLForms()
+{
+ int n = 0;
+
+ while (FormDirList[n][0])
+ GetHTMLFormSet(FormDirList[n++]);
+
+ return 0;
+}
+
+int GetHTMLFormSet(char * FormSet)
+{
+ int i;
+
+#ifdef WIN32
+
+ WIN32_FIND_DATA ffd;
+ char szDir[MAX_PATH];
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ DWORD dwError=0;
+
+ sprintf(szDir, "%s/%s/*", BPQDirectory, FormSet);
+
+ // Find the first file in the directory.
+
+ hFind = FindFirstFile(szDir, &ffd);
+
+ if (INVALID_HANDLE_VALUE == hFind)
+ {
+ // Accept either
+ return 0;
+ }
+
+ // Scan all directories looking for file
+
+ do
+ {
+ if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
+ continue;
+
+ // Add to Directory List
+
+ ProcessFormDir(FormSet, ffd.cFileName, &HtmlFormDirs, &FormDirCount);
+ }
+ }
+
+ while (FindNextFile(hFind, &ffd) != 0);
+
+ FindClose(hFind);
+
+#else
+
+ DIR *dir;
+ struct dirent *entry;
+ char name[256];
+
+ sprintf(name, "%s/%s", BPQDirectory, FormSet);
+
+ if (!(dir = opendir(name)))
+ {
+ Debugprintf("cant open forms dir %s %d %d", name, errno, dir);
+ }
+ else
+ {
+ while ((entry = readdir(dir)) != NULL)
+ {
+ if (entry->d_type == DT_DIR)
+ {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ // Add to Directory List
+
+ ProcessFormDir(FormSet, entry->d_name, &HtmlFormDirs, &FormDirCount);
+ }
+ }
+ closedir(dir);
+ }
+#endif
+
+ // List for testing
+
+ return 0;
+
+ Debugprintf("%d form dirs", FormDirCount);
+
+ for (i = 0; i < FormDirCount; i++)
+ {
+ struct HtmlFormDir * Dir = HtmlFormDirs[i];
+
+ int j;
+ Debugprintf("%3d %s", Dir->FormCount, Dir->DirName);
+
+ for (j = 0; j < Dir->FormCount; j++)
+ Debugprintf(" %s", Dir->Forms[j]->FileName);
+
+ if (Dir->DirCount)
+ {
+ int k, l;
+
+ for (l = 0; l < Dir->DirCount; l++)
+ {
+ Debugprintf("Subdir %3d %s", Dir->Dirs[l]->DirCount, Dir->Dirs[l]->DirName);
+ for (k = 0; k < Dir->Dirs[l]->FormCount; k++)
+ Debugprintf(" %s", Dir->Dirs[l]->Forms[k]->FileName);
+ }
+ }
+ }
+
+
+ return 0;
+}
+
+
+static int compare(const void *arg1, const void *arg2)
+{
+ // Compare Calls. Fortunately call is at start of stuct
+
+ return _stricmp(*(char**)arg1 , *(char**)arg2);
+}
+
+
+int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session)
+{
+ return SendWebMailHeaderEx(Reply, Key, Session, NULL);
+}
+
+
+int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert)
+{
+ // Ex includes an alert string to be sent before message
+
+ struct UserInfo * User = Session->User;
+ char Messages[245000];
+ int m;
+ struct MsgInfo * Msg;
+ char * ptr = Messages;
+ int n = NumberofMessages; //LineCount;
+ char Via[64];
+ int Count = 0;
+
+ Messages[0] = 0;
+
+ if (Alert && Alert[0])
+ ptr += sprintf(Messages, "", Alert, Key);
+
+ ptr += sprintf(ptr, "%s", " # Date XX Len To @ From Subject\r\n\r\n");
+
+ for (m = LatestMsg; m >= 1; m--)
+ {
+ if (ptr > &Messages[244000])
+ break; // protect buffer
+
+ Msg = GetMsgFromNumber(m);
+
+ if (Msg == 0 || Msg->type == 0 || Msg->status == 0)
+ continue; // Protect against corrupt messages
+
+ if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
+ {
+ char UTF8Title[4096];
+ char * EncodedTitle;
+
+ // List if it is the right type and in the page range we want
+
+ if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0)
+ continue;
+
+ // All Types or right Type. Check Mine Flag
+
+ if (Session->WebMailMine)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0)
+ continue;
+ }
+
+ if (Session->WebMailMyTX)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->from) != 0)
+ continue;
+ }
+
+ if (Session->WebMailMyRX)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->to)!= 0)
+ continue;
+ }
+
+ if (Count++ < Session->WebMailSkip)
+ continue;
+
+ strcpy(Via, Msg->via);
+ strlop(Via, '.');
+
+ // make sure title is HTML safe (no < > etc) and UTF 8 encoded
+
+ EncodedTitle = doXMLTransparency(Msg->title);
+
+ memset(UTF8Title, 0, 4096); // In case convert fails part way through
+ ConvertTitletoUTF8(Session->WebMail, EncodedTitle, UTF8Title, 4095);
+
+ free(EncodedTitle);
+
+ ptr += sprintf(ptr, "%6d %s %c%c %5d %-8s%-8s%-8s%s\r\n",
+ Key, Msg->number, Msg->number,
+ FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type,
+ Msg->status, Msg->length, Msg->to, Via,
+ Msg->from, UTF8Title);
+
+ n--;
+
+ if (n == 0)
+ break;
+ }
+ }
+
+ if (WebMailTemplate == NULL)
+ WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt");
+
+ return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key, Messages);
+}
+
+int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Number, BOOL DisplayHTML)
+{
+ char * Key = Session->Key;
+ struct UserInfo * User = Session->User;
+ WebMailInfo * WebMail = Session->WebMail;
+ char * DisplayStyle;
+
+ char Message[200000] = "";
+ struct MsgInfo * Msg;
+ char * ptr = Message;
+ char * MsgBytes, * Save;
+ int msgLen;
+
+ char FullTo[100];
+ char UTF8Title[4096];
+ int Index;
+ char * crcrptr;
+ char DownLoad[256] = "";
+
+ DisplayStyle = "textarea"; // Prevents interpretation of html and xml
+
+ Msg = GetMsgFromNumber(Number);
+
+ if (Msg == NULL)
+ {
+ ptr += sprintf(ptr, "Message %d not found\r\n", Number);
+ return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message);
+ }
+
+ // New Display so free any old values
+
+ FreeWebMailFields(WebMail);
+
+ WebMail->CurrentMessageIndex = Number;
+
+
+ if (!CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
+ {
+ ptr += sprintf(ptr, "Message %d not for you\r", Number);
+ return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message);
+ }
+
+ if (_stricmp(Msg->to, "RMS") == 0)
+ sprintf(FullTo, "RMS:%s", Msg->via);
+ else
+ if (Msg->to[0] == 0)
+ sprintf(FullTo, "smtp:%s", Msg->via);
+ else
+ strcpy(FullTo, Msg->to);
+
+ // make sure title is UTF 8 encoded
+
+ memset(UTF8Title, 0, 4096); // In case convert fails part way through
+ ConvertTitletoUTF8(Session->WebMail, Msg->title, UTF8Title, 4095);
+
+ // if a B2 message diplay B2 Header instead of a locally generated one
+
+ if ((Msg->B2Flags & B2Msg) == 0)
+ {
+ ptr += sprintf(ptr, "From: %s%s\nTo: %s\nType/Status: %c%c\nDate/Time: %s\nBid: %s\nTitle: %s\n\n",
+ Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, UTF8Title);
+ }
+
+ MsgBytes = Save = ReadMessageFile(Number);
+
+ msgLen = Msg->length;
+
+ if (Msg->type == 'P')
+ Index = PMSG;
+ else if (Msg->type == 'B')
+ Index = BMSG;
+ else
+ Index = TMSG;
+
+ if (MsgBytes)
+ {
+ if (Msg->B2Flags & B2Msg)
+ {
+ char * ptr1;
+
+ // if message has attachments, display them if plain text
+
+ if (Msg->B2Flags & Attachments)
+ {
+ int BodyLen, NewLen;
+ int i;
+ char *ptr2, *attptr;
+
+ sprintf(DownLoad, "Save Attachments | ", Key, Msg->number);
+
+ WebMail->Files = 0;
+
+ ptr1 = MsgBytes;
+
+ // ptr += sprintf(ptr, "Message has Attachments\r\n\r\n");
+
+ while(*ptr1 != 13)
+ {
+ ptr2 = strchr(ptr1, 10); // Find CR
+
+ if (memcmp(ptr1, "Body: ", 6) == 0)
+ {
+ BodyLen = atoi(&ptr1[6]);
+ }
+
+ if (memcmp(ptr1, "File: ", 6) == 0)
+ {
+ char * ptr3 = strchr(&ptr1[6], ' '); // Find Space
+ *(ptr2 - 1) = 0;
+
+ WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]);
+ WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]);
+ *(ptr2 - 1) = ' '; // put space back
+ }
+
+ ptr1 = ptr2;
+ ptr1++;
+ }
+
+ ptr1 += 2; // Over Blank Line and Separator
+
+ // ptr1 is pointing to body. Save for possible reply
+
+ WebMail->Body = malloc(BodyLen + 2);
+ memcpy(WebMail->Body, ptr1, BodyLen);
+ WebMail->Body[BodyLen] = 0;
+
+ *(ptr1 + BodyLen) = 0;
+
+ ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body
+
+ ptr1 += BodyLen + 2; // to first file
+
+ // Save pointers to file
+
+ attptr = ptr1;
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ WebMail->FileBody[i] = malloc(WebMail->FileLen[i]);
+ memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]);
+ attptr += (WebMail->FileLen[i] + 2);
+ }
+
+ // if first (only??) attachment is XML and filename
+ // starts "RMS_Express_Form" process as HTML Form
+
+ if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0)
+ {
+ int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments"
+ free(MsgBytes);
+
+ // Flag as read
+
+ if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
+ {
+ if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
+ {
+ if (Msg->status != 'Y')
+ {
+ Msg->status = 'Y';
+ Msg->datechanged=time(NULL);
+ SaveMessageDatabase();
+ SendMessageReadEvent(Session->Callsign, Msg);
+ }
+ }
+ }
+
+ return Len;
+ }
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ int n;
+ char * p = ptr1;
+ char c;
+
+ // Check if message is probably binary
+
+ int BinCount = 0;
+
+ NewLen = WebMail->FileLen[i];
+
+ for (n = 0; n < NewLen; n++)
+ {
+ c = *p;
+
+ if (c==0 || (c & 128))
+ BinCount++;
+
+ p++;
+
+ }
+
+ if (BinCount > NewLen/10)
+ {
+ // File is probably Binary
+
+ ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]);
+ }
+ else
+ {
+ *(ptr1 + NewLen) = 0;
+ ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]);
+ RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own
+
+ ptr += sprintf(ptr, "%s\r\r", ptr1);
+
+ User->Total.MsgsSent[Index] ++;
+ User->Total.BytesForwardedOut[Index] += NewLen;
+ }
+
+ ptr1 += WebMail->FileLen[i];
+ ptr1 +=2; // Over separator
+ }
+
+ free(Save);
+
+ ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from);
+
+ RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own
+
+ if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
+ {
+ if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
+ {
+ if (Msg->status != 'Y')
+ {
+ Msg->status = 'Y';
+ Msg->datechanged=time(NULL);
+ SaveMessageDatabase();
+ SendMessageReadEvent(Session->Callsign, Msg);
+ }
+ }
+ }
+
+ if (DisplayHTML && stristr(Message, ""))
+ DisplayStyle = "div"; // Use div so HTML and XML are interpreted
+
+ return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle);
+ }
+
+ // Remove B2 Headers (up to the File: Line)
+
+ // ptr1 = strstr(MsgBytes, "Body:");
+
+ // if (ptr1)
+ // MsgBytes = ptr1;
+ }
+
+ // Body may have cr cr lf which causes double space
+
+ crcrptr = strstr(MsgBytes, "\r\r\n");
+
+ while (crcrptr)
+ {
+ *crcrptr = ' ';
+ crcrptr = strstr(crcrptr, "\r\r\n");
+ }
+
+ // Remove lf chars
+
+ msgLen = RemoveLF(MsgBytes, msgLen);
+
+ User->Total.MsgsSent[Index] ++;
+ // User->Total.BytesForwardedOut[Index] += Length;
+
+ // if body not UTF-8, convert it
+
+ if (WebIsUTF8(MsgBytes, msgLen) == FALSE)
+ {
+ int code = TrytoGuessCode(MsgBytes, msgLen);
+
+ UCHAR * UTF = malloc(msgLen * 3);
+
+ if (code == 437)
+ msgLen = Convert437toUTF8(MsgBytes, msgLen, UTF);
+ else if (code == 1251)
+ msgLen = Convert1251toUTF8(MsgBytes, msgLen, UTF);
+ else
+ msgLen = Convert1252toUTF8(MsgBytes, msgLen, UTF);
+
+ free(MsgBytes);
+ Save = MsgBytes = UTF;
+
+ MsgBytes[msgLen] = 0;
+ }
+
+ // ptr += sprintf(ptr, "%s", MsgBytes);
+
+ memcpy(ptr, MsgBytes, msgLen);
+ ptr += msgLen;
+ ptr[0] = 0;
+
+ free(Save);
+
+ ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from);
+
+ if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
+ {
+ if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
+ {
+ if (Msg->status != 'Y')
+ {
+ Msg->status = 'Y';
+ Msg->datechanged=time(NULL);
+ SaveMessageDatabase();
+ SendMessageReadEvent(Session->Callsign, Msg);
+ }
+ }
+ }
+ }
+ else
+ {
+ ptr += sprintf(ptr, "File for Message %d not found\r", Number);
+ }
+
+ if (DisplayHTML && stristr(Message, ""))
+ DisplayStyle = "div"; // Use div so HTML and XML are interpreted
+
+
+ return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle);
+}
+
+int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number)
+{
+ struct MsgInfo * Msg;
+ char Message[100] = "";
+
+ Msg = GetMsgFromNumber(Number);
+
+ if (Msg == NULL)
+ {
+ sprintf(Message, "Message %d not found", Number);
+ goto returnit;
+ }
+
+ if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg))
+ {
+ FlagAsKilled(Msg, TRUE);
+ sprintf(Message, "Message #%d Killed\r", Number);
+ goto returnit;
+ }
+
+ sprintf(Message, "Not your message\r");
+
+returnit:
+ return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div");
+}
+
+void freeKeys(KeyValues * Keys)
+{
+ while (Keys->Key)
+ {
+ free(Keys->Key);
+ free(Keys->Value);
+ Keys++;
+ }
+}
+
+void FreeWebMailFields(WebMailInfo * WebMail)
+{
+ // release any malloc'ed resources
+
+ int i;
+ char * SaveReply;
+ int * SaveRlen;
+
+ if (WebMail == NULL)
+ return;
+
+ if (WebMail->txtFile)
+ free(WebMail->txtFile);
+
+ if (WebMail->txtFileName)
+ free(WebMail->txtFileName);
+
+ if (WebMail->InputHTMLName)
+ free(WebMail->InputHTMLName);
+
+ if (WebMail->DisplayHTMLName)
+ free(WebMail->DisplayHTMLName);
+
+ if (WebMail->ReplyHTMLName)
+ free(WebMail->ReplyHTMLName);
+
+ if (WebMail->To)
+ free(WebMail->To);
+ if (WebMail->CC)
+ free(WebMail->CC);
+ if (WebMail->Subject)
+ free(WebMail->Subject);
+ if (WebMail->BID)
+ free(WebMail->BID);
+ if (WebMail->Body)
+ free(WebMail->Body);
+ if (WebMail->XML)
+ free(WebMail->XML);
+ if (WebMail->XMLName)
+ free(WebMail->XMLName);
+
+ if (WebMail->OrigTo)
+ free(WebMail->OrigTo);
+ if (WebMail->OrigSubject)
+ free(WebMail->OrigSubject);
+ if (WebMail->OrigBID)
+ free(WebMail->OrigBID);
+ if (WebMail->OrigBody)
+ free(WebMail->OrigBody);
+
+ freeKeys(WebMail->txtKeys);
+ freeKeys(WebMail->XMLKeys);
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ free(WebMail->FileBody[i]);
+ free(WebMail->FileName[i]);
+ }
+
+ if (WebMail->Header)
+ free(WebMail->Header);
+ if (WebMail->Footer)
+ free(WebMail->Footer);
+
+ SaveReply = WebMail->Reply;
+ SaveRlen = WebMail->RLen;
+
+#ifndef WIN32
+ if (WebMail->iconv_toUTF8)
+ iconv_close(WebMail->iconv_toUTF8);
+#endif
+
+ memset(WebMail, 0, sizeof(WebMailInfo));
+
+ WebMail->Reply = SaveReply;
+ WebMail->RLen = SaveRlen;
+
+ return;
+}
+
+
+void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen)
+{
+ char * URLParams = strlop(Key, '&');
+ int ReplyLen;
+ char Appl = 'M';
+
+ // Webmail doesn't use the normal Mail Key.
+
+ // webscript.js doesn't need a key
+
+ if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0)
+ {
+ if (jsTemplate)
+ free(jsTemplate);
+
+ jsTemplate = GetTemplateFromFile(2, "webscript.js");
+
+ ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
+ "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate);
+ *RLen = ReplyLen;
+ return;
+ }
+
+ // Neither do js or file downloads
+
+ // This could be a request for a Template file
+ // WebMail/Local_Templates/My Forms/inc/logo_ad63.png
+ // WebMail/Standard Templates/
+
+
+ if (_memicmp(NodeURL, "/WebMail/Local", 14) == 0 || (_memicmp(NodeURL, "/WebMail/Standard", 17) == 0))
+ {
+ int FileSize;
+ char * MsgBytes;
+ char MsgFile[512];
+ FILE * hFile;
+ size_t ReadLen;
+ char TimeString[64];
+ char FileTimeString[64];
+ struct stat STAT;
+ char * FN = &NodeURL[9];
+ char * fileBit = FN;
+ char * ext;
+ char Type[64] = "Content-Type: text/html\r\n";
+
+ UndoTransparency(FN);
+ ext = strchr(FN, '.');
+
+ sprintf(MsgFile, "%s/%s", BPQDirectory, FN);
+
+ while (strchr(fileBit, '/'))
+ fileBit = strlop(fileBit, '/');
+
+ if (stat(MsgFile, &STAT) == -1)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == 0)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ FileSize = STAT.st_size;
+ MsgBytes = malloc(FileSize + 1);
+ ReadLen = fread(MsgBytes, 1, FileSize, hFile);
+
+ fclose(hFile);
+
+ FormatTime2(FileTimeString, STAT.st_ctime);
+ FormatTime2(TimeString, time(NULL));
+
+ ext++;
+
+ if (_stricmp(ext, "js") == 0)
+ strcpy(Type, "Content-Type: text/javascript\r\n");
+
+ if (_stricmp(ext, "css") == 0)
+ strcpy(Type, "Content-Type: text/css\r\n");
+
+ if (_stricmp(ext, "pdf") == 0)
+ strcpy(Type, "Content-Type: application/pdf\r\n");
+
+ if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 ||
+ _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0)
+ strcpy(Type, "Content-Type: image\r\n");
+
+ // File may be binary so output header then copy in message
+
+ *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
+ "%s"
+ "Date: %s\r\n"
+ "Last-Modified: %s\r\n"
+ "\r\n", FileSize, Type,TimeString, FileTimeString);
+
+ memcpy(&Reply[*RLen], MsgBytes, FileSize);
+ *RLen += FileSize;
+ free (MsgBytes);
+ return;
+ }
+
+ //
+
+ if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0)
+ {
+ int FileSize;
+ char * MsgBytes;
+ char MsgFile[512];
+ FILE * hFile;
+ size_t ReadLen;
+ char TimeString[64];
+ char FileTimeString[64];
+ struct stat STAT;
+ char * FN = &NodeURL[16];
+ char * fileBit = FN;
+ char * ext;
+ char Type[64] = "Content-Type: text/html\r\n";
+
+
+ UndoTransparency(FN);
+ ext = strchr(FN, '.');
+
+ sprintf(MsgFile, "%s/%s", BPQDirectory, FN);
+
+ while (strchr(fileBit, '/'))
+ fileBit = strlop(fileBit, '/');
+
+ if (stat(MsgFile, &STAT) == -1)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == 0)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ FileSize = STAT.st_size;
+ MsgBytes = malloc(FileSize + 1);
+ ReadLen = fread(MsgBytes, 1, FileSize, hFile);
+
+ fclose(hFile);
+
+ FormatTime2(FileTimeString, STAT.st_ctime);
+ FormatTime2(TimeString, time(NULL));
+
+ ext++;
+
+ if (_stricmp(ext, "js") == 0)
+ strcpy(Type, "Content-Type: text/javascript\r\n");
+
+ if (_stricmp(ext, "css") == 0)
+ strcpy(Type, "Content-Type: text/css\r\n");
+
+ if (_stricmp(ext, "pdf") == 0)
+ strcpy(Type, "Content-Type: application/pdf\r\n");
+
+ if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 ||
+ _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0)
+ strcpy(Type, "Content-Type: image\r\n");
+
+ // File may be binary so output header then copy in message
+
+ *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
+ "%s"
+ "Date: %s\r\n"
+ "Last-Modified: %s\r\n"
+ "\r\n", FileSize, Type,TimeString, FileTimeString);
+
+ memcpy(&Reply[*RLen], MsgBytes, FileSize);
+ *RLen += FileSize;
+ free (MsgBytes);
+ return;
+ }
+
+ Session = NULL;
+
+ if (Key && Key[0])
+ Session = FindWMSession(Key);
+
+ if (Session == NULL)
+ {
+ // Lost Session
+
+ if (LOCAL)
+ {
+ Session = AllocateWebMailSession();
+
+ Key = Session->Key;
+
+ if (SYSOPCall[0])
+ Session->User = LookupCall(SYSOPCall);
+ else
+ Session->User = LookupCall(BBSName);
+
+ if (Session->User)
+ {
+ strcpy(NodeURL, "/WebMail/WebMail");
+ Session->WebMailSkip = 0;
+ Session->WebMailLastUsed = time(NULL);
+ }
+ }
+ else
+ {
+ // Send Login Page unless Signon request
+
+ if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0)
+ {
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+ }
+ }
+
+ if (strcmp(Method, "POST") == 0)
+ {
+ if (_stricmp(NodeURL, "/WebMail/Signon") == 0)
+ {
+ char * msg = strstr(input, "\r\n\r\n"); // End of headers
+ char * user, * password, * Key;
+ char Msg[128];
+ int n;
+
+ if (msg)
+ {
+ struct UserInfo * User;
+
+ if (strstr(msg, "Cancel=Cancel"))
+ {
+ *RLen = sprintf(Reply, "");
+ return;
+ }
+ // Webmail Gets Here with a dummy Session
+
+ Session = AllocateWebMailSession();
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+
+ Key = Session->Key;
+
+ user = strtok_s(&msg[9], "&", &Key);
+ password = strtok_s(NULL, "=", &Key);
+ password = Key;
+
+ Session->User = User = LookupCall(user);
+
+ if (User)
+ {
+ // Check Password
+
+ if (password[0] && strcmp(User->pass, password) == 0)
+ {
+ // send Message Index
+
+ Session->WebMailLastUsed = time(NULL);
+ Session->WebMailSkip = 0;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+ Session->WebMailMine = FALSE;
+
+ if (WebMailTemplate)
+ {
+ free(WebMailTemplate);
+ WebMailTemplate = NULL;
+ }
+
+ if (User->flags & F_Excluded)
+ {
+ n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user));
+ WriteLogLine(NULL, '|',Msg, n, LOG_BBS);
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user));
+ WriteLogLine(NULL, '|',Msg, n, LOG_BBS);
+
+ return;
+ }
+
+ }
+
+ // Bad User or Pass
+
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+ }
+
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+ if (_stricmp(NodeURL, "/WebMail/EMSave") == 0)
+ {
+ // Save New Message
+
+ SaveNewMessage(Session, input, Reply, RLen, Key, InputLen);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/Submit") == 0)
+ {
+ // Get the POST data from the page and place in message
+
+ char * param = strstr(input, "\r\n\r\n"); // End of headers
+ WebMailInfo * WebMail = Session->WebMail;
+
+ if (WebMail == NULL)
+ return; // Can't proceed if we have no info on form
+
+ ProcessFormInput(Session, input, Reply, RLen, InputLen);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0)
+ {
+ // Save New Message
+
+ SaveTemplateMessage(Session, input, Reply, RLen, Key);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0)
+ {
+ SendTemplateSelectScreen(Session, input, InputLen);
+ return;
+ }
+
+ // End of POST section
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0)
+ {
+ Session->Key[0] = 0;
+ Session->WebMailLastUsed = 0;
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+
+ if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) ||
+ (_stricmp(NodeURL, "/WebMail") == 0) ||
+ (_stricmp(NodeURL, "/WebMail/") == 0))
+ {
+ // Entry from Menu if signed in, continue. If not and Localhost
+ // signin as sysop.
+
+ if (Session->User == NULL)
+ {
+ // Not yet signed in
+
+ if (LOCAL)
+ {
+ // Webmail Gets Here with a dummy Session
+
+ Session = AllocateWebMailSession();
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+ Key = Session->Key;
+
+ if (SYSOPCall[0])
+ Session->User = LookupCall(SYSOPCall);
+ else
+ Session->User = LookupCall(BBSName);
+
+ if (Session->User)
+ {
+ strcpy(NodeURL, "/WebMail/WebMail");
+ Session->WebMailSkip = 0;
+ Session->WebMailLastUsed = time(NULL);
+ }
+ }
+ else
+ {
+ // Send Login Page
+
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+ }
+ }
+
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+ if (_stricmp(NodeURL, "/WebMail/WebMail") == 0)
+ {
+ if (WebMailTemplate)
+ {
+ free(WebMailTemplate);
+ WebMailTemplate = NULL;
+ }
+
+ Session->WebMailSkip = 0;
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMAll") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMB") == 0)
+ {
+ Session->WebMailSkip = 0;
+ strcpy(Session->WebMailTypes, "B");
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMP") == 0)
+ {
+ Session->WebMailSkip = 0;
+ strcpy(Session->WebMailTypes, "P");
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMT") == 0)
+ {
+ Session->WebMailSkip = 0;
+ strcpy(Session->WebMailTypes, "T");
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMMine") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = TRUE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMtoMe") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = TRUE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMfromMe") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = TRUE;
+ Session->WebMailMyTX = TRUE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+
+ if (_stricmp(NodeURL, "/WebMail/WMSame") == 0)
+ {
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0)
+ {
+ // Auto Refresh Version of index page. Uses Web Sockets
+
+ char Page[4096];
+
+ char WebSockPage[] =
+ "\r\n"
+ " \r\n"
+ " \r\n"
+ " \r\n"
+ " \r\n"
+ "\r\n"
+ "\r\n"
+
+ "\r\n"
+ "WebMail \r\n"
+ "\r\n"
+
+ "\r\n"
+ " %s Webmail Interface - User %s - Message List
\r\n"
+ "\r\n"
+ "
\r\n"
+
+ "Waiting for data...
\r\n"
+ "\r\n";
+
+ sprintf(Page, WebSockPage, Key, Key ,BBSName, Session->User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key);
+
+ *RLen = sprintf(Reply, "%s", Page);
+ return;
+ }
+
+
+ if (memcmp(NodeURL, "/WebMail/QuoteOriginal/", 15) == 0)
+ {
+ // Reply to Message
+
+ int n, len;
+ struct MsgInfo * Msg;
+ char Message[100] = "";
+ char Title[100];
+ char * MsgBytes, * Save, * NewBytes;
+ char * ptr;
+ char * ptr1, * ptr2;
+ char * EncodedTitle;
+
+ n = Session->WebMail->CurrentMessageIndex;
+
+ Msg = GetMsgFromNumber(n);
+
+ if (Msg == NULL)
+ {
+ sprintf(Message, "Message %d not found", n);
+ *RLen = sprintf(Reply, "%s", Message);
+ return;
+ }
+
+ Session->WebMail->Msg = Msg;
+
+ if (stristr(Msg->title, "Re:") == 0)
+ sprintf(Title, "Re:%s", Msg->title);
+ else
+ sprintf(Title, "%s", Msg->title);
+
+ MsgBytes = Save = ReadMessageFile(n);
+
+
+ ptr = NewBytes = malloc((Msg->length * 2) + 256);
+
+ // Copy a line at a time with "> " in front of each
+
+ ptr += sprintf(ptr, "%s", "\r\n\r\n\r\n\r\n\r\nOriginal Message\r\n\r\n> ");
+
+ ptr1 = ptr2 = MsgBytes;
+ len = (int)strlen(MsgBytes);
+
+ while (len-- > 0)
+ {
+ *ptr++ = *ptr1;
+
+ if (*(ptr1) == '\n')
+ {
+ *ptr++ = '>';
+ *ptr++ = ' ';
+ }
+
+ ptr1++;
+ }
+
+ *ptr++ = 0;
+
+ EncodedTitle = doXMLTransparency(Msg->title);
+
+ *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, "", EncodedTitle , NewBytes);
+
+ free(EncodedTitle);
+
+ free(MsgBytes);
+ free(NewBytes);
+
+ return;
+ }
+
+
+
+ if (memcmp(NodeURL, "/WebMail/Reply/", 15) == 0)
+ {
+ // Reply to Message
+
+ int n = atoi(&NodeURL[15]);
+ struct MsgInfo * Msg;
+ char Message[100] = "";
+ char Title[100];
+ char * EncodedTitle;
+
+ // Quote Original
+
+ char Button[] =
+ " "
+ "";
+
+ char Temp[1024];
+ char ReplyAddr[128];
+
+ Msg = GetMsgFromNumber(n);
+
+ if (Msg == NULL)
+ {
+ sprintf(Message, "Message %d not found", n);
+ *RLen = sprintf(Reply, "%s", Message);
+ return;
+ }
+
+ Session->WebMail->Msg = Msg;
+
+ // See if the message was displayed in an HTML form with a reply template
+
+ *RLen = ReplyToFormsMessage(Session, Msg, Reply, FALSE);
+
+ // If couldn't build reply form use normal text reply
+
+ if (*RLen)
+ return;
+
+
+ sprintf(Temp, Button, Key);
+
+ if (stristr(Msg->title, "Re:") == 0)
+ sprintf(Title, "Re:%s", Msg->title);
+ else
+ sprintf(Title, "%s", Msg->title);
+
+ strcpy(ReplyAddr, Msg->from);
+ strcat(ReplyAddr, Msg->emailfrom);
+
+ EncodedTitle = doXMLTransparency(Msg->title);
+
+ *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, Temp, EncodedTitle , "");
+
+ free(EncodedTitle);
+ return;
+ }
+
+ if (strcmp(NodeURL, "/WebMail/WM") == 0)
+ {
+ // Read Message
+
+ int n = 0;
+
+ if (URLParams)
+ n = atoi(URLParams);
+
+ if (WebMailMsgTemplate)
+ free(WebMailMsgTemplate);
+
+ WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt");
+
+ *RLen = ViewWebMailMessage(Session, Reply, n, TRUE);
+
+ return;
+ }
+
+ if (strcmp(NodeURL, "/WebMail/WMPrev") == 0)
+ {
+ // Read Previous Message
+
+ int m;
+ struct MsgInfo * Msg;
+ struct UserInfo * User = Session->User;
+
+
+ for (m = Session->WebMail->CurrentMessageIndex - 1; m >= 1; m--)
+ {
+
+ Msg = GetMsgFromNumber(m);
+
+ if (Msg == 0 || Msg->type == 0 || Msg->status == 0)
+ continue; // Protect against corrupt messages
+
+ if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
+ {
+ // Display if it is the right type and in the page range we want
+
+ if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0)
+ continue;
+
+ // All Types or right Type. Check Mine Flag
+
+ if (Session->WebMailMine)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0)
+ continue;
+ }
+
+ if (Session->WebMailMyTX)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->from) != 0)
+ continue;
+ }
+
+ if (Session->WebMailMyRX)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->to) != 0)
+ continue;
+ }
+ *RLen = ViewWebMailMessage(Session, Reply, m, TRUE);
+
+ return;
+ }
+ }
+
+ // No More
+
+ *RLen = sprintf(Reply, "", Session->Key);
+ return;
+
+ }
+
+ if (strcmp(NodeURL, "/WebMail/WMNext") == 0)
+ {
+ // Read Previous Message
+
+ int m;
+ struct MsgInfo * Msg;
+ struct UserInfo * User = Session->User;
+
+ for (m = Session->WebMail->CurrentMessageIndex + 1; m <= LatestMsg; m++)
+ {
+ Msg = GetMsgFromNumber(m);
+
+ if (Msg == 0 || Msg->type == 0 || Msg->status == 0)
+ continue; // Protect against corrupt messages
+
+ if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
+ {
+ // Display if it is the right type and in the page range we want
+
+ if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0)
+ continue;
+
+ // All Types or right Type. Check Mine Flag
+
+ if (Session->WebMailMine)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0)
+ continue;
+ }
+
+ if (Session->WebMailMyTX)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->from) != 0)
+ continue;
+ }
+
+ if (Session->WebMailMyRX)
+ {
+ // Only list if to or from me
+
+ if (strcmp(User->Call, Msg->to) != 0)
+ continue;
+ }
+ *RLen = ViewWebMailMessage(Session, Reply, m, TRUE);
+
+ return;
+ }
+ }
+
+ // No More
+
+ *RLen = sprintf(Reply, "", Session->Key);
+ return;
+
+ }
+
+
+ if (strcmp(NodeURL, "/WebMail/DisplayText") == 0)
+ {
+ // Read Message
+
+ int n = 0;
+
+ if (URLParams)
+ n = atoi(URLParams);
+
+ if (WebMailMsgTemplate)
+ free(WebMailMsgTemplate);
+
+ WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt");
+
+ *RLen = ViewWebMailMessage(Session, Reply, n, FALSE);
+
+ return;
+ }
+ if (memcmp(NodeURL, "/WebMail/WMDel/", 15) == 0)
+ {
+ // Kill Message
+
+ int n = atoi(&NodeURL[15]);
+
+ *RLen = KillWebMailMessage(Reply, Session->Key, Session->User, n);
+
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/NewMsg") == 0)
+ {
+ // Add HTML Template Button if we have any HTML Form
+
+ char Button[] =
+ " "
+ "";
+
+ char Temp[1024];
+
+ FreeWebMailFields(Session->WebMail); // Tidy up for new message
+
+ sprintf(Temp, Button, Key);
+
+ if (FormDirCount == 0)
+ *RLen = sprintf(Reply, MsgInputPage, Key, "", "", "", "");
+ else
+ *RLen = sprintf(Reply, MsgInputPage, Key, "", Temp, "", "");
+
+ return;
+ }
+
+ if (_memicmp(NodeURL, "/WebMail/GetPage/", 17) == 0)
+ {
+ // Read and Parse Template File
+
+ GetPage(Session, NodeURL);
+ return;
+ }
+
+ if (_memicmp(NodeURL, "/WebMail/GetList/", 17) == 0)
+ {
+ // Send Select Template Popup
+
+ char * SubDir;
+ int DirNo = 0;
+ int SubDirNo = 0;
+ char popup[10000];
+
+ char popuphddr[] =
+
+ ""
+ ""
+ ""
+ "Select Required Template from %s
"
+ "
");
+
+ *RLen = sprintf(Reply, "%s", popup);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/DL") == 0)
+ {
+ getAttachmentList(Session, Reply, RLen, URLParams);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/GetDownLoad") == 0)
+ {
+ DownloadAttachments(Session, Reply, RLen, URLParams);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/DoSelect") == 0)
+ {
+ // User has selected item from Template
");
+
+ *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup);
+
+ free(Boundary);
+ return;
+}
+
+static char WinlinkAddr[] = "WINLINK.ORG";
+
+VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen)
+{
+ int i, ReplyLen = 0;
+ struct MsgInfo * Msg;
+ FILE * hFile;
+ int Template=0;
+ char * via = NULL;
+ BIDRec * BIDRec;
+ char MsgFile[MAX_PATH];
+ size_t WriteLen=0;
+ char * HDest;
+ char * HDestCopy;
+ char * HDestRest;
+ char * Vptr = NULL;
+ char * FileList = NULL;
+ char Prompt[256] = "Message Saved";
+ char OrigTo[256];
+ WebMailInfo * WebMail = Session->WebMail;
+ struct UserInfo * user;
+ CIRCUIT conn;
+
+ // So we can have attachments input is now Content-Type: multipart/form-data;
+
+ char * Input;
+ size_t MsgLen = 0;
+ char * Boundary;
+
+ strcpy(conn.Callsign, Session->User->Call);
+
+ Input = MsgPtr;
+
+ Boundary = initMultipartUnpack(&Input);
+
+ if (Boundary == NULL)
+ return; // Can't work without one
+
+ // Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part
+
+ while(Input && Input[-1] != '-')
+ {
+ char * Name, * Value;
+ int ValLen;
+
+ if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE)
+ {
+ // ReportCorrupt(WebMail);
+ free(Boundary);
+ return;
+ }
+ if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE)
+ {
+ *RLen = sprintf(Reply, "", Session->Key);
+ return;
+ }
+ }
+
+
+ if (WebMail->txtFileName)
+ {
+ // Processing Form Input
+
+ SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest);
+
+ // Prevent re-entry
+
+ free(WebMail->txtFileName);
+ WebMail->txtFileName = NULL;
+
+ return;
+ }
+
+ // If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before.
+
+ strlop(WebMail->BID, ' ');
+ if (strlen(WebMail->BID) > 12)
+ WebMail->BID[12] = 0;
+
+ UndoTransparency(WebMail->BID);
+ UndoTransparency(WebMail->To);
+ UndoTransparency(WebMail->Subject);
+ UndoTransparency(WebMail->Body);
+
+ MsgLen = strlen(WebMail->Body);
+
+ // We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one
+
+ if (WebMail->To[0] == 0)
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise
+ return;
+ }
+
+ if (strlen(WebMail->To) > 255)
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise
+ return;
+ }
+
+ HDest = _strdup(WebMail->To);
+
+ if (strlen(WebMail->BID))
+ {
+ if (LookupBID(WebMail->BID))
+ {
+ // Duplicate bid
+ *RLen = sprintf(Reply, "%s", "");
+ FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise
+ return;
+ }
+ }
+
+ if (WebMail->Type == 'B')
+ {
+ if (RefuseBulls)
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise
+ return;
+ }
+ }
+
+ // ?? Can we just loop though the rest of the code to allow multiple dests ??
+
+ HDestCopy = HDest;
+
+ while (HDest && HDest[0])
+ {
+ HDestRest = strlop(HDest, ';');
+
+ Msg = AllocateMsgRecord();
+
+ // Set number here so they remain in sequence
+
+ Msg->number = ++LatestMsg;
+ MsgnotoMsg[Msg->number] = Msg;
+
+ strcpy(Msg->from, Session->User->Call);
+
+ if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0)
+ {
+ Vptr=&HDest[4];
+ strcpy(Msg->to, "RMS");
+ }
+ else if (_memicmp(HDest, "smtp:", 5) == 0)
+ {
+ if (ISP_Gateway_Enabled)
+ {
+ Vptr=&HDest[5];
+ Msg->to[0] = 0;
+ }
+ }
+ else if (strchr(HDest, '@'))
+ {
+ strcpy(OrigTo, HDest);
+
+ Vptr = strlop(HDest, '@');
+
+ if (Vptr)
+ {
+ // If looks like a valid email address, treat as such
+
+ if (strlen(HDest) > 6 || !CheckifPacket(Vptr))
+ {
+ // Assume Email address
+
+ Vptr = OrigTo;
+
+ if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route
+ strcpy(Msg->to, "RMS");
+ else if (ISP_Gateway_Enabled)
+ Msg->to[0] = 0;
+ else if (isAMPRMsg(OrigTo))
+ strcpy(Msg->to, "RMS"); // Routing will redirect it
+ else
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise
+ return;
+
+ }
+ }
+ else
+ strcpy(Msg->to, _strupr(HDest));
+ }
+ }
+ else
+ {
+ // No @
+
+ if (strlen(HDest) > 6)
+ HDest[6] = 0;
+
+ strcpy(Msg->to, _strupr(HDest));
+ }
+
+ if (SendBBStoSYSOPCall)
+ if (_stricmp(HDest, BBSName) == 0)
+ strcpy(Msg->to, SYSOPCall);
+
+ if (Vptr)
+ {
+ if (strlen(Vptr) > 40)
+ Vptr[40] = 0;
+
+ strcpy(Msg->via, _strupr(Vptr));
+ }
+ else
+ {
+ // No via. If not local user try to add BBS
+
+ struct UserInfo * ToUser = LookupCall(Msg->to);
+
+ if (ToUser)
+ {
+ // Local User. If Home BBS is specified, use it
+
+ if (ToUser->flags & F_RMSREDIRECT)
+ {
+ // sent to Winlink
+
+ strcpy(Msg->via, WinlinkAddr);
+ sprintf(Prompt, "Redirecting to winlink.org\r");
+ }
+ else if (ToUser->HomeBBS[0])
+ {
+ strcpy(Msg->via, ToUser->HomeBBS);
+ sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via);
+ }
+ else
+ {
+ // No HomeBBS - check WP
+
+ WPRecP WP = LookupWP(Msg->to);
+
+ if (WP)
+ {
+ strcpy(Msg->via, WP->first_homebbs);
+ sprintf(Prompt, "%s added from WP", Msg->via);
+ }
+ }
+ }
+ else
+ {
+ // Not local user - Check WP
+
+ WPRecP WP = LookupWP(Msg->to);
+
+ if (WP)
+ {
+ strcpy(Msg->via, WP->first_homebbs);
+ sprintf(Prompt, "%s added from WP", Msg->via);
+ }
+ }
+ }
+
+ if (strlen(WebMail->Subject) > 60)
+ WebMail->Subject[60] = 0;
+
+ strcpy(Msg->title, WebMail->Subject);
+ Msg->type = WebMail->Type;
+
+ if (Session->User->flags & F_HOLDMAIL)
+ {
+ int Length=0;
+ char * MailBuffer = malloc(100);
+ char Title[100];
+
+ Msg->status = 'H';
+
+ Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number);
+ sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set");
+ SendMessageToSYSOP(Title, MailBuffer, Length);
+ }
+ else
+ Msg->status = 'N';
+
+ if (strlen(WebMail->BID) == 0)
+ sprintf(Msg->bid, "%d_%s", LatestMsg, BBSName);
+ else
+ strcpy(Msg->bid, WebMail->BID);
+
+ Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
+
+ BIDRec = AllocateBIDRecord();
+
+ strcpy(BIDRec->BID, Msg->bid);
+ BIDRec->mode = Msg->type;
+ BIDRec->u.msgno = LOWORD(Msg->number);
+ BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
+
+ Msg->length = (int)MsgLen + WebMail->HeaderLen + WebMail->FooterLen;
+
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
+
+ // BuildFormMessage(Session, Msg);
+
+ if (WebMail->Files)
+ {
+ // Send as B2
+
+ char * B2Header = BuildB2Header(WebMail, Msg, NULL, 0);
+
+ hFile = fopen(MsgFile, "wb");
+
+ if (hFile)
+ {
+ WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile);
+ WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile);
+ WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile);
+ WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile);
+ WriteLen += fwrite("\r\n", 1, 2, hFile);
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile);
+ WriteLen += fwrite("\r\n", 1, 2, hFile);
+ }
+ fclose(hFile);
+ free(B2Header);
+
+ Msg->length = (int)WriteLen;
+ }
+ }
+ else
+ {
+ hFile = fopen(MsgFile, "wb");
+
+ if (hFile)
+ {
+ WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile);
+ WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile);
+ WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile);
+ fclose(hFile);
+ }
+ }
+ MatchMessagetoBBSList(Msg, &conn);
+
+ BuildNNTPList(Msg); // Build NNTP Groups list
+
+ if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
+ Msg->status = '$'; // Has forwarding
+
+
+ if (EnableUI)
+ SendMsgUI(Msg);
+
+ user = LookupCall(Msg->to);
+
+ // If Event Notifications enabled report a new message event
+
+ SendNewMessageEvent(user->Call, Msg);
+
+#ifndef NOMQTT
+ if (MQTT)
+ MQTTMessageEvent(Msg);
+#endif
+
+ if (user && (user->flags & F_APRSMFOR))
+ {
+ char APRS[128];
+ char Call[16];
+ int SSID = user->flags >> 28;
+
+ if (SSID)
+ sprintf(Call, "%s-%d", Msg->to, SSID);
+ else
+ strcpy(Call, Msg->to);
+
+ sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from);
+ APISendAPRSMessage(APRS, Call);
+ }
+
+ HDest = HDestRest;
+ }
+
+ *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt);
+
+ SaveMessageDatabase();
+ SaveBIDDatabase();
+ FreeWebMailFields(WebMail);
+ free(HDestCopy);
+
+ return;
+}
+
+
+
+
+
+
+// RMS Express Forms Support
+
+char * GetHTMLViewerTemplate(char * FN, struct HtmlFormDir ** FormDir)
+{
+ int i, j, k, l;
+
+ // Seach list of forms for base file (without .html)
+
+ for (i = 0; i < FormDirCount; i++)
+ {
+ struct HtmlFormDir * Dir = HtmlFormDirs[i];
+
+ for (j = 0; j < Dir->FormCount; j++)
+ {
+ if (strcmp(FN, Dir->Forms[j]->FileName) == 0)
+ {
+ *FormDir = Dir;
+ return CheckFile(Dir, FN);
+ }
+ }
+
+ if (Dir->DirCount)
+ {
+ for (l = 0; l < Dir->DirCount; l++)
+ {
+ struct HtmlFormDir * SDir = Dir->Dirs[l];
+
+ if (SDir->DirCount)
+ {
+ struct HtmlFormDir * SSDir = SDir->Dirs[0];
+ int x = 1;
+ }
+
+ for (k = 0; k < SDir->FormCount; k++)
+ {
+ if (_stricmp(FN, SDir->Forms[k]->FileName) == 0)
+ {
+ *FormDir = SDir;
+ return CheckFile(SDir, SDir->Forms[k]->FileName);
+ }
+ }
+ if (SDir->DirCount)
+ {
+ struct HtmlFormDir * SSDir = SDir->Dirs[0];
+ int x = 1;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+VOID GetReply(struct HTTPConnectionInfo * Session, char * NodeURL)
+{
+}
+
+VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL)
+{
+ // Read the HTML Template file and do any needed substitutions
+
+ WebMailInfo * WebMail = Session->WebMail;
+ KeyValues * txtKey = WebMail->txtKeys;
+
+ int DirNo;
+ char * ptr;
+ int FileNo = 0;
+ char * SubDir;
+ int SubDirNo;
+ int i;
+ struct HtmlFormDir * Dir;
+ char * Template;
+ char * inptr;
+ char FormDir[MAX_PATH] = "";
+ char FN[MAX_PATH] = "";
+ char * InputName = NULL; // HTML to input message
+ char * ReplyName = NULL;
+ char * To = NULL;
+ char * CC = NULL;
+ char * BID = NULL;
+ char Type = 0;
+ char * Subject = NULL;
+ char * MsgBody = NULL;
+ char * varptr;
+ char * endptr;
+ size_t varlen, vallen = 0;
+ char val[256]=""; // replacement text
+ char var[100] = "\"";
+ char * MsgBytes;
+ char Submit[64];
+
+ if (NodeURL == NULL)
+ {
+ //rentry after processing or
+
+ goto reEnter;
+ }
+
+ DirNo = atoi(&NodeURL[17]);
+ ptr = strchr(&NodeURL[17], ',');
+ Dir = HtmlFormDirs[DirNo];
+
+
+ if (DirNo == -1)
+ {
+ *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key);
+ return;
+ }
+
+ SubDir = strlop(&NodeURL[17], ':');
+ if (SubDir)
+ {
+ SubDirNo = atoi(SubDir);
+ Dir = Dir->Dirs[SubDirNo];
+ }
+
+ sprintf(Submit, "/Webmail/Submit?%s", Session->Key);
+
+ if (ptr)
+ FileNo = atoi(ptr + 1);
+
+ // First we read the .txt. then get name of input .html from it
+
+ if (WebMail->txtFile)
+ free(WebMail->txtFile);
+
+ WebMail->txtFile = NULL;
+
+ if (WebMail->txtFileName)
+ free(WebMail->txtFileName);
+
+ WebMail->txtFileName = _strdup(Dir->Forms[FileNo]->FileName);
+
+ // Read the file here to simplify reentry if we do or substitutions
+
+ // if Dir not specified search all for Filename
+
+ if (Dir == NULL)
+ {
+ for (i = 0; i < FormDirCount; i++)
+ {
+ int n;
+
+ Dir = HtmlFormDirs[i];
+
+ MsgBytes = CheckFile(Dir, WebMail->txtFileName);
+ if (MsgBytes)
+ goto gotFile;
+
+ // Recurse any Subdirs
+
+ n = 0;
+ while (n < Dir->DirCount)
+ {
+ MsgBytes = CheckFile(Dir->Dirs[n], FN);
+ if (MsgBytes)
+ {
+ Dir = Dir->Dirs[n];
+ goto gotFile;
+ }
+ n++;
+ }
+ }
+ return;
+ }
+ else
+ MsgBytes = CheckFile(Dir, WebMail->txtFileName);
+
+gotFile:
+
+ WebMail->Dir = Dir;
+
+ if (WebMail->txtFile)
+ free(WebMail->txtFile);
+
+ WebMail->txtFile = MsgBytes;
+
+reEnter:
+
+ if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE)
+ {
+ // Template has or tags and value has been requested
+
+ return;
+ }
+
+ if (WebMail->InputHTMLName == NULL)
+ {
+ // This is a plain text template without HTML
+
+ if (To == NULL &&WebMail->To && WebMail->To[0])
+ To = WebMail->To;
+ else
+ To = "";
+
+ if (CC == NULL && WebMail->CC && WebMail->CC[0])
+ CC = WebMail->CC;
+ else
+ CC = "";
+
+
+ if (Subject == NULL && WebMail->Subject && WebMail->Subject[0])
+ Subject = WebMail->Subject;
+ else
+ Subject = "";
+
+ if (BID == NULL && WebMail->BID && WebMail->BID[0])
+ BID = WebMail->BID;
+ else
+ BID = "";
+
+ if (Type == 0 && WebMail->Type)
+ Type = WebMail->Type;
+ else
+ Type = 'P';
+
+ if (MsgBody == NULL)
+ MsgBody = "";
+
+ if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0])
+ MsgBody = WebMail->Body;
+
+ *WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject,
+ (Type =='P') ? "Selected" : "", (Type =='B') ? "Selected" : "", (Type =='T') ? "Selected" : "", BID, MsgBody);
+ return;
+ }
+
+ Template = CheckFile(Dir, WebMail->InputHTMLName);
+
+ if (Template == NULL)
+ {
+ *WebMail->RLen = sprintf(WebMail->Reply, "%s", "HTML Template not found");
+ return;
+ }
+
+ // I've going to update the template in situ, as I can't see a better way
+ // of making sure all occurances of variables in any order are substituted.
+ // The space allocated to Template is twice the size of the file
+ // to allow for insertions
+
+ // First find the Form Action string and replace with our URL. It should have
+ // action="http://{FormServer}:{FormPort}" but some forms have localhost:8001 instead
+
+ // Also remove the OnSubmit if it contains the standard popup about having to close browser
+
+ UpdateFormAction(Template, Session->Key);
+
+ // Search for "{var }" strings in form and replace with
+ // corresponding variable
+
+ // we run through the Template once for each variable
+
+ while (txtKey->Key)
+ {
+ char Key[256] = "{";
+
+ strcpy(&Key[1], &txtKey->Key[1]);
+
+ ptr = strchr(Key, '>');
+ if (ptr)
+ *ptr = '}';
+
+ inptr = Template;
+ varptr = stristr(inptr, Key);
+
+ while (varptr)
+ {
+ // Move the remaining message up/down the buffer to make space for substitution
+
+ varlen = strlen(Key);
+ if (txtKey->Value)
+ vallen = strlen(txtKey->Value);
+ else vallen = 0;
+
+ endptr = varptr + varlen;
+
+ memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end
+ memcpy(varptr, txtKey->Value, vallen);
+
+ inptr = endptr + 1;
+
+ varptr = stristr(inptr, Key);
+ }
+ txtKey++;
+ }
+
+ // Remove from end as we add it on later
+
+ ptr = stristr(Template, "");
+
+ if (ptr)
+ *ptr = 0;
+
+ *WebMail->RLen = sprintf(WebMail->Reply, "%s", Template);
+ free(Template);
+ return;
+}
+
+
+
+char * xxReadTemplate(char * FormSet, char * DirName, char *FileName)
+{
+ int FileSize;
+ char * MsgBytes;
+ char MsgFile[265];
+ size_t ReadLen;
+ struct stat STAT;
+ FILE * hFile;
+
+#ifndef WIN32
+
+ // Need to do case insensitive file search
+
+ DIR *dir;
+ struct dirent *entry;
+ char name[256];
+
+ sprintf(name, "%s/%s/%s", BPQDirectory, FormSet, DirName);
+
+ if (!(dir = opendir(name)))
+ {
+ Debugprintf("cant open forms dir %s %d %d", name, errno, dir);
+ return 0;
+ }
+
+ while ((entry = readdir(dir)) != NULL)
+ {
+ if (entry->d_type == DT_DIR)
+ continue;
+
+ if (stristr(entry->d_name, FileName))
+ {
+ sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), FormSet, DirName, entry->d_name);
+ closedir(dir);
+ break;
+ }
+ }
+ closedir(dir);
+
+#else
+
+ sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), FormSet, DirName, FileName);
+
+#endif
+
+ if (stat(MsgFile, &STAT) != -1)
+ {
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == 0)
+ {
+ MsgBytes = _strdup("File is missing");
+ return MsgBytes;
+ }
+
+ FileSize = STAT.st_size;
+ MsgBytes = malloc(FileSize * 2); // Allow plenty of room for template substitution
+ ReadLen = fread(MsgBytes, 1, FileSize, hFile);
+ MsgBytes[FileSize] = 0;
+ fclose(hFile);
+
+ return MsgBytes;
+ }
+
+ return NULL;
+}
+
+int ReturnRawMessage(struct UserInfo * User, struct MsgInfo * Msg, char * Key, char * Reply, char * RawMessage, int len, char * ErrorString)
+{
+ char * ErrorMsg = malloc(len + 100);
+ char * ptr;
+ char DownLoad[256];
+
+ sprintf(DownLoad, "Save Attachments | ", Key, Msg->number);
+
+ RawMessage[strlen(RawMessage)] = '.'; // We null terminated file name
+ RawMessage[strlen(RawMessage)] = ' '; // We null terminated file name Len = XML - RawMessage;
+
+ RawMessage[len] = 0;
+
+ // Body seems to have cr cr lf which causes double space
+
+ ptr = strstr(RawMessage, "\r\r");
+
+ while (ptr)
+ {
+ *ptr = ' ';
+ ptr = strstr(ptr, "\r\r");
+ }
+
+ sprintf(ErrorMsg, ErrorString, RawMessage);
+
+ len = sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, "textarea", ErrorMsg, "textarea");
+ free(ErrorMsg);
+ return len;
+}
+
+char * FindXMLVariable(WebMailInfo * WebMail, char * Var)
+{
+ KeyValues * XMLKey = &WebMail->XMLKeys[0];
+
+ while (XMLKey->Key)
+ {
+ if (_stricmp(Var, XMLKey->Key) == 0)
+ {
+ return XMLKey->Value;
+ }
+
+ XMLKey++;
+ }
+ return NULL;
+}
+
+
+BOOL ParseXML(WebMailInfo * WebMail, char * XMLOrig)
+{
+ char * XML = _strdup(XMLOrig); // Get copy so we can mess about with it
+ char * ptr1, * ptr2, * ptr3;
+ KeyValues * XMLKeys = &WebMail->XMLKeys[0];
+
+ // Extract Fields (stuff between < and >. Ignore Whitespace between fields
+
+ // Add FormFolder Key with our folder
+
+// XMLKeys->Key = "FormFolder";
+// XMLKeys->Value = _strdup(FormDir);
+
+// XMLKeys++;
+
+ ptr1 = strstr(XML, "");
+
+ while (ptr1)
+ {
+ ptr2 = strchr(++ptr1, '>');
+
+ if (ptr2 == NULL)
+ goto quit;
+
+ *ptr2++ = 0;
+
+ ptr3 = strstr(ptr2, ""); // end of value string
+ if (ptr3 == NULL)
+ goto quit;
+
+ *ptr3++ = 0;
+
+ XMLKeys->Key = _strdup(ptr1);
+ XMLKeys->Value = _strdup(ptr2);
+
+ XMLKeys++;
+
+ ptr1 = strchr(ptr3, '<');
+
+ if (_memicmp(ptr1, "", 2) == 0)
+ {
+ // end of a parameter block. Find start of next block
+
+ ptr1 = strchr(++ptr1, '<');
+ ptr1 = strchr(++ptr1, '<'); // Skip start of next block
+ }
+ }
+
+
+
+
+quit:
+ free(XML);
+ return TRUE;
+}
+/*
+?xml version="1.0"?>
+
+
+ 1,0
+ 6.0.16.38 Debug Build
+ 20181022105202
+ G8BPQ
+
+ Alaska_ARES_ICS213_Initial_Viewer.html
+ Alaska_ARES_ICS213_SendReply.txt
+
+
+ g8bpq
+
+ G8BPQ
+
+
+ false
+ false
+ false
+ false
+ G8BPQ-1
+ Routine
+
+
+
+ Here
+
+ 2018-10-22
+ Test
+ John
+ John
+ Test
+ 2018-10-22 11:51
+
+ Me
+ Me
+ Submit
+
+
+*/
+
+
+char HTMLNotFoundMsg[] = " *** HTML Template for message not found - displaying raw content ***\r\n\r\n%s";
+char VarNotFoundMsg[] = " *** Variable {%s} not found in message - displaying raw content ***\r\n\r\n%s";
+char BadXMLMsg[] = " *** XML for Variable {%s} invalid - displaying raw content ***\r\n\r\n%s";
+char BadTemplateMsg[] = " *** Template near \"%s\" invalid - displaying raw content ***\r\n\r\n%s";
+
+int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen)
+{
+ WebMailInfo * WebMail = Session->WebMail;
+ struct UserInfo * User = Session->User;
+ char * Key = Session->Key;
+ int Len = 0;
+ char * Form;
+ char * SaveReply = Reply;
+ char FN[MAX_PATH] = "";
+ char * formptr, * varptr, * xmlptr;
+ char * ptr = NULL, * endptr, * xmlend;
+ size_t varlen, xmllen;
+ char var[100] = "<";
+ KeyValues * KeyValue;
+ struct HtmlFormDir * Dir;
+ char FormDir[MAX_PATH];
+
+ if (ParseXML(WebMail, XML))
+ ptr = FindXMLVariable(WebMail, "display_form");
+
+ if (ptr == NULL) // ?? No Display Form Specified??
+ {
+ // Not found - just display as normal message
+
+ return ReturnRawMessage(User, Msg, Key, Reply, RawMessage, (int)(XML - RawMessage), HTMLNotFoundMsg);
+ }
+
+ strcpy(FN, ptr);
+
+ Form = GetHTMLViewerTemplate(FN, &Dir);
+
+ sprintf(FormDir, "WMFile/%s/%s/", Dir->FormSet, Dir->DirName);
+
+
+
+ if (Form == NULL)
+ {
+ // Not found - just display as normal message
+
+ return ReturnRawMessage(User, Msg, Key, Reply, RawMessage, (int)(XML - RawMessage), HTMLNotFoundMsg);
+ }
+
+ formptr = Form;
+
+ // Search for {var xxx} strings in form and replace with
+ // corresponding variable in xml
+
+ // Don't know why, but {MsgOriginalBody} is sent instead of {var MsgOriginalBody}
+
+ // So is {FormFolder} instread of {var FormFolder}
+
+ // As a fiddle replace {FormFolder} with {var Folder} and look for that
+
+ while (varptr = stristr(Form, "{FormFolder}"))
+ {
+ memcpy(varptr, "{var ", 5);
+ }
+
+ varptr = stristr(Form, "{MsgOriginalBody}");
+ {
+ char * temp, * tempsave;
+ char * xvar, * varsave, * ptr;
+
+ if (varptr)
+ {
+ varptr++;
+
+ endptr = strchr(varptr, '}');
+ varlen = endptr - varptr;
+
+ if (endptr == NULL || varlen > 99)
+ {
+ // corrupt template - display raw message
+
+ char Err[256];
+
+ varptr[20] = 0;
+
+ sprintf(Err, BadTemplateMsg, varptr - 5, "%s");
+ return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
+ }
+
+ memcpy(var + 1, varptr, varlen);
+ var[++varlen] = '>';
+ var[++varlen] = 0;
+
+ xmlptr = stristr(XML, var);
+
+ if (xmlptr)
+ {
+ xmlptr += varlen;
+
+ xmlend = strstr(xmlptr, "");
+
+ if (xmlend == NULL)
+ {
+ // Bad XML - return error message
+
+ char Err[256];
+
+ // remove <> from var as it confuses html
+
+ var[strlen(var) -1] = 0;
+
+ sprintf(Err, BadXMLMsg, var + 1, "%s");
+ return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
+ }
+
+ xmllen = xmlend - xmlptr;
+ }
+ else
+ {
+ // Variable not found - return error message
+
+ char Err[256];
+
+ // remove <> from var as it confuses html
+
+ var[strlen(var) -1] = 0;
+
+ sprintf(Err, VarNotFoundMsg, var + 1, "%s");
+ return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
+ }
+
+ // Ok, we have the position of the variable and the substitution text.
+ // Copy message up to variable to Result, then copy value
+
+ // We create a copy so we can rescan later.
+ // We also need to replace CR or CRLF with
+
+ xvar = varsave = malloc(xmllen * 2);
+
+ ptr = xmlptr;
+
+ while(ptr < xmlend)
+ {
+ while (*ptr == '\n')
+ ptr++;
+
+ if (*ptr == '\r')
+ {
+ *ptr++;
+ strcpy(xvar, "
");
+ xvar += 4;
+ }
+ else
+ *(xvar++) = *(ptr++);
+ }
+ *xvar = 0;
+
+ temp = tempsave = malloc(strlen(Form) + strlen(XML));
+
+ memcpy(temp, formptr, varptr - formptr - 1); // omit "{"
+ temp += (varptr - formptr - 1);
+
+ strcpy(temp, varsave);
+ temp += strlen(varsave);
+ free(varsave);
+
+ formptr = endptr + 1;
+ strcpy(temp, formptr);
+ strcpy(Form, tempsave);
+ free(tempsave);
+ }
+ }
+
+ formptr = Form;
+
+ varptr = stristr(Form, "{var ");
+
+ while (varptr)
+ {
+ varptr+= 5;
+
+ endptr = strchr(varptr, '}');
+
+ varlen = endptr - varptr;
+
+ if (endptr == NULL || varlen > 99)
+ {
+ // corrupt template - display raw message
+
+ char Err[256];
+
+ varptr[20] = 0;
+
+ sprintf(Err, BadTemplateMsg, varptr - 5, "%s");
+ return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
+ }
+
+ memcpy(var, varptr, varlen);
+ var[varlen] = 0;
+
+ KeyValue = &WebMail->XMLKeys[0];
+
+ while (KeyValue->Key)
+ {
+ if (_stricmp(var, "Folder") == 0)
+ {
+ // Local form folder, not senders
+
+ xmllen = strlen(FormDir);
+
+ // Ok, we have the position of the variable and the substitution text.
+ // Copy message up to variable to Result, then copy value
+
+ memcpy(Reply, formptr, varptr - formptr - 5); // omit "{var "
+ Reply += (varptr - formptr - 5);
+
+ strcpy(Reply, FormDir);
+ Reply += xmllen;
+ break;
+ }
+
+ if (_stricmp(var, KeyValue->Key) == 0)
+ {
+ xmllen = strlen(KeyValue->Value);
+
+ // Ok, we have the position of the variable and the substitution text.
+ // Copy message up to variable to Result, then copy value
+
+ memcpy(Reply, formptr, varptr - formptr - 5); // omit "{var "
+ Reply += (varptr - formptr - 5);
+
+ strcpy(Reply, KeyValue->Value);
+ Reply += xmllen;
+ break;
+ }
+
+ KeyValue++;
+
+ if (KeyValue->Key == NULL)
+ {
+ // Not found in XML
+
+ char Err[256];
+
+ sprintf(Err, VarNotFoundMsg, var, "%s");
+ return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
+ }
+
+ }
+
+ formptr = endptr + 1;
+
+ varptr = stristr(endptr, "{var ");
+ }
+
+ // copy remaining
+
+ // Remove as we add it later
+
+ ptr = strstr(formptr, "");
+
+ if (ptr)
+ *ptr = 0;
+
+ strcpy(Reply, formptr);
+
+ // Add Webmail header between and form data
+
+ ptr = stristr(SaveReply, "');
+ if (ptr)
+ {
+ char * temp = malloc(strlen(SaveReply) + 1000);
+ size_t len = ++ptr - SaveReply;
+ memcpy(temp, SaveReply, len);
+
+ sprintf(&temp[len],
+ ""
+ " %s Webmail Interface - User %s - Message %d
"
+ "", BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, Key, Msg->number, Key, Key, Key);
+
+ strcat(temp, ptr);
+
+ strcpy(SaveReply, temp);
+ free(temp);
+ }
+ }
+
+ if (Form)
+ free(Form);
+
+ return (int)strlen(SaveReply);
+}
+
+char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls)
+{
+ // Create B2 Header
+
+ char * NewMsg = malloc(100000);
+ char * SaveMsg = NewMsg;
+ char DateString[80];
+ struct tm * tm;
+ int n;
+ char Type[16] = "Private";
+
+ // Get Type
+
+ if (Msg->type == 'B')
+ strcpy(Type, "Bulletin");
+ else if (Msg->type == 'T')
+ strcpy(Type, "Traffic");
+
+ tm = gmtime((time_t *)&Msg->datecreated);
+
+ sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+
+ NewMsg += sprintf(NewMsg,
+ "MID: %s\r\n"
+ "Date: %s\r\n"
+ "Type: %s\r\n"
+ "From: %s\r\n",
+ Msg->bid, DateString, Type, Msg->from);
+
+ if (ToCalls)
+ {
+ int i;
+
+ for (i = 0; i < Calls; i++)
+ NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]);
+ }
+ else
+ {
+ NewMsg += sprintf(NewMsg, "To: %s\r\n",
+ WebMail->To);
+ }
+ if (WebMail->CC && WebMail->CC[0])
+ NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC);
+
+ NewMsg += sprintf(NewMsg,
+ "Subject: %s\r\n"
+ "Mbo: %s\r\n",
+ Msg->title, BBSName);
+
+ NewMsg += sprintf(NewMsg, "Body: %d\r\n", (int)strlen(WebMail->Body) + WebMail->HeaderLen + WebMail->FooterLen);
+
+ Msg->B2Flags = B2Msg;
+
+ if (WebMail->XML)
+ {
+ Msg->B2Flags |= Attachments;
+ NewMsg += sprintf(NewMsg, "File: %d %s\r\n",
+ WebMail->XMLLen, WebMail->XMLName);
+ }
+
+ for (n = 0; n < WebMail->Files; n++)
+ {
+ Msg->B2Flags |= Attachments;
+ NewMsg += sprintf(NewMsg, "File: %d %s\r\n",
+ WebMail->FileLen[n], WebMail->FileName[n]);
+ }
+
+ NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header
+
+ return SaveMsg;
+}
+
+VOID WriteOneRecipient(struct MsgInfo * Msg, WebMailInfo * WebMail, int MsgLen, char ** ToCalls, int Calls, char * BID)
+{
+ FILE * hFile;
+ char * via = NULL;
+ BIDRec * BIDRec;
+ char MsgFile[MAX_PATH];
+ size_t WriteLen=0;
+ char * B2Header;
+
+ if (strlen(WebMail->Subject) > 60)
+ WebMail->Subject[60] = 0;
+
+ strcpy(Msg->title, WebMail->Subject);
+
+ if (strlen(BID) == 0)
+ sprintf_s(BID, 32, "%d_%s", LatestMsg, BBSName);
+
+ strcpy(Msg->bid, BID);
+
+ Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
+
+ BIDRec = AllocateBIDRecord();
+
+ strcpy(BIDRec->BID, Msg->bid);
+ BIDRec->mode = Msg->type;
+ BIDRec->u.msgno = LOWORD(Msg->number);
+ BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
+
+ Msg->length = MsgLen;
+
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
+
+ // We write a B2 Header, Body and XML attachment if present
+
+ B2Header = BuildB2Header(WebMail, Msg, ToCalls, Calls);
+
+ hFile = fopen(MsgFile, "wb");
+
+ if (hFile)
+ {
+ int i;
+
+ WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile);
+ WriteLen += fwrite(WebMail->Body, 1, Msg->length, hFile);
+ WriteLen += fwrite("\r\n", 1, 2, hFile);
+ if (WebMail->XML)
+ {
+ WriteLen += fwrite(WebMail->XML, 1, WebMail->XMLLen, hFile);
+ WriteLen += fwrite("\r\n", 1, 2, hFile);
+ }
+ // Do any attachments
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile);
+ WriteLen += fwrite("\r\n", 1, 2, hFile);
+ }
+ fclose(hFile);
+ }
+
+ free(B2Header);
+
+ Msg->length = (int)WriteLen;
+
+ MatchMessagetoBBSList(Msg, 0);
+
+ if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
+ Msg->status = '$'; // Has forwarding
+
+ BuildNNTPList(Msg); // Build NNTP Groups list
+
+#ifndef NOMQTT
+ if (MQTT)
+ MQTTMessageEvent(Msg);
+#endif
+
+}
+
+
+VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest)
+{
+ int ReplyLen = 0;
+ struct MsgInfo * Msg;
+ char * ptr, *input, *context;
+ size_t MsgLen;
+ char Type;
+ char * via = NULL;
+ char BID[32];
+ size_t WriteLen=0;
+ char * Body = NULL;
+ char * To = NULL;
+ char * CC = NULL;
+ char * HDest = NULL;
+ char * Title = NULL;
+ char * Vptr = NULL;
+ char * Context;
+ char Prompt[256] = "Message Saved";
+ char OrigTo[256];
+ WebMailInfo * WebMail = Session->WebMail;
+ BOOL SendMsg = FALSE;
+ BOOL SendReply = FALSE;
+
+ char * RMSTo[1000] = {NULL}; // Winlink addressees
+ char * PKTT0[1000] = {NULL}; // Packet addressees
+
+ __int32 Recipients = 0;
+ __int32 RMSMsgs = 0, BBSMsgs = 0;
+
+
+ input = strstr(MsgPtr, "\r\n\r\n"); // End of headers
+
+ if (input == NULL)
+ return;
+
+ if (strstr(input, "Cancel=Cancel"))
+ {
+ *RLen = sprintf(Reply, "", Session->Key);
+ return;
+ }
+
+ if (WebMail->txtFileName == NULL)
+ {
+ // No template, so user must have used back button
+
+ *RLen = sprintf(Reply, "", Session->Key);
+ return;
+ }
+
+ ptr = strtok_s(input + 4, "&", &Context);
+
+ while (ptr)
+ {
+ char * val = strlop(ptr, '=');
+
+ if (strcmp(ptr, "To") == 0)
+ HDest = To = val;
+ else if (strcmp(ptr, "CC") == 0)
+ CC = val;
+ else if (strcmp(ptr, "Subj") == 0)
+ Title = val;
+ else if (strcmp(ptr, "Type") == 0)
+ Type = val[0];
+ else if (strcmp(ptr, "BID") == 0)
+ strcpy(BID, val);
+ else if (strcmp(ptr, "Msg") == 0)
+ Body = _strdup(val);
+
+ ptr = strtok_s(NULL, "&", &Context);
+
+ }
+ strlop(BID, ' ');
+ if (strlen(BID) > 12)
+ BID[12] = 0;
+
+ UndoTransparency(To);
+ UndoTransparency(CC);
+ UndoTransparency(BID);
+ UndoTransparency(HDest);
+ UndoTransparency(Title);
+ UndoTransparency(Body);
+
+ MsgLen = strlen(Body);
+
+ // The user could have changed any of the input fields.
+
+ if (To && To[0])
+ {
+ free (WebMail->To);
+ WebMail->To = _strdup(To);
+ }
+
+ if (CC && CC[0])
+ {
+ free (WebMail->CC);
+ WebMail->CC = _strdup(CC);
+ }
+
+ if (Title && Title[0])
+ {
+ free (WebMail->Subject);
+ WebMail->Subject = _strdup(Title);
+ }
+
+ if (Body && Body[0])
+ {
+ free (WebMail->Body);
+ WebMail->Body = _strdup(Body);
+ }
+
+ // We will put the supplied address in the B2 header
+
+ // We may need to change the HDest to make sure message
+ // is delivered to Internet or Packet as requested
+
+ if (HDest == NULL || HDest[0] == 0)
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ return;
+ }
+
+ // Multiple TO fields could be more than 255 bytes long
+
+// if (strlen(HDest) > 255)
+// {
+// *RLen = sprintf(Reply, "%s", "");
+// return;
+// }
+
+ if (strlen(BID))
+ {
+ if (LookupBID(BID))
+ {
+ // Duplicate bid
+ *RLen = sprintf(Reply, "%s", "");
+ return;
+ }
+ }
+
+ if (Type == 'B')
+ {
+ if (RefuseBulls)
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ return;
+ }
+ }
+
+ // We should be able to handle multiple recipients, with all Winlink addresses sent in one message and
+ // multiple copies for packet addressess. For Winlink we should use multiple To@ lines in B2 Header
+
+ ptr = strtok_s(HDest, " ;", &context);
+
+ while (ptr && ptr[0])
+ {
+ int Winlink = 0;
+ int AMPR = 0;
+
+ char * dest = zalloc(256);
+ char * via = NULL;
+
+ strcpy(dest, ptr);
+
+ // See if packet or Winlink
+
+ // If Type=Winlink specified send plain call as @winlink.org
+
+ if (strchr(dest, '@') == 0 && WebMail->Winlink)
+ strcat(dest, "@winlink.org");
+
+ if (_memicmp(dest, "rms:", 4) == 0 || _memicmp(dest, "rms/", 4) == 0)
+ {
+ memcpy(dest, &dest[4], strlen(dest));
+ Winlink = 1;
+ RMSTo[RMSMsgs++] = dest;
+ ptr = strtok_s(NULL, " ;", &context);
+ continue;
+ }
+
+ else if (_memicmp(dest, "smtp:", 5) == 0)
+ {
+ if (ISP_Gateway_Enabled)
+ {
+ via = &dest[5];
+ dest[0] = 0;
+ }
+ }
+ else if (strchr(dest, '@'))
+ {
+ strcpy(OrigTo, dest);
+
+ via = strlop(dest, '@');
+
+ if (via)
+ {
+ // If looks like a valid email address, treat as such
+
+ if (strlen(via) > 6 || !CheckifPacket(via))
+ {
+ // Assume Email address. See if to send via RMS or SMTP
+
+ if (isAMPRMsg(OrigTo))
+ {
+ dest[strlen(dest)] = '@'; // Put back together
+ memmove(&dest[1], dest, strlen(dest));
+ dest[0] = 0;
+ via = &dest[1];
+ AMPR = 1;
+ }
+ else if (FindRMS() || strchr(via, '!')) // have RMS or source route
+ {
+ dest[strlen(dest)] = '@'; // Put back together
+ Winlink = 1;
+ RMSTo[RMSMsgs++] = dest;
+ ptr = strtok_s(NULL, " ;", &context);
+ continue;
+ }
+ else if (ISP_Gateway_Enabled)
+ {
+ dest[strlen(dest)] = '@'; // Put back together
+ memmove(dest, &dest[1], strlen(dest));
+ dest[0] = 0;
+ }
+ else
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ return;
+
+ }
+ }
+ }
+ }
+ else
+ {
+ // No @
+
+ if (strlen(dest) > 6)
+ dest[6] = 0;
+ }
+
+ // This isn't an RMS Message, so can queue now
+
+ Msg = AllocateMsgRecord();
+
+ // Set number here so they remain in sequence
+
+ Msg->number = ++LatestMsg;
+ MsgnotoMsg[Msg->number] = Msg;
+
+ strcpy(Msg->title, WebMail->Subject);
+ Msg->type = Type;
+ Msg->status = 'N';
+
+
+ strcpy(Msg->from, Session->User->Call);
+
+ strcpy(Msg->to, _strupr(dest));
+
+ if (SendBBStoSYSOPCall)
+ if (_stricmp(dest, BBSName) == 0)
+ strcpy(Msg->to, SYSOPCall);
+
+ if (via)
+ {
+ if (strlen(via) > 40)
+ via[40] = 0;
+
+ strcpy(Msg->via, _strupr(via));
+ }
+ else
+ {
+ // No via. If not local user try to add BBS
+
+ struct UserInfo * ToUser = LookupCall(Msg->to);
+
+ if (ToUser)
+ {
+ // Local User. If Home BBS is specified, use it
+
+ if (ToUser->flags & F_RMSREDIRECT)
+ {
+ // sent to Winlink
+
+ strcpy(Msg->via, WinlinkAddr);
+ sprintf(Prompt, "Redirecting to winlink.org\r");
+ }
+ else if (ToUser->HomeBBS[0])
+ {
+ strcpy(Msg->via, ToUser->HomeBBS);
+ sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via);
+ }
+ else
+ {
+ // No HomeBBS - Check WP
+
+ WPRecP WP = LookupWP(Msg->to);
+
+ if (WP)
+ {
+ strcpy(Msg->via, WP->first_homebbs);
+ sprintf(Prompt, "%s added from WP", Msg->via);
+ }
+ }
+ }
+ else
+ {
+ // Not local user - Check WP
+
+ WPRecP WP = LookupWP(Msg->to);
+
+ if (WP)
+ {
+ strcpy(Msg->via, WP->first_homebbs);
+ sprintf(Prompt, "%s added from WP", Msg->via);
+ }
+ }
+ }
+ WriteOneRecipient(Msg, WebMail, MsgLen, NULL, 0, BID);
+ ptr = strtok_s(NULL, " ;", &context);
+ free(dest);
+ BID[0] = 0; // Can't use more than once
+ }
+
+ if (RMSMsgs)
+ {
+ // Write one message to all Winlink addresses
+
+ int i;
+
+ Msg = AllocateMsgRecord();
+
+ // Set number here so they remain in sequence
+
+ Msg->number = ++LatestMsg;
+ MsgnotoMsg[Msg->number] = Msg;
+
+ strcpy(Msg->title, WebMail->Subject);
+ Msg->type = Type;
+ Msg->status = 'N';
+
+ strcpy(Msg->from, Session->User->Call);
+ strcpy(Msg->to, "RMS");
+
+ WriteOneRecipient(Msg, WebMail, MsgLen, RMSTo, RMSMsgs, BID);
+
+ for (i = 0; i < RMSMsgs; i++)
+ free(RMSTo[i]);
+
+ }
+
+ SaveMessageDatabase();
+ SaveBIDDatabase();
+
+ *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt);
+
+ FreeWebMailFields(WebMail);
+
+ return;
+}
+
+
+
+VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile)
+{
+ WebMailInfo * WebMail = Session->WebMail;
+ struct UserInfo * User = Session->User;
+ KeyValues * txtKey = WebMail->txtKeys;
+
+ char * inptr, * varptr, * endptr;
+ int varlen, vallen;
+
+ while (txtKey->Key != NULL)
+ {
+ inptr = WebMail->txtFile;
+
+ varptr = stristr(inptr, txtKey->Key);
+
+ while (varptr)
+ {
+ // Move the remaining message up/down the buffer to make space for substitution
+
+ varlen = (int)strlen(txtKey->Key);
+
+ if (txtKey->Value)
+ vallen = (int)strlen(txtKey->Value);
+ else
+ vallen = 0;
+
+ endptr = varptr + varlen;
+
+ memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end
+ memcpy(varptr, txtKey->Value, vallen);
+
+ inptr = endptr + 1;
+
+ varptr = stristr(inptr, txtKey->Key);
+ }
+ txtKey++;
+ }
+}
+
+
+
+VOID BuildMessageFromHTMLInput(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Keys[1000], char * Values[1000], int NumKeys)
+{
+ int ReplyLen = 0;
+ struct MsgInfo * Msg;
+ int MsgLen;
+ FILE * hFile;
+ char Type = 'P';
+ BIDRec * BIDRec;
+ char * MailBuffer;
+ char MsgFile[MAX_PATH];
+ int WriteLen=0;
+ char Prompt[256] = "Message Saved";
+ char OrigTo[256];
+ WebMailInfo * WebMail = Session->WebMail;
+ char * HDest = _strdup(WebMail->To);
+ char * Vptr = NULL;
+ char BID[16] = "";
+
+/// if (strlen(HDest) > 255)
+/// {
+// *RLen = sprintf(Reply, "%s", "");
+// return;
+// }
+
+ MsgLen = (int)strlen(WebMail->Body);
+ Msg = AllocateMsgRecord();
+
+ // Set number here so they remain in sequence
+
+ Msg->number = ++LatestMsg;
+ MsgnotoMsg[Msg->number] = Msg;
+
+ strcpy(Msg->from, Session->User->Call);
+
+ if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0)
+ {
+ Vptr=&HDest[4];
+ strcpy(Msg->to, "RMS");
+ }
+ else if (_memicmp(HDest, "smtp:", 5) == 0)
+ {
+ if (ISP_Gateway_Enabled)
+ {
+ Vptr=&HDest[5];
+ Msg->to[0] = 0;
+ }
+ }
+ else if (strchr(HDest, '@'))
+ {
+ strcpy(OrigTo, HDest);
+
+ Vptr = strlop(HDest, '@');
+
+ if (Vptr)
+ {
+ // If looks like a valid email address, treat as such
+
+ if (strlen(HDest) > 6 || !CheckifPacket(Vptr))
+ {
+ // Assume Email address
+
+ Vptr = OrigTo;
+
+ if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route
+ strcpy(Msg->to, "RMS");
+ else if (ISP_Gateway_Enabled)
+ Msg->to[0] = 0;
+ else if (isAMPRMsg(OrigTo))
+ strcpy(Msg->to, "RMS"); // Routing will redirect it
+ else
+ {
+ *RLen = sprintf(Reply, "%s", "");
+ return;
+
+ }
+ }
+ }
+ }
+
+ else
+ {
+ if (strlen(HDest) > 6)
+ HDest[6] = 0;
+
+ strcpy(Msg->to, _strupr(HDest));
+ }
+
+ if (SendBBStoSYSOPCall)
+ if (_stricmp(HDest, BBSName) == 0)
+ strcpy(Msg->to, SYSOPCall);
+
+ if (Vptr)
+ {
+ if (strlen(Vptr) > 40)
+ Vptr[40] = 0;
+
+ strcpy(Msg->via, _strupr(Vptr));
+ }
+ else
+ {
+ // No via. If not local user try to add BBS
+
+ struct UserInfo * ToUser = LookupCall(Msg->to);
+
+ if (ToUser)
+ {
+ // Local User. If Home BBS is specified, use it
+
+ if (ToUser->flags & F_RMSREDIRECT)
+ {
+ // sent to Winlink
+
+ strcpy(Msg->via, WinlinkAddr);
+ sprintf(Prompt, "Redirecting to winlink.org\r");
+ }
+ else if (ToUser->HomeBBS[0])
+ {
+ strcpy(Msg->via, ToUser->HomeBBS);
+ sprintf(Prompt, "%s added from HomeBBS", Msg->via);
+ }
+ else
+ {
+ // No HomeBBS - Check WP
+
+ WPRecP WP = LookupWP(Msg->to);
+
+ if (WP)
+ {
+ strcpy(Msg->via, WP->first_homebbs);
+ sprintf(Prompt, "%s added from WP", Msg->via);
+ }
+ }
+ }
+ else
+ {
+ // Not local user - Check WP
+
+ WPRecP WP = LookupWP(Msg->to);
+
+ if (WP)
+ {
+ strcpy(Msg->via, WP->first_homebbs);
+ sprintf(Prompt, "%s added from WP", Msg->via);
+ }
+ }
+ }
+
+ if (strlen(WebMail->Subject) > 60)
+ WebMail->Subject[60] = 0;
+
+ strcpy(Msg->title, WebMail->Subject);
+ Msg->type = Type;
+ Msg->status = 'N';
+
+ if (strlen(BID) == 0)
+ sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName);
+
+ strcpy(Msg->bid, BID);
+
+ Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
+
+ BIDRec = AllocateBIDRecord();
+
+ strcpy(BIDRec->BID, Msg->bid);
+ BIDRec->mode = Msg->type;
+ BIDRec->u.msgno = LOWORD(Msg->number);
+ BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
+
+ MailBuffer = malloc(MsgLen + 2000); // Allow for a B2 Header if attachments
+
+ Msg->length = MsgLen;
+
+ BuildFormMessage(Session, Msg, Keys, Values, NumKeys);
+
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
+
+ hFile = fopen(MsgFile, "wb");
+
+ if (hFile)
+ {
+ WriteLen = (int)fwrite(WebMail->Body, 1, Msg->length, hFile);
+ fclose(hFile);
+ }
+
+ free(WebMail->Body);
+ free(HDest);
+
+ WebMail->Body = NULL;
+
+ MatchMessagetoBBSList(Msg, 0);
+
+ if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
+ Msg->status = '$'; // Has forwarding
+
+ BuildNNTPList(Msg); // Build NNTP Groups list
+
+#ifndef NOMQTT
+ if (MQTT)
+ MQTTMessageEvent(Msg);
+#endif
+
+
+ SaveMessageDatabase();
+ SaveBIDDatabase();
+
+ *RLen = sprintf(Reply, "", Prompt);
+
+ return;
+}
+
+
+
+
+
+
+
+
+
+void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen)
+{
+ // If there is no display html defined place data in a normal
+ // input window, else build the Message body and XML attachment and send
+
+ // I now think it is better to put data in a normal input window
+ // even if there is a display form so user can view it before submission
+
+ WebMailInfo * WebMail = Session->WebMail;
+
+ char * info = strstr(input, "\r\n\r\n"); // To end of HTML header
+
+ // look through header for Content-Type line, and if multipart
+ // find boundary string.
+
+ char * ptr, * saveptr, * ptr1, * ptr2, * inptr;
+ char Boundary[1000];
+ BOOL Multipart = FALSE;
+ int Partlen;
+ char ** Body = &info;
+ int i;
+ char * Keys[1000];
+ char * Values[1000];
+ char * saveForfree[1000];
+
+ int NumKeys = 0;
+ char * varptr;
+ char * endptr;
+ int varlen, vallen = 0;
+ char *crcrptr;
+
+ if (WebMail->txtFile == NULL)
+ {
+ // No template, so user must have used back button
+
+ *RLen = sprintf(Reply, "", Session->Key);
+ return;
+ }
+
+ ptr = input;
+
+ while(*ptr != 13)
+ {
+ ptr2 = strchr(ptr, 10); // Find CR
+
+ while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line
+ {
+ ptr2 = strchr(&ptr2[1], 10); // Find CR
+ }
+
+// Content-Type: multipart/mixed;
+// boundary="----=_NextPart_000_025B_01CAA004.84449180"
+// 7.2.2 The Multipart/mixed (primary) subtype
+// 7.2.3 The Multipart/alternative subtype
+
+
+ if (_memicmp(ptr, "Content-Type: ", 14) == 0)
+ {
+ char Line[1000] = "";
+ char lcLine[1000] = "";
+
+ char * ptr3;
+
+ memcpy(Line, &ptr[14], ptr2-ptr-14);
+ memcpy(lcLine, &ptr[14], ptr2-ptr-14);
+ _strlwr(lcLine);
+
+ if (_memicmp(Line, "Multipart/", 10) == 0)
+ {
+ Multipart = TRUE;
+
+
+ ptr3 = strstr(Line, "boundary");
+
+ if (ptr3)
+ {
+ ptr3+=9;
+
+ if ((*ptr3) == '"')
+ ptr3++;
+
+ strcpy(Boundary, ptr3);
+ ptr3 = strchr(Boundary, '"');
+ if (ptr3) *ptr3 = 0;
+ ptr3 = strchr(Boundary, 13); // CR
+ if (ptr3) *ptr3 = 0;
+
+ }
+ else
+ return; // Can't do anything without a boundary ??
+ }
+
+ }
+
+ ptr = ptr2;
+ ptr++;
+
+ }
+
+ if (info == NULL)
+ return; // Wot!
+
+ // Extract the Key/Value pairs from input data
+
+ saveptr = ptr = WebFindPart(Body, Boundary, &Partlen, input + InputLen);
+
+ if (ptr == NULL)
+ return; // Couldn't find separator
+
+ // Now extract fields
+
+ while (ptr)
+ {
+ char * endptr;
+ char * val;
+// Debugprintf(ptr);
+
+ // Format seems to be
+
+ //Content-Disposition: form-data; name="FieldName"
+ // crlf crlf
+ // field value
+ // crlf crlf
+
+ // No, is actually
+
+ // ------WebKitFormBoundary7XHZ1i7Jc8tOZJbw
+ // Content-Disposition: form-data; name="State"
+ //
+ // UK
+ // ------WebKitFormBoundary7XHZ1i7Jc8tOZJbw
+
+ // ie Value is terminated by ------WebKitFormBoundary7XHZ1i7Jc8tOZJbw
+ // But FindPart has returned length, so can use that
+ // Be aware that Part and PartLen include the CRLF which is actually part of the Boundary string so should be removed.
+
+
+ ptr = strstr(ptr, "name=");
+
+ if (ptr)
+ {
+ endptr = strstr(ptr, "\"\r\n\r\n"); // "/r/n/r/n
+ if (endptr)
+ {
+ *endptr = 0;
+ ptr += 6; // to start of name string
+ val = endptr + 5;
+
+ // val was Null Terminated by FindPart so can just use it. This assumes all fields are text,
+ // which I think is safe enough here.
+
+ saveptr[Partlen - 2] = 0;
+
+ // Now have key value pair
+
+ Keys[NumKeys] = ptr;
+ Values[NumKeys] = val;
+ saveForfree[NumKeys++] = saveptr; // so we can free() when finished with it
+ }
+ else
+ free(saveptr);
+ }
+ else
+ free(saveptr);
+
+ saveptr = ptr = WebFindPart(Body, Boundary, &Partlen, input + InputLen);
+ }
+
+ if (info == NULL)
+ return; // Wot!
+
+ info += 4;
+
+ // It looks like some standard variables can be used in User->Call;
+ Keys[NumKeys] = "MsgSubject";
+ Values[NumKeys++] = "";
+ Keys[NumKeys] = "MsgBody";
+// if (WebMail->OrigBody)
+// txtKey++->Value = _strdup(WebMail->OrigBody);
+// else
+ Values[NumKeys++] = "";
+
+ Keys[NumKeys] = _strdup("MsgP2P");
+ Values[NumKeys++] = _strdup("");
+
+ Keys[NumKeys] = _strdup("MsgIsReply");
+ if (WebMail->isReply)
+ Values[NumKeys++] = "True";
+ else
+ Values[NumKeys++] = "True";
+
+ Keys[NumKeys] = "MsgIsForward";
+ Values[NumKeys++] = "False";
+ Keys[NumKeys] = "MsgIsAcknowledgement";
+ Values[NumKeys++] = "False";
+
+
+ // Update Template with variables from the form
+
+ // I've going to update the template in situ, as I can't see a better way
+ // of making sure all occurances of variables in any order are substituted.
+ // The space allocated to Template is twice the size of the file
+ // to allow for insertions
+
+ inptr = WebMail->txtFile;
+
+ // Search for "" strings in form and replace with
+ // corresponding variable
+
+ // we run through the Template once for each variable
+
+ i = 0;
+
+ while (i < NumKeys)
+ {
+ char Key[256];
+
+ sprintf(Key, "", Keys[i]);
+
+ inptr = WebMail->txtFile;
+ varptr = stristr(inptr, Key);
+
+ while (varptr)
+ {
+ // Move the remaining message up/down the buffer to make space for substitution
+
+ varlen = (int)strlen(Key);
+ vallen = (int)strlen(Values[i]);
+
+ endptr = varptr + varlen;
+
+ memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end
+ memcpy(varptr, Values[i], vallen);
+
+ inptr = endptr + 1;
+
+ varptr = stristr(inptr, Key);
+ }
+ i++;
+ }
+
+ // We need to look for To:, CC: and Subject lines, and remove any other
+ // Var: lines. Use everything following Msg: as the plain text body
+
+ // Find start of Message body
+
+ ptr = WebMail->txtFile;
+
+ ptr1 = strchr(ptr, '\r');
+
+ while (ptr1)
+ {
+ if (_memicmp(ptr, "Msg:", 4) == 0)
+ {
+ // Rest is message body. substitutions have been done
+
+ if (WebMail->Body)
+ free(WebMail->Body);
+
+ WebMail->Body = _strdup(ptr + 4); // Remove Msg:
+ break;
+ }
+
+ // Can now terminate lines
+
+ *ptr1++ = 0;
+
+ while (*ptr1 == '\r' || *ptr1 == '\n')
+ *ptr1++ = 0;
+
+ if (_memicmp(ptr, "To:", 3) == 0)
+ {
+ if (strlen(ptr) > 5)
+ WebMail->To = _strdup(&ptr[3]);
+ }
+ else if (_memicmp(ptr, "CC:", 3) == 0)
+ {
+ if (strlen(ptr) > 5)
+ WebMail->CC = _strdup(&ptr[3]);
+ }
+ else if (_memicmp(ptr, "Subj:", 5) == 0)
+ {
+ if (ptr[5] == ' ') // May have space after :
+ ptr++;
+ if (strlen(ptr) > 6)
+ WebMail->Subject = _strdup(&ptr[5]);
+ }
+
+ else if (_memicmp(ptr, "Subject:", 8) == 0)
+ {
+ if (ptr[8] == ' ')
+ ptr++;
+ if (strlen(ptr) > 9)
+ WebMail->Subject = _strdup(&ptr[8]);
+ }
+ ptr = ptr1;
+ ptr1 = strchr(ptr, '\r');
+ }
+
+ if (WebMail->Subject == NULL)
+ WebMail->Subject = _strdup("");
+
+ if (WebMail->To == NULL)
+ WebMail->To = _strdup("");
+
+ if (WebMail->CC == NULL)
+ WebMail->CC = _strdup("");
+
+ // Replace var in Subject
+
+ if (_memicmp(WebMail->Subject, "Subject = realloc(WebMail->Subject, 512); // Plenty of space
+ i = 0;
+
+ while (i < NumKeys)
+ {
+ char Key[256];
+
+ sprintf(Key, "", Keys[i]);
+
+ inptr = WebMail->Subject;
+ varptr = stristr(inptr, Key);
+
+ while (varptr)
+ {
+ // Move the remaining message up/down the buffer to make space for substitution
+
+ varlen = (int)strlen(Key);
+ vallen = (int)strlen(Values[i]);
+
+ endptr = varptr + varlen;
+
+ memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end
+ memcpy(varptr, Values[i], vallen);
+
+ inptr = endptr + 1;
+
+ varptr = stristr(inptr, Key);
+ }
+ i++;
+ }
+ }
+
+
+
+ // Build XML Attachment if Display Form is defined
+
+ if (WebMail->DisplayHTMLName)
+ BuildXMLAttachment(Session, Keys, Values, NumKeys);
+
+ // if Reply, attach original message to Body;
+
+ if (WebMail->isReply && WebMail->OrigBody)
+ {
+ char * NewBody = malloc(strlen(WebMail->Body) + strlen(WebMail->OrigBody) + 100);
+ sprintf(NewBody, "%s\r\n%s", WebMail->Body, WebMail->OrigBody);
+ free(WebMail->Body);
+ WebMail->Body = NewBody;
+ }
+
+ // Display Message for user to check and send
+
+ // fix any cr cr lf sequence
+
+ crcrptr = strstr(WebMail->Body, "\r\r");
+
+ while (crcrptr)
+ {
+ *crcrptr = ' ';
+ crcrptr = strstr(crcrptr, "\r\r");
+ }
+
+ if (WebMail->BID == NULL)
+ WebMail->BID = _strdup("");
+
+ *RLen = sprintf(Reply, CheckFormMsgPage, Session->Key, WebMail->To, WebMail->CC, WebMail->Subject, "Selected", "", "", WebMail->BID, WebMail->Body);
+
+ // Free the part strings
+
+ i = 0;
+
+ while (saveForfree[i])
+ free(saveForfree[i++]);
+}
+
+// XML Template Stuff
+
+char XMLHeader [] =
+ "\r\n"
+ "\r\n"
+ " \r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " \r\n"
+ "\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n"
+ " %s\r\n";
+
+
+char XMLLine[] = " <%s>%s%s>\r\n";
+
+char XMLTrailer[] = "\r\n\r\n";
+
+char * doXMLTransparency(char * string)
+{
+ // Make sure string doesn't contain forbidden XML chars (<>"'&)
+
+ char * newstring = malloc(5 * strlen(string) + 1); // If len is zero still need null terminator
+
+ char * in = string;
+ char * out = newstring;
+ char c;
+
+ c = *(in++);
+
+ while (c)
+ {
+ switch (c)
+ {
+ case '<':
+
+ strcpy(out, "<");
+ out += 4;
+ break;
+
+ case '>':
+
+ strcpy(out, ">");
+ out += 4;
+ break;
+
+ case '"':
+
+ strcpy(out, """);
+ out += 6;
+ break;
+
+ case '\'':
+
+ strcpy(out, "'");
+ out += 6;
+ break;
+
+ case '&':
+
+ strcpy(out, "&");
+ out += 5;
+ break;
+
+ default:
+
+ *(out++) = c;
+ }
+ c = *(in++);
+ }
+
+ *(out++) = 0;
+ return newstring;
+}
+
+
+VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys)
+{
+ // Create XML Attachment for form
+
+ WebMailInfo * WebMail = Session->WebMail;
+ char XMLName[MAX_PATH];
+ char * XMLPtr;
+ char DateString[80];
+ struct tm * tm;
+ time_t NOW = time(NULL);
+
+ int n;
+ int TotalFileSize = 0;
+
+ tm = gmtime(&NOW);
+
+ sprintf(DateString, "%04d%02d%02d%02d%02d%02d",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ strcpy(XMLName, WebMail->DisplayHTMLName);
+ XMLName[strlen(XMLName) - 5] = 0; // remove .html
+
+ WebMail->XMLName = malloc(MAX_PATH);
+ WebMail->XML = XMLPtr = malloc(100000);
+ WebMail->XMLLen = 0;
+
+ sprintf(WebMail->XMLName, "RMS_Express_Form_%s.xml", XMLName);
+
+ XMLPtr += sprintf(XMLPtr, XMLHeader,
+ "1,0", VersionString,
+ DateString,
+ Session->User->Call,
+ "", //Grid
+ WebMail->DisplayHTMLName,
+ WebMail->ReplyHTMLName,
+ WebMail->To,
+ WebMail->CC,
+ Session->User->Call,
+ WebMail->OrigSubject,
+ "", // WebMail->OrigBody,
+ "False", // P2P
+ WebMail->isReply ? "True": "False",
+ "False", // Forward,
+ "False"); // Ack
+
+ // create XML lines for Key/Value Pairs
+
+ for (n = 0; n < NumKeys; n++)
+ {
+ if (Values[n] == NULL)
+ Values[n] = _strdup("");
+
+ XMLPtr += sprintf(XMLPtr, XMLLine, Keys[n], Values[n], Keys[n]);
+ }
+ if (WebMail->isReply)
+ {
+ if (WebMail->OrigBody)
+ {
+ char * goodXML = doXMLTransparency(WebMail->OrigBody);
+ XMLPtr += sprintf(XMLPtr, XMLLine, "MsgOriginalBody", goodXML, "MsgOriginalBody");
+ }
+ else
+ XMLPtr += sprintf(XMLPtr, XMLLine, "MsgOriginalBody", "", "MsgOriginalBody");
+ }
+
+ XMLPtr += sprintf(XMLPtr, "%s", XMLTrailer);
+ WebMail->XMLLen = (int)(XMLPtr - WebMail->XML);
+}
+
+char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys)
+{
+
+ // Create B2 message with template body and xml attachment
+
+ char * NewMsg = malloc(100000);
+ char * SaveMsg = NewMsg;
+ char * XMLPtr;
+
+ char DateString[80];
+ struct tm * tm;
+
+ char * FileName[100];
+ int FileLen[100];
+ char * FileBody[100];
+ int n, Files = 0;
+ int TotalFileSize = 0;
+ char Type[16] = "Private";
+
+ WebMailInfo * WebMail = Session->WebMail;
+
+ // Create a B2 Message
+
+ tm = gmtime((time_t *)&Msg->datecreated);
+
+ sprintf(DateString, "%04d%02d%02d%02d%02d%02d",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ if (WebMail->DisplayHTMLName)
+ {
+ char XMLName[MAX_PATH];
+
+ strcpy(XMLName, WebMail->DisplayHTMLName);
+
+ XMLName[strlen(XMLName) - 5] = 0; // remove .html
+
+ FileName[0] = malloc(MAX_PATH);
+ FileBody[0] = malloc(100000);
+ Files = 1;
+ FileLen[0] = 0;
+
+ sprintf(FileName[0], "RMS_Express_Form_%s.xml", XMLName);
+
+ XMLPtr = FileBody[0];
+
+ XMLPtr += sprintf(XMLPtr, XMLHeader,
+ "1,0", VersionString,
+ DateString,
+ Session->User->Call,
+ "", //Grid
+ WebMail->DisplayHTMLName,
+ WebMail->ReplyHTMLName,
+ WebMail->OrigTo,
+ "", // CC
+ Session->User->Call,
+ WebMail->OrigSubject,
+ WebMail->OrigBody,
+ "false", // P2P,
+ "false", //Reply
+ "false", //Forward,
+ "false"); // Ack
+
+ // create XML lines for Key/Value Pairs
+
+ for (n = 0; n < NumKeys; n++)
+ {
+ if (Values[n] == NULL)
+ Values[n] = _strdup("");
+
+ XMLPtr += sprintf(XMLPtr, XMLLine, Keys[n], Values[n], Keys[n]);
+ }
+ XMLPtr += sprintf(XMLPtr, "%s", XMLTrailer);
+
+ FileLen[0] = (int)(XMLPtr - FileBody[0]);
+
+ }
+
+ sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+
+
+ // Get Type
+
+ if (Msg->type == 'B')
+ strcpy(Type, "Bulletin");
+ else if (Msg->type == 'T')
+ strcpy(Type, "NTS");
+
+ // We put original To call in B2 Header
+
+ NewMsg += sprintf(NewMsg,
+ "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n",
+ Msg->bid, DateString, Type, Msg->from, WebMail->To, Msg->title, BBSName);
+
+
+ NewMsg += sprintf(NewMsg, "Body: %d\r\n", (int)strlen(WebMail->Body));
+
+ for (n = 0; n < Files; n++)
+ {
+ char * p = FileName[n], * q;
+
+ // Remove any path
+
+ q = strchr(p, '\\');
+
+ while (q)
+ {
+ if (q)
+ *q++ = 0;
+ p = q;
+ q = strchr(p, '\\');
+ }
+
+ NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p);
+ }
+
+ NewMsg += sprintf(NewMsg, "\r\n");
+ strcpy(NewMsg, WebMail->Body);
+ NewMsg += strlen(WebMail->Body);
+ NewMsg += sprintf(NewMsg, "\r\n");
+
+ for (n = 0; n < Files; n++)
+ {
+ memcpy(NewMsg, FileBody[n], FileLen[n]);
+ NewMsg += FileLen[n];
+ free(FileName[n]);
+ free(FileBody[n]);
+ NewMsg += sprintf(NewMsg, "\r\n");
+ }
+
+ Msg->length = (int)strlen(SaveMsg);
+ Msg->B2Flags = B2Msg;
+
+ if (Files)
+ Msg->B2Flags |= Attachments;
+
+ if (WebMail->Body)
+ free(WebMail->Body);
+
+ WebMail->Body = SaveMsg;
+
+ return NULL;
+}
+
+VOID UpdateFormAction(char * Template, char * Key)
+{
+ char * inptr, * saveptr;
+ char * varptr, * endptr;
+ size_t varlen, vallen;
+ char Submit[64];
+
+ sprintf(Submit, "/Webmail/Submit?%s", Key);
+
+ // First find the Form Action string and replace with our URL. It should have
+ // action="http://{FormServer}:{FormPort}" but some forms have localhost:8001 instead
+
+ // Also remove the OnSubmit if it contains the standard popup about having to close browser
+
+ inptr = Template;
+ saveptr = varptr = stristr(inptr, "