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

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

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

BPQ32 Chat Node %s

" + "

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

" +"Sorry, Session had been lost

    " +"
"; + +char * ChatConfigTemplate = NULL; +char * ChatStatusTemplate = NULL; + +static int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int SendChatHeader(char * Reply, char * Key) +{ + return sprintf(Reply, ChatPage, OurNode, OurNode, Key, Key); +} + + +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen) +{ + char * Conxtext = 0, * NodeURL; + int ReplyLen; + char * Key; + char Appl = 'M'; + + NodeURL = strtok_s(URL, "?", &Conxtext); + Key = Session->Key; + + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/Chat/Header") == 0) + { + *RLen = SendChatHeader(Reply, Session->Key); + return; + } + + if (_stricmp(NodeURL, "/Chat/ChatConfig") == 0) + { + if (ChatConfigTemplate) + free(ChatConfigTemplate); + + ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt"); + + NodeURL[strlen(NodeURL)] = ' '; // Undo strtok + SaveChatInfo(Session, input, Reply, RLen, Key); + return ; + } + + if (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0) + { + ProcessChatDisUser(Session, input, Reply, RLen, Key); + return ; + } + + + // End of POST section + } + + if ((_stricmp(NodeURL, "/chat/Chat.html") == 0) || (_stricmp(NodeURL, "/chat/Header") == 0)) + { + *RLen = SendChatHeader(Reply, Session->Key); + return; + } + + if ((_stricmp(NodeURL, "/Chat/ChatStatus") == 0) || (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0)) + { + if (ChatStatusTemplate) + free(ChatStatusTemplate); + + ChatStatusTemplate = GetTemplateFromFile(1, "ChatStatus.txt"); + SendChatStatusPage(Reply, RLen, Key); + + return; + } + + if (_stricmp(NodeURL, "/Chat/ChatConf") == 0) + { + if (ChatConfigTemplate) + free(ChatConfigTemplate); + + ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt"); + + SendChatConfigPage(Reply, RLen, Key); + return; + } + + ReplyLen = sprintf(Reply, ChatSignon, OurNode, OurNode); + *RLen = ReplyLen; + +} + + +static VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +static VOID GetCheckBox(char * input, char * key, int * value) +{ + char * ptr = strstr(input, key); + if (ptr) + *value = 1; + else + *value = 0; +} + + +VOID SaveChatInfo(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = NULL; + char Temp[80]; + char Nodes[10000] = ""; + char * ptr1, * ptr2; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = SendChatHeader(Reply, Session->Key); + return; + } + + + GetParam(input, "ApplNum=", Temp); + ChatApplNum = atoi(Temp); + GetParam(input, "Streams=", Temp); + MaxChatStreams = atoi(Temp); + GetParam(input, "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], "%sOpen   RTT %d", link->call, link->RTT); + else + Len += sprintf(&Links[Len], "%sOpen", link->call); + else if (link->flags & (p_linked | p_linkini)) + Len += sprintf(&Links[Len], "%sConnecting", link->call); + else if (link->flags & p_linkfailed) + Len += sprintf(&Links[Len], "%sConnect failed", link->call); + else + Len += sprintf(&Links[Len], "%sIdle", link->call); + } + + Len = 0; + Streams[0] = 0; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn=&ChatConnections[n]; + i = conn->BPQStream; + if (!conn->Active) + { + Len += sprintf(&Streams[Len], "Idle        ", i, i); + } + else + { + if (conn->Flags & CHATLINK) + { + if (conn->BPQStream > 64 || conn->u.link == 0 || 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

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

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

Webmail Interface - Message Input Form

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

Webmail Forms Interface - Check Message

" + "
" + + "
" + "To      
" + "CC      
" + "Subject " +//"" + "
Type    " + "" + " BID

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

%s Webmail Interface - User %s - Message List

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

" + "Select Required Template from %s

" + "

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

"); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + + free(Boundary); + return; +} + +static char WinlinkAddr[] = "WINLINK.ORG"; + +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen) +{ + int i, ReplyLen = 0; + struct MsgInfo * Msg; + FILE * hFile; + int Template=0; + char * via = NULL; + BIDRec * BIDRec; + char MsgFile[MAX_PATH]; + size_t WriteLen=0; + char * HDest; + char * HDestCopy; + char * HDestRest; + char * Vptr = NULL; + char * FileList = NULL; + char Prompt[256] = "Message Saved"; + char OrigTo[256]; + WebMailInfo * WebMail = Session->WebMail; + struct UserInfo * user; + CIRCUIT conn; + + // So we can have attachments input is now Content-Type: multipart/form-data; + + char * Input; + size_t MsgLen = 0; + char * Boundary; + + strcpy(conn.Callsign, Session->User->Call); + + Input = MsgPtr; + + Boundary = initMultipartUnpack(&Input); + + if (Boundary == NULL) + return; // Can't work without one + + // Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part + + while(Input && Input[-1] != '-') + { + char * Name, * Value; + int ValLen; + + if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE) + { + // ReportCorrupt(WebMail); + free(Boundary); + return; + } + if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE) + { + *RLen = sprintf(Reply, "", Session->Key); + return; + } + } + + + if (WebMail->txtFileName) + { + // Processing Form Input + + SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest); + + // Prevent re-entry + + free(WebMail->txtFileName); + WebMail->txtFileName = NULL; + + return; + } + + // If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before. + + strlop(WebMail->BID, ' '); + if (strlen(WebMail->BID) > 12) + WebMail->BID[12] = 0; + + UndoTransparency(WebMail->BID); + UndoTransparency(WebMail->To); + UndoTransparency(WebMail->Subject); + UndoTransparency(WebMail->Body); + + MsgLen = strlen(WebMail->Body); + + // We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one + + if (WebMail->To[0] == 0) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + if (strlen(WebMail->To) > 255) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + HDest = _strdup(WebMail->To); + + if (strlen(WebMail->BID)) + { + if (LookupBID(WebMail->BID)) + { + // Duplicate bid + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + if (WebMail->Type == 'B') + { + if (RefuseBulls) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + // ?? Can we just loop though the rest of the code to allow multiple dests ?? + + HDestCopy = HDest; + + while (HDest && HDest[0]) + { + HDestRest = strlop(HDest, ';'); + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, Session->User->Call); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr=&HDest[4]; + strcpy(Msg->to, "RMS"); + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr=&HDest[5]; + Msg->to[0] = 0; + } + } + else if (strchr(HDest, '@')) + { + strcpy(OrigTo, HDest); + + Vptr = strlop(HDest, '@'); + + if (Vptr) + { + // If looks like a valid email address, treat as such + + if (strlen(HDest) > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = OrigTo; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else if (isAMPRMsg(OrigTo)) + strcpy(Msg->to, "RMS"); // Routing will redirect it + else + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + + } + } + else + strcpy(Msg->to, _strupr(HDest)); + } + } + else + { + // No @ + + if (strlen(HDest) > 6) + HDest[6] = 0; + + strcpy(Msg->to, _strupr(HDest)); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(HDest, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, _strupr(Vptr)); + } + else + { + // No via. If not local user try to add BBS + + struct UserInfo * ToUser = LookupCall(Msg->to); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + strcpy(Msg->via, WinlinkAddr); + sprintf(Prompt, "Redirecting to winlink.org\r"); + } + else if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); + } + else + { + // 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 substitutions + + // if Dir not specified search all for Filename + + if (Dir == NULL) + { + for (i = 0; i < FormDirCount; i++) + { + int n; + + Dir = HtmlFormDirs[i]; + + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + if (MsgBytes) + goto gotFile; + + // Recurse any Subdirs + + n = 0; + while (n < Dir->DirCount) + { + MsgBytes = CheckFile(Dir->Dirs[n], FN); + if (MsgBytes) + { + Dir = Dir->Dirs[n]; + goto gotFile; + } + n++; + } + } + return; + } + else + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + +gotFile: + + WebMail->Dir = Dir; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + WebMail->txtFile = MsgBytes; + +reEnter: + + if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE) + { + // Template has or + + if (WebMail->InputHTMLName == NULL) + { + // This is a plain text template without HTML +/* + if (To == NULL) + To = ""; + + if (To[0] == 0 && WebMail->To && WebMail->To[0]) + To = WebMail->To; + + if (CC == NULL) + CC = ""; + + if (CC[0] == 0 && WebMail->CC && WebMail->CC[0]) + CC = WebMail->CC; + + if (Subject == NULL) + Subject = ""; + + if (Subject[0] == 0 && WebMail->Subject && WebMail->Subject[0]) + Subject = WebMail->Subject; + + if (MsgBody == NULL) + MsgBody = ""; + + if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0]) + MsgBody = WebMail->Body; + + *WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject, MsgBody); + */ + return *WebMail->RLen; + } + + Template = CheckFile(WebMail->Dir, WebMail->InputHTMLName); + + if (Template == NULL) + { + // Missing HTML + + *WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->InputHTMLName); + return *WebMail->RLen; + } + + // I've going to update the template in situ, as I can't see a better way + // of making sure all occurances of variables in any order are substituted. + // The space allocated to Template is twice the size of the file + // to allow for insertions + + UpdateFormAction(Template, Session->Key); // Update "Submit" Action + + // Search for "{var }" strings in form and replace with + // corresponding variable from XML + + while (txtKey->Key) + { + char Key[256] = "{var "; + + strcpy(&Key[5], txtKey->Key); + strcat(Key, "}"); + + inptr = Template; + varptr = stristr(inptr, Key); + + while (varptr) + { + // Move the remaining message up/down the buffer to make space for substitution + + varlen = (int)strlen(Key); + if (txtKey->Value) + vallen = (int)strlen(txtKey->Value); + else vallen = 0; + + endptr = varptr + varlen; + + memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end + memcpy(varptr, txtKey->Value, vallen); + + inptr = endptr + 1; + + varptr = stristr(inptr, Key); + } + txtKey++; + } + + // Remove from end as we add it on later + + ptr = stristr(Template, ""); + + if (ptr) + *ptr = 0; + + Len = sprintf(Reply, "%s", Template); + free(Template); + return Len; +} + +char * CheckFile(struct HtmlFormDir * Dir, char * FN) +{ + struct stat STAT; + FILE * hFile; + char MsgFile[MAX_PATH]; + char * MsgBytes; + int ReadLen; + int FileSize; + +#ifndef WIN32 + + // Need to do case insensitive file search + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s/%s", BPQDirectory, Dir->FormSet, Dir->DirName); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %s %d", Dir->DirName, name, errno); + return 0; + } + + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + continue; + + if (stricmp(entry->d_name, FN) == 0) + { + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, entry->d_name); + break; + } + } + closedir(dir); + +#else + + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, FN); + +#endif + + if (stat(MsgFile, &STAT) != -1) + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + MsgBytes = _strdup("File is missing"); + return MsgBytes; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize * 10); // Allow plenty of room for template substitution + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + MsgBytes[FileSize] = 0; + fclose(hFile); + + return MsgBytes; + } + return NULL; +} + +BOOL DoSelectPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + // Send a Popup window to select value. Reply handling code will update template then reenter ParsetxtTemplate + + char popuphddr[] = + + "" + "" + "
%s

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

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