From 7b2a99c53b210949fb2e993e65b6be66a9cbf7a1 Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Tue, 10 Oct 2023 22:07:04 +0100 Subject: [PATCH] New upstream version 6.0.24.15 --- 250mS@1000Hz.wav | Bin 0 -> 6044 bytes 250mS@600Hz.wav | Bin 0 -> 6044 bytes APRSCode.c | 21 +- APRSStdPages-HPLaptop.c | 3719 ----- BBSHTMLConfig.c | 151 +- BBSUtilities.c | 209 +- BBSUtilities.c.bak | 14926 ------------------- BPQINP3.c | 12 +- BPQMail.aps | Bin 68844 -> 91752 bytes BPQMail.c | 3 + BPQMail.c.bak | 3651 ----- BPQMail.rc | 46 +- BPQMail.vcproj | 2 +- BPQMail.vcproj.DESKTOP-MHE5LO8.johnw.user | 65 + BPQMail.vcxproj | 141 +- BPQMailrc.h | 4 +- Bpq32.c | 20 + CBPQ32.suo | Bin 5632 -> 0 bytes CBPQ32.vcproj | 4 +- CBPQ32.vcproj.DESKTOP-MHE5LO8.johnw.user | 4 +- ChatHTMLConfig.c | 13 +- ChatUtils.c | 7 +- Cmd-HPLaptop-2.c | 5599 ------- Cmd-HPLaptop.c | 5495 ------- Cmd.c | 9 +- CommonCode.c | 622 +- FBBRoutines.c | 4 +- HALDriver.c.bak | 1903 --- HTTPcode.c | 10 +- HTTPcode.c.bak | 4849 ------ HanksRT.c | 43 +- L2Code.c | 14 +- LinBPQ.c | 140 +- MailNode.ncb | Bin 0 -> 13732864 bytes MailNode.vcproj | 4 + MailNode.vcproj.DESKTOP-MHE5LO8.johnw.user | 65 + MailNode.vcproj.DESKTOP-TGEL8RC.John.user | 2 +- MailNode.vcxproj | 4 +- MailNode.vcxproj.user | 8 - MailTCP-DESKTOP-MHE5LO8.c | 4127 +++++ MailTCP.c | 5 + QtTermTCP.ini | 120 + RCa22388 | Bin 26344 -> 0 bytes RigControl.c | 782 +- SCSPactor.c | 11 +- TNCCode.c | 2 +- TNCEmulators.c | 23 +- TelnetV6.c | 4 +- UIRoutines.c | 2 +- Versions.h | 6 +- WebMail.c | 49 +- asmstrucs.h | 4 + bpqchat.c | 3 + bpqmail.h | 30 +- bpqmail.h.bak | 1604 -- cMain.c | 9 +- compatbits.h | 4 +- config.c | 28 +- configstructs.h | 6 +- datadefs.c | 1 + getopt.c | 715 + getopt.h | 108 + linether.c~ | 498 - rigcontrol.h | 12 +- templatedefs.c | 18 +- 65 files changed, 7384 insertions(+), 42556 deletions(-) create mode 100644 250mS@1000Hz.wav create mode 100644 250mS@600Hz.wav delete mode 100644 APRSStdPages-HPLaptop.c delete mode 100644 BBSUtilities.c.bak delete mode 100644 BPQMail.c.bak create mode 100644 BPQMail.vcproj.DESKTOP-MHE5LO8.johnw.user delete mode 100644 CBPQ32.suo delete mode 100644 Cmd-HPLaptop-2.c delete mode 100644 Cmd-HPLaptop.c delete mode 100644 HALDriver.c.bak delete mode 100644 HTTPcode.c.bak create mode 100644 MailNode.ncb create mode 100644 MailNode.vcproj.DESKTOP-MHE5LO8.johnw.user delete mode 100644 MailNode.vcxproj.user create mode 100644 MailTCP-DESKTOP-MHE5LO8.c create mode 100644 QtTermTCP.ini delete mode 100644 RCa22388 delete mode 100644 bpqmail.h.bak create mode 100644 getopt.c create mode 100644 getopt.h delete mode 100644 linether.c~ diff --git a/250mS@1000Hz.wav b/250mS@1000Hz.wav new file mode 100644 index 0000000000000000000000000000000000000000..0079795415083f1335fb96212be173cbedb91fd8 GIT binary patch literal 6044 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 Bo3;P| literal 0 HcmV?d00001 diff --git a/APRSCode.c b/APRSCode.c index 67ab0c2..70327d5 100644 --- a/APRSCode.c +++ b/APRSCode.c @@ -103,6 +103,7 @@ void SaveAPRSMessage(struct APRSMESSAGE * ptr); void ClearSavedMessages(); void GetSavedAPRSMessages(); static VOID GPSDConnect(void * unused); +int CanPortDigi(int Port); extern int SemHeldByAPI; extern int APRSMONDECODE(); @@ -664,9 +665,10 @@ Dll BOOL APIENTRY Init_APRS() memset(&CrossPortMap[0][0], 0, sizeof(CrossPortMap)); memset(&APRSBridgeMap[0][0], 0, sizeof(APRSBridgeMap)); - for (i = 1; i <= 32; i++) + for (i = 1; i <= MaxBPQPortNo; i++) { - CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port + if (CanPortDigi(i)) + CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port CrossPortMap[i][0] = TRUE; // and APRS-IS } @@ -1945,7 +1947,7 @@ static int APRSProcessLine(char * buf) { SendTo = atoi(ptr); // this gives zero for IS - if (SendTo > 32) + if (SendTo > MaxBPQPortNo) return FALSE; Object->PortMap[SendTo] = TRUE; @@ -2175,6 +2177,12 @@ static int APRSProcessLine(char * buf) if (GetPortTableEntryFromPortNum(Port) == NULL) return FALSE; + // Check that port can digi (SCS Pactor can't set digi'd bit in calls) + + if (CanPortDigi(Port) == 0) + return FALSE; + + CrossPortMap[Port][Port] = FALSE; // Cancel Default mapping CrossPortMap[Port][0] = FALSE; // Cancel Default APRSIS @@ -2218,7 +2226,7 @@ static int APRSProcessLine(char * buf) { DigiTo = atoi(ptr); // this gives zero for IS - if (DigiTo > 32) + if (DigiTo > MaxBPQPortNo) return FALSE; APRSBridgeMap[Port][DigiTo] = TRUE; @@ -5238,6 +5246,7 @@ int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station) DecodeLocationString(Payload + 18, Object); Object->TimeLastUpdated = time(NULL); + Object->LastPort = Station->LastPort; Station->Object = Object; return 0; @@ -8168,6 +8177,8 @@ VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * else Bufferptr = Cmdprintf(Session, Bufferptr, "but not connected\r"); } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); return; } @@ -8844,7 +8855,7 @@ int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int a if (lastLat != ptr->Lat) Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->Lat, ptr->Lon); //Add current position to end of track else - Len += sprintf(&Buffer[Len],"\r\n|", ptr->Lat, ptr->Lon); + Len += sprintf(&Buffer[Len],"\r\n|"); } } } diff --git a/APRSStdPages-HPLaptop.c b/APRSStdPages-HPLaptop.c deleted file mode 100644 index 7cb38e1..0000000 --- a/APRSStdPages-HPLaptop.c +++ /dev/null @@ -1,3719 +0,0 @@ - -#include -#include - -#ifndef WIN32 - -#define _stricmp stricmp -#define _strdup strdup - -int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); - -#endif - -unsigned char aisblue_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, - 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, - 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xff, 0x23, 0xde, 0xb7, 0xa6, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, - 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, - 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, - 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, - 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, - 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x03, 0x7a, 0x0e, 0x22, 0xdf, - 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, - 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, - 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, - 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, - 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, - 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, - 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, - 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, - 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int aisblue_png_len = 227; -unsigned char aisgreen_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, - 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, - 0x0c, 0x50, 0x4c, 0x54, 0x45, 0x61, 0x62, 0x6c, 0x00, 0x00, 0x00, 0x00, - 0xff, 0x00, 0x13, 0xfe, 0x00, 0x1b, 0x46, 0xca, 0x60, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, - 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, - 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, - 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, - 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x11, 0x89, - 0xb7, 0x53, 0x97, 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, - 0xd7, 0x63, 0x60, 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, - 0xa1, 0x22, 0x1d, 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, - 0xad, 0x9a, 0x80, 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, - 0x41, 0xa6, 0x96, 0x21, 0xf3, 0xc4, 0x56, 0x2f, 0x05, 0x51, 0x52, 0xab, - 0x56, 0x12, 0xa2, 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, - 0x06, 0xc5, 0x08, 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, - 0x1a, 0x1a, 0x1a, 0xc0, 0x00, 0x00, 0xd2, 0x45, 0x24, 0x46, 0xf4, 0x40, - 0x4e, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, - 0x60, 0x82 -}; -unsigned int aisgreen_png_len = 230; -unsigned char yellowplane_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, - 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x02, - 0xf7, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x38, 0x44, 0x47, 0x44, - 0x45, 0x46, 0x3b, 0x4c, 0x51, 0x42, 0x4b, 0x4e, 0x3f, 0x4d, 0x51, 0x43, - 0x4e, 0x51, 0x44, 0x4e, 0x51, 0x44, 0x51, 0x54, 0x4b, 0x50, 0x52, 0x42, - 0x54, 0x5a, 0x43, 0x54, 0x5a, 0x4c, 0x52, 0x53, 0x48, 0x53, 0x56, 0x4a, - 0x53, 0x56, 0x4d, 0x53, 0x55, 0x46, 0x57, 0x5c, 0x4b, 0x56, 0x59, 0x4a, - 0x58, 0x5c, 0x4b, 0x58, 0x5b, 0x4f, 0x58, 0x5b, 0x4d, 0x59, 0x5c, 0x4e, - 0x59, 0x5d, 0x53, 0x58, 0x59, 0x50, 0x59, 0x5c, 0x49, 0x5b, 0x5f, 0x4e, - 0x5b, 0x5e, 0x4e, 0x5c, 0x60, 0x52, 0x5b, 0x5e, 0x49, 0x5e, 0x65, 0x4c, - 0x5e, 0x63, 0x52, 0x5d, 0x60, 0x55, 0x5d, 0x5f, 0x53, 0x5e, 0x61, 0x55, - 0x5f, 0x62, 0x53, 0x60, 0x64, 0x54, 0x60, 0x63, 0x54, 0x61, 0x65, 0x55, - 0x61, 0x64, 0x57, 0x61, 0x64, 0x5c, 0x60, 0x60, 0x58, 0x61, 0x64, 0x5a, - 0x61, 0x63, 0x52, 0x63, 0x69, 0x57, 0x62, 0x66, 0x5c, 0x61, 0x63, 0x56, - 0x63, 0x67, 0x5d, 0x62, 0x63, 0x59, 0x63, 0x66, 0x56, 0x64, 0x68, 0x5c, - 0x63, 0x66, 0x5c, 0x64, 0x67, 0x61, 0x65, 0x67, 0x63, 0x65, 0x65, 0x5d, - 0x67, 0x6a, 0x61, 0x66, 0x68, 0x5b, 0x68, 0x6c, 0x63, 0x66, 0x67, 0x5c, - 0x68, 0x6c, 0x59, 0x69, 0x6d, 0x61, 0x67, 0x69, 0x59, 0x69, 0x6f, 0x5e, - 0x68, 0x6b, 0x61, 0x69, 0x6b, 0x61, 0x6a, 0x6d, 0x5a, 0x6c, 0x71, 0x63, - 0x6a, 0x6b, 0x64, 0x6a, 0x6c, 0x66, 0x68, 0x7e, 0x5d, 0x6c, 0x70, 0x66, - 0x6a, 0x6b, 0x65, 0x6b, 0x6d, 0x5b, 0x6f, 0x74, 0x5c, 0x70, 0x75, 0x68, - 0x6d, 0x6e, 0x64, 0x6e, 0x71, 0x5e, 0x70, 0x75, 0x62, 0x6f, 0x73, 0x67, - 0x6e, 0x70, 0x69, 0x6e, 0x6f, 0x69, 0x6e, 0x70, 0x66, 0x6f, 0x72, 0x66, - 0x71, 0x75, 0x6b, 0x70, 0x71, 0x67, 0x71, 0x74, 0x6e, 0x70, 0x70, 0x62, - 0x74, 0x7a, 0x66, 0x74, 0x78, 0x70, 0x72, 0x72, 0x6d, 0x73, 0x75, 0x66, - 0x75, 0x7a, 0x68, 0x75, 0x79, 0x71, 0x73, 0x74, 0x72, 0x74, 0x75, 0x72, - 0x76, 0x77, 0x6c, 0x78, 0x7b, 0x72, 0x77, 0x78, 0x72, 0x77, 0x79, 0x73, - 0x78, 0x79, 0x72, 0x79, 0x7b, 0x72, 0x7b, 0x7e, 0x78, 0x7a, 0x7a, 0x77, - 0x7b, 0x7d, 0x74, 0x7c, 0x7e, 0x75, 0x7c, 0x7e, 0x76, 0x7d, 0x80, 0x77, - 0x7e, 0x80, 0x77, 0x80, 0x82, 0x7c, 0x7f, 0x7f, 0x72, 0x82, 0x87, 0x76, - 0x81, 0x85, 0x7b, 0x80, 0x81, 0x76, 0x83, 0x87, 0x7e, 0x85, 0x87, 0x83, - 0x84, 0x85, 0x7d, 0x87, 0x89, 0x7b, 0x88, 0x8c, 0x7b, 0x89, 0x8e, 0x7e, - 0x8a, 0x8d, 0x7f, 0x8b, 0x8e, 0x88, 0x8b, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, - 0x8c, 0x8c, 0x88, 0x8e, 0x91, 0x85, 0x8f, 0x93, 0x8c, 0x8e, 0x8f, 0x8d, - 0x8f, 0x8f, 0x87, 0x91, 0x94, 0x88, 0x93, 0x97, 0x91, 0x94, 0x96, 0x93, - 0x94, 0x95, 0x8d, 0x96, 0x9a, 0x8d, 0x97, 0x9a, 0x90, 0x97, 0x99, 0x95, - 0x96, 0x96, 0x91, 0x98, 0x9b, 0x94, 0x98, 0x99, 0x91, 0x99, 0x9b, 0x96, - 0x97, 0xa4, 0x93, 0x99, 0x9b, 0x91, 0x9a, 0x9c, 0x98, 0x99, 0x99, 0x93, - 0x9b, 0x9d, 0x97, 0x9c, 0x9d, 0x93, 0x9d, 0xa1, 0x95, 0x9d, 0x9f, 0x96, - 0x9d, 0xa0, 0x98, 0xa0, 0xa3, 0x9d, 0x9f, 0xa0, 0x97, 0xa1, 0xa5, 0xa1, - 0xa1, 0xa1, 0x99, 0xa3, 0xa7, 0x9b, 0xa3, 0xa5, 0xa4, 0xa5, 0xa5, 0x9d, - 0xa7, 0xa9, 0xa6, 0xa8, 0xa8, 0xa2, 0xa9, 0xac, 0xa2, 0xab, 0xad, 0xaa, - 0xab, 0xab, 0xa6, 0xad, 0xb0, 0xa5, 0xaf, 0xb2, 0xab, 0xb3, 0xb6, 0xaf, - 0xb2, 0xb4, 0xae, 0xb5, 0xb8, 0xb4, 0xb4, 0xb4, 0xb0, 0xb6, 0xb8, 0xb1, - 0xb7, 0xba, 0xb2, 0xb7, 0xb9, 0xb4, 0xb7, 0xb9, 0xb2, 0xb8, 0xb9, 0xb2, - 0xb8, 0xba, 0xb3, 0xb8, 0xb9, 0xb3, 0xbb, 0xbe, 0xb4, 0xbb, 0xbd, 0xb5, - 0xbb, 0xbd, 0xb6, 0xbb, 0xbc, 0xb7, 0xbc, 0xbf, 0xb8, 0xbd, 0xbe, 0xbd, - 0xbe, 0xbf, 0xbc, 0xbf, 0xc0, 0xbc, 0xc1, 0xc2, 0xbd, 0xc1, 0xc2, 0xbb, - 0xc2, 0xc5, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xbe, 0xc5, 0xc7, 0xc2, - 0xc4, 0xc5, 0xc3, 0xc4, 0xc4, 0xc2, 0xc6, 0xc7, 0xc4, 0xc6, 0xc7, 0xc3, - 0xc7, 0xc8, 0xc3, 0xc7, 0xc9, 0xc3, 0xc8, 0xca, 0xc4, 0xc9, 0xcb, 0xc6, - 0xca, 0xcb, 0xc6, 0xcc, 0xce, 0xca, 0xcd, 0xce, 0xca, 0xce, 0xd0, 0xcb, - 0xce, 0xcf, 0xca, 0xcf, 0xd1, 0xcb, 0xcf, 0xd0, 0xcb, 0xd0, 0xd2, 0xcd, - 0xd0, 0xd1, 0xcf, 0xd1, 0xd2, 0xd0, 0xd1, 0xd1, 0xce, 0xd2, 0xd3, 0xd0, - 0xd2, 0xd3, 0xd0, 0xd3, 0xd4, 0xd2, 0xd4, 0xd5, 0xd4, 0xd5, 0xd8, 0xd2, - 0xd6, 0xd8, 0xd2, 0xd7, 0xd8, 0xd4, 0xd7, 0xd8, 0xd3, 0xd8, 0xd9, 0xd7, - 0xd7, 0xdd, 0xd6, 0xd8, 0xd9, 0xd6, 0xd8, 0xda, 0xd7, 0xd8, 0xd8, 0xd7, - 0xda, 0xdb, 0xd8, 0xdb, 0xdd, 0xd7, 0xdc, 0xde, 0xd9, 0xdc, 0xdd, 0xda, - 0xdc, 0xdc, 0xdb, 0xdd, 0xdd, 0xdb, 0xdd, 0xde, 0xdc, 0xdf, 0xe0, 0xdd, - 0xdf, 0xe0, 0xdd, 0xe0, 0xe0, 0xde, 0xe1, 0xe2, 0xe0, 0xe1, 0xe2, 0xe0, - 0xe2, 0xe2, 0xe1, 0xe3, 0xe3, 0xe1, 0xe3, 0xe4, 0xe2, 0xe3, 0xe3, 0xe1, - 0xe6, 0xe7, 0xe5, 0xe7, 0xe7, 0xe6, 0xe7, 0xe7, 0xe6, 0xe7, 0xe8, 0xe6, - 0xe8, 0xe9, 0xe7, 0xe8, 0xe9, 0xe4, 0xe9, 0xeb, 0xe6, 0xe9, 0xea, 0xe7, - 0xe9, 0xea, 0xe8, 0xe9, 0xea, 0xe6, 0xea, 0xeb, 0xe9, 0xeb, 0xec, 0xe8, - 0xec, 0xed, 0xea, 0xec, 0xed, 0xeb, 0xec, 0xed, 0xeb, 0xed, 0xed, 0xec, - 0xed, 0xee, 0xec, 0xef, 0xf1, 0xff, 0xff, 0x00, 0x0f, 0x9e, 0xcb, 0x48, - 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, - 0x66, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, - 0xc4, 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00, - 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x37, - 0x0f, 0x41, 0x8e, 0x0c, 0x76, 0x00, 0x00, 0x01, 0xda, 0x49, 0x44, 0x41, - 0x54, 0x38, 0xcb, 0x63, 0x60, 0x20, 0x1b, 0xdc, 0x7a, 0x49, 0x94, 0xb2, - 0x0e, 0x67, 0xf5, 0x43, 0x44, 0x28, 0xdb, 0x14, 0xf7, 0xe7, 0xcf, 0x44, - 0xc2, 0xca, 0xde, 0x86, 0x14, 0xfd, 0xf9, 0x23, 0x7c, 0x90, 0xa0, 0xba, - 0x59, 0xd2, 0x7f, 0x80, 0x60, 0x29, 0x41, 0x75, 0x1b, 0xed, 0x41, 0xea, - 0x7a, 0x08, 0xaa, 0x5b, 0xe9, 0x07, 0x52, 0x17, 0x4e, 0x50, 0xdd, 0xea, - 0x60, 0x90, 0x3a, 0xd7, 0xc7, 0x84, 0xd4, 0x4d, 0x76, 0x06, 0xa9, 0xb3, - 0xb8, 0x4b, 0x48, 0xdd, 0x8a, 0x00, 0x90, 0xba, 0xa0, 0xe3, 0x78, 0x15, - 0xbd, 0x38, 0xb7, 0x25, 0xca, 0x1c, 0xa4, 0x4e, 0xa8, 0xa4, 0xbe, 0xf9, - 0x19, 0x4e, 0x65, 0xf7, 0x1d, 0xcd, 0x0c, 0x18, 0x35, 0x40, 0xea, 0x58, - 0x24, 0x9c, 0x04, 0xf3, 0x70, 0xaa, 0x3b, 0xa5, 0x35, 0xbb, 0x8a, 0x29, - 0x14, 0xa4, 0xce, 0x34, 0x62, 0x49, 0x53, 0xe0, 0x1b, 0x5c, 0xea, 0x76, - 0x59, 0xde, 0xfc, 0x83, 0x00, 0x46, 0xf7, 0x70, 0x28, 0xfb, 0x55, 0x20, - 0xbe, 0x1d, 0x49, 0x1d, 0x7b, 0xcd, 0x25, 0xec, 0xea, 0xe6, 0x08, 0xa4, - 0xee, 0x40, 0x52, 0xf7, 0x47, 0x21, 0x6c, 0x1b, 0x16, 0x55, 0x97, 0x36, - 0x8b, 0xc5, 0xee, 0xfe, 0x83, 0x02, 0xfc, 0x53, 0xa6, 0x9f, 0x44, 0x57, - 0x76, 0xdd, 0xd6, 0xa6, 0xed, 0xcc, 0x1f, 0x34, 0x90, 0x23, 0xe3, 0xb1, - 0xe0, 0x27, 0xb2, 0xaa, 0xcf, 0xab, 0x32, 0xdc, 0xe6, 0xfd, 0xc1, 0x04, - 0xd3, 0xf4, 0xe5, 0xf6, 0xbf, 0x43, 0x28, 0x7b, 0xd8, 0xcd, 0xfa, 0x07, - 0x07, 0xa8, 0x48, 0x6c, 0x38, 0x0c, 0x53, 0xb6, 0x2c, 0x1a, 0x22, 0x56, - 0x69, 0x2f, 0x22, 0xcd, 0xc1, 0x09, 0x62, 0xf1, 0x33, 0x67, 0x27, 0x4b, - 0x79, 0xd5, 0xf5, 0x02, 0x99, 0x53, 0x4c, 0xaa, 0x5f, 0x43, 0x94, 0x3d, - 0xd2, 0x86, 0x28, 0x73, 0xcf, 0x9a, 0x71, 0xe2, 0x68, 0x2e, 0x1f, 0x88, - 0xa9, 0x58, 0xfa, 0xe4, 0xc7, 0xfa, 0x49, 0x8d, 0x3a, 0x36, 0x20, 0x8e, - 0xe6, 0x3a, 0xa8, 0x79, 0x49, 0x20, 0x9e, 0x43, 0xf1, 0x9e, 0x4f, 0x40, - 0xf6, 0x8d, 0x48, 0x10, 0xc7, 0xe0, 0x08, 0x58, 0xe2, 0x74, 0xb9, 0x35, - 0x90, 0xc3, 0xb6, 0x15, 0xa2, 0xec, 0xae, 0xdd, 0x1f, 0x1e, 0xbd, 0x96, - 0x63, 0x10, 0xce, 0x15, 0x63, 0x90, 0x3a, 0x95, 0xe5, 0x50, 0x23, 0x76, - 0x1e, 0x6a, 0xfd, 0x53, 0x08, 0x65, 0x7f, 0x9d, 0xe0, 0xb9, 0xef, 0x01, - 0xcc, 0xad, 0x27, 0x44, 0x41, 0xea, 0x94, 0xe6, 0x23, 0x3c, 0x79, 0xe0, - 0x21, 0x9c, 0x79, 0x15, 0x21, 0xfa, 0x54, 0x15, 0xa4, 0x4e, 0x79, 0x2e, - 0xc1, 0x84, 0x9f, 0x09, 0x52, 0x67, 0x75, 0x9e, 0x90, 0xb2, 0xdb, 0xf1, - 0x20, 0x75, 0xdc, 0x8b, 0x09, 0x28, 0xfb, 0xbe, 0xd0, 0x0e, 0xa4, 0x4e, - 0xbe, 0x8c, 0x40, 0x06, 0x99, 0x6a, 0x98, 0x00, 0x52, 0x57, 0xcb, 0xeb, - 0xb3, 0x06, 0x9f, 0xb2, 0xad, 0x92, 0x31, 0x90, 0x30, 0x9f, 0xa9, 0x2b, - 0x7b, 0x16, 0xb7, 0xb2, 0xe7, 0xde, 0x69, 0xf0, 0x88, 0xcd, 0xef, 0xff, - 0x81, 0x53, 0x5d, 0x3b, 0x72, 0x0a, 0xe0, 0xda, 0x86, 0x53, 0xdd, 0x22, - 0x94, 0xa4, 0xf2, 0x11, 0xa7, 0xba, 0xbd, 0x6a, 0xe9, 0x7d, 0x6b, 0x2f, - 0x76, 0xb9, 0x6c, 0xf8, 0x70, 0xb9, 0xd3, 0xf7, 0x02, 0x1e, 0x7f, 0xbc, - 0xff, 0xf2, 0x0d, 0x48, 0x5e, 0x63, 0xf8, 0xcd, 0xf0, 0xea, 0x0e, 0x03, - 0x4d, 0x01, 0x00, 0x38, 0x16, 0x7f, 0x3f, 0xdc, 0xdc, 0x55, 0xf3, 0x00, - 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int yellowplane_png_len = 1355; - -unsigned char greenplane_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, - 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x02, - 0xf7, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x38, 0x44, 0x47, 0x44, - 0x45, 0x46, 0x3b, 0x4c, 0x51, 0x42, 0x4b, 0x4e, 0x3f, 0x4d, 0x51, 0x43, - 0x4e, 0x51, 0x44, 0x4e, 0x51, 0x44, 0x51, 0x54, 0x4b, 0x50, 0x52, 0x42, - 0x54, 0x5a, 0x43, 0x54, 0x5a, 0x4c, 0x52, 0x53, 0x48, 0x53, 0x56, 0x4a, - 0x53, 0x56, 0x4d, 0x53, 0x55, 0x46, 0x57, 0x5c, 0x4b, 0x56, 0x59, 0x4a, - 0x58, 0x5c, 0x4b, 0x58, 0x5b, 0x4f, 0x58, 0x5b, 0x4d, 0x59, 0x5c, 0x4e, - 0x59, 0x5d, 0x53, 0x58, 0x59, 0x50, 0x59, 0x5c, 0x49, 0x5b, 0x5f, 0x4e, - 0x5b, 0x5e, 0x4e, 0x5c, 0x60, 0x52, 0x5b, 0x5e, 0x49, 0x5e, 0x65, 0x4c, - 0x5e, 0x63, 0x52, 0x5d, 0x60, 0x55, 0x5d, 0x5f, 0x53, 0x5e, 0x61, 0x55, - 0x5f, 0x62, 0x53, 0x60, 0x64, 0x54, 0x60, 0x63, 0x54, 0x61, 0x65, 0x55, - 0x61, 0x64, 0x57, 0x61, 0x64, 0x5c, 0x60, 0x60, 0x58, 0x61, 0x64, 0x5a, - 0x61, 0x63, 0x52, 0x63, 0x69, 0x57, 0x62, 0x66, 0x5c, 0x61, 0x63, 0x56, - 0x63, 0x67, 0x5d, 0x62, 0x63, 0x59, 0x63, 0x66, 0x56, 0x64, 0x68, 0x5c, - 0x63, 0x66, 0x5c, 0x64, 0x67, 0x61, 0x65, 0x67, 0x63, 0x65, 0x65, 0x5d, - 0x67, 0x6a, 0x61, 0x66, 0x68, 0x5b, 0x68, 0x6c, 0x63, 0x66, 0x67, 0x5c, - 0x68, 0x6c, 0x59, 0x69, 0x6d, 0x61, 0x67, 0x69, 0x59, 0x69, 0x6f, 0x5e, - 0x68, 0x6b, 0x61, 0x69, 0x6b, 0x61, 0x6a, 0x6d, 0x5a, 0x6c, 0x71, 0x63, - 0x6a, 0x6b, 0x64, 0x6a, 0x6c, 0x66, 0x68, 0x7e, 0x5d, 0x6c, 0x70, 0x66, - 0x6a, 0x6b, 0x65, 0x6b, 0x6d, 0x5b, 0x6f, 0x74, 0x5c, 0x70, 0x75, 0x68, - 0x6d, 0x6e, 0x64, 0x6e, 0x71, 0x5e, 0x70, 0x75, 0x62, 0x6f, 0x73, 0x67, - 0x6e, 0x70, 0x69, 0x6e, 0x6f, 0x69, 0x6e, 0x70, 0x66, 0x6f, 0x72, 0x66, - 0x71, 0x75, 0x6b, 0x70, 0x71, 0x67, 0x71, 0x74, 0x6e, 0x70, 0x70, 0x62, - 0x74, 0x7a, 0x66, 0x74, 0x78, 0x70, 0x72, 0x72, 0x6d, 0x73, 0x75, 0x66, - 0x75, 0x7a, 0x68, 0x75, 0x79, 0x71, 0x73, 0x74, 0x72, 0x74, 0x75, 0x72, - 0x76, 0x77, 0x6c, 0x78, 0x7b, 0x72, 0x77, 0x78, 0x72, 0x77, 0x79, 0x73, - 0x78, 0x79, 0x72, 0x79, 0x7b, 0x72, 0x7b, 0x7e, 0x78, 0x7a, 0x7a, 0x77, - 0x7b, 0x7d, 0x74, 0x7c, 0x7e, 0x75, 0x7c, 0x7e, 0x76, 0x7d, 0x80, 0x77, - 0x7e, 0x80, 0x77, 0x80, 0x82, 0x7c, 0x7f, 0x7f, 0x72, 0x82, 0x87, 0x76, - 0x81, 0x85, 0x7b, 0x80, 0x81, 0x76, 0x83, 0x87, 0x7e, 0x85, 0x87, 0x83, - 0x84, 0x85, 0x7d, 0x87, 0x89, 0x7b, 0x88, 0x8c, 0x7b, 0x89, 0x8e, 0x7e, - 0x8a, 0x8d, 0x7f, 0x8b, 0x8e, 0x88, 0x8b, 0x8b, 0x89, 0x8b, 0x8c, 0x8b, - 0x8c, 0x8c, 0x88, 0x8e, 0x91, 0x85, 0x8f, 0x93, 0x8c, 0x8e, 0x8f, 0x8d, - 0x8f, 0x8f, 0x87, 0x91, 0x94, 0x88, 0x93, 0x97, 0x91, 0x94, 0x96, 0x93, - 0x94, 0x95, 0x8d, 0x96, 0x9a, 0x8d, 0x97, 0x9a, 0x90, 0x97, 0x99, 0x95, - 0x96, 0x96, 0x91, 0x98, 0x9b, 0x94, 0x98, 0x99, 0x91, 0x99, 0x9b, 0x96, - 0x97, 0xa4, 0x93, 0x99, 0x9b, 0x91, 0x9a, 0x9c, 0x98, 0x99, 0x99, 0x93, - 0x9b, 0x9d, 0x97, 0x9c, 0x9d, 0x93, 0x9d, 0xa1, 0x95, 0x9d, 0x9f, 0x96, - 0x9d, 0xa0, 0x98, 0xa0, 0xa3, 0x9d, 0x9f, 0xa0, 0x97, 0xa1, 0xa5, 0xa1, - 0xa1, 0xa1, 0x99, 0xa3, 0xa7, 0x9b, 0xa3, 0xa5, 0xa4, 0xa5, 0xa5, 0x9d, - 0xa7, 0xa9, 0xa6, 0xa8, 0xa8, 0xa2, 0xa9, 0xac, 0xa2, 0xab, 0xad, 0xaa, - 0xab, 0xab, 0xa6, 0xad, 0xb0, 0xa5, 0xaf, 0xb2, 0xab, 0xb3, 0xb6, 0xaf, - 0xb2, 0xb4, 0xae, 0xb5, 0xb8, 0xb4, 0xb4, 0xb4, 0xb0, 0xb6, 0xb8, 0xb1, - 0xb7, 0xba, 0xb2, 0xb7, 0xb9, 0xb4, 0xb7, 0xb9, 0xb2, 0xb8, 0xb9, 0xb2, - 0xb8, 0xba, 0x00, 0xff, 0x00, 0xb3, 0xb8, 0xb9, 0xb3, 0xbb, 0xbe, 0xb4, - 0xbb, 0xbd, 0xb5, 0xbb, 0xbd, 0xb6, 0xbb, 0xbc, 0xb7, 0xbc, 0xbf, 0xb8, - 0xbd, 0xbe, 0xbd, 0xbe, 0xbf, 0xbc, 0xbf, 0xc0, 0xbc, 0xc1, 0xc2, 0xbd, - 0xc1, 0xc2, 0xbb, 0xc2, 0xc5, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xbe, - 0xc5, 0xc7, 0xc2, 0xc4, 0xc5, 0xc3, 0xc4, 0xc4, 0xc2, 0xc6, 0xc7, 0xc4, - 0xc6, 0xc7, 0xc3, 0xc7, 0xc8, 0xc3, 0xc7, 0xc9, 0xc3, 0xc8, 0xca, 0xc4, - 0xc9, 0xcb, 0xc6, 0xca, 0xcb, 0xc6, 0xcc, 0xce, 0xca, 0xcd, 0xce, 0xca, - 0xce, 0xd0, 0xcb, 0xce, 0xcf, 0xca, 0xcf, 0xd1, 0xcb, 0xcf, 0xd0, 0xcb, - 0xd0, 0xd2, 0xcd, 0xd0, 0xd1, 0xcf, 0xd1, 0xd2, 0xd0, 0xd1, 0xd1, 0xce, - 0xd2, 0xd3, 0xd0, 0xd2, 0xd3, 0xd0, 0xd3, 0xd4, 0xd2, 0xd4, 0xd5, 0xd4, - 0xd5, 0xd8, 0xd2, 0xd6, 0xd8, 0xd2, 0xd7, 0xd8, 0xd4, 0xd7, 0xd8, 0xd3, - 0xd8, 0xd9, 0xd7, 0xd7, 0xdd, 0xd6, 0xd8, 0xd9, 0xd6, 0xd8, 0xda, 0xd7, - 0xd8, 0xd8, 0xd7, 0xda, 0xdb, 0xd8, 0xdb, 0xdd, 0xd7, 0xdc, 0xde, 0xd9, - 0xdc, 0xdd, 0xda, 0xdc, 0xdc, 0xdb, 0xdd, 0xdd, 0xdb, 0xdd, 0xde, 0xdc, - 0xdf, 0xe0, 0xdd, 0xdf, 0xe0, 0xdd, 0xe0, 0xe0, 0xde, 0xe1, 0xe2, 0xe0, - 0xe1, 0xe2, 0xe0, 0xe2, 0xe2, 0xe1, 0xe3, 0xe3, 0xe1, 0xe3, 0xe4, 0xe2, - 0xe3, 0xe3, 0xe1, 0xe6, 0xe7, 0xe5, 0xe7, 0xe7, 0xe6, 0xe7, 0xe7, 0xe6, - 0xe7, 0xe8, 0xe6, 0xe8, 0xe9, 0xe7, 0xe8, 0xe9, 0xe4, 0xe9, 0xeb, 0xe6, - 0xe9, 0xea, 0xe7, 0xe9, 0xea, 0xe8, 0xe9, 0xea, 0xe6, 0xea, 0xeb, 0xe9, - 0xeb, 0xec, 0xe8, 0xec, 0xed, 0xea, 0xec, 0xed, 0xeb, 0xec, 0xed, 0xeb, - 0xed, 0xed, 0xec, 0xed, 0xee, 0xec, 0xef, 0xf1, 0xbe, 0x3f, 0x0a, 0xac, - 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, - 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, - 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, - 0x0e, 0xc4, 0x00, 0x00, 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, - 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, - 0x36, 0x22, 0x1d, 0x4a, 0x61, 0x42, 0x00, 0x00, 0x01, 0xda, 0x49, 0x44, - 0x41, 0x54, 0x38, 0xcb, 0x63, 0x60, 0x20, 0x1b, 0xdc, 0x7e, 0x45, 0x94, - 0xb2, 0x0e, 0x67, 0xf5, 0xc3, 0x44, 0x28, 0xdb, 0x1c, 0xb7, 0x7a, 0xf5, - 0x44, 0xc2, 0xca, 0xde, 0x85, 0x14, 0xad, 0x5e, 0x2d, 0x7c, 0x88, 0xa0, - 0xba, 0x59, 0xd2, 0xab, 0x81, 0x60, 0x29, 0x41, 0x75, 0x9b, 0xec, 0x41, - 0xea, 0x7a, 0x08, 0xaa, 0x5b, 0xe9, 0x07, 0x52, 0x17, 0x4e, 0x50, 0xdd, - 0x9a, 0x60, 0x90, 0x3a, 0xd7, 0x27, 0x84, 0xd4, 0x4d, 0x76, 0x06, 0xa9, - 0xb3, 0xb8, 0x47, 0x48, 0xdd, 0x8a, 0x00, 0x90, 0xba, 0xa0, 0x13, 0x78, - 0x15, 0xbd, 0x3c, 0xbf, 0x35, 0xca, 0x1c, 0xa4, 0x4e, 0xa8, 0xa4, 0xbe, - 0xf9, 0x39, 0x4e, 0x65, 0x0f, 0x1c, 0xcd, 0x0c, 0x18, 0x35, 0x40, 0xea, - 0x58, 0x24, 0x9c, 0x04, 0xf3, 0x70, 0xaa, 0x3b, 0xad, 0x35, 0xbb, 0x8a, - 0x29, 0x14, 0xa4, 0xce, 0x34, 0x62, 0x49, 0x53, 0xe0, 0x5b, 0x5c, 0xea, - 0x76, 0x5b, 0xde, 0x5a, 0x8d, 0x00, 0x46, 0xf7, 0x71, 0x28, 0xfb, 0x5d, - 0x20, 0xbe, 0x03, 0x49, 0x1d, 0x7b, 0xcd, 0x65, 0xec, 0xea, 0xe6, 0x08, - 0xa4, 0xee, 0x44, 0x52, 0xb7, 0x5a, 0x21, 0x6c, 0x3b, 0x16, 0x55, 0x97, - 0xb7, 0x88, 0xc5, 0xee, 0x59, 0x8d, 0x02, 0xfc, 0x53, 0xa6, 0x9f, 0x42, - 0x57, 0x76, 0xc3, 0xd6, 0xa6, 0xed, 0xec, 0x6a, 0x34, 0x90, 0x23, 0xe3, - 0xb1, 0xe0, 0x17, 0xb2, 0xaa, 0x2f, 0xab, 0x32, 0xdc, 0xe6, 0xad, 0xc6, - 0x04, 0xd3, 0xf4, 0xe5, 0x0e, 0xbc, 0x47, 0x28, 0x7b, 0xd4, 0xcd, 0xba, - 0x1a, 0x07, 0xa8, 0x48, 0x6c, 0x38, 0x02, 0x53, 0xb6, 0x2c, 0x1a, 0x22, - 0x56, 0x69, 0x2f, 0x22, 0xcd, 0xc1, 0x09, 0x62, 0xf1, 0x33, 0x67, 0x27, - 0x4b, 0x79, 0xd5, 0xf5, 0x02, 0x99, 0x53, 0x4c, 0xaa, 0xdf, 0x40, 0x94, - 0x3d, 0xd6, 0x86, 0x28, 0x73, 0xcf, 0x9a, 0x71, 0xf2, 0x58, 0x2e, 0x1f, - 0x88, 0xa9, 0x58, 0xfa, 0xf4, 0xe7, 0x86, 0x49, 0x8d, 0x3a, 0x36, 0x20, - 0x8e, 0xe6, 0x7a, 0xa8, 0x79, 0x49, 0x20, 0x9e, 0x43, 0xf1, 0xde, 0xcf, - 0x40, 0xf6, 0xcd, 0x48, 0x10, 0xc7, 0xe0, 0x28, 0x58, 0xe2, 0x4c, 0xb9, - 0x35, 0x90, 0xc3, 0xb6, 0x0d, 0xa2, 0xec, 0x9e, 0xdd, 0x6a, 0x1e, 0xbd, - 0x96, 0xe3, 0x10, 0xce, 0x55, 0x63, 0x90, 0x3a, 0x95, 0xe5, 0x50, 0x23, - 0x76, 0x1d, 0x6e, 0x5d, 0x5d, 0x08, 0x65, 0x7f, 0x9b, 0xe0, 0xb9, 0xff, - 0x21, 0xcc, 0xad, 0x27, 0x45, 0x41, 0xea, 0x94, 0xe6, 0x23, 0x3c, 0x79, - 0xf0, 0x11, 0x9c, 0x79, 0x0d, 0x21, 0xfa, 0x4c, 0x15, 0xa4, 0x4e, 0x79, - 0x2e, 0xc1, 0x84, 0x9f, 0x09, 0x52, 0x67, 0x75, 0x81, 0x90, 0xb2, 0x3b, - 0xf1, 0x20, 0x75, 0xdc, 0x8b, 0x09, 0x28, 0xfb, 0xb1, 0xd0, 0x0e, 0xa4, - 0x4e, 0xbe, 0x8c, 0x40, 0x06, 0x99, 0x6a, 0x98, 0x00, 0x52, 0x57, 0xcb, - 0xeb, 0xb3, 0x16, 0x9f, 0xb2, 0x6d, 0x92, 0x31, 0x90, 0x30, 0x9f, 0xa9, - 0x2b, 0x7b, 0x0e, 0xb7, 0xb2, 0x17, 0xde, 0x69, 0xf0, 0x88, 0xcd, 0xef, - 0xff, 0x89, 0x53, 0x5d, 0x3b, 0x72, 0x0a, 0xe0, 0xda, 0x8e, 0x53, 0xdd, - 0x22, 0x94, 0xa4, 0xf2, 0x09, 0xa7, 0xba, 0x7d, 0x6a, 0xe9, 0x7d, 0xeb, - 0x2e, 0x75, 0xb9, 0x6c, 0xfc, 0x78, 0xa5, 0xd3, 0xf7, 0x22, 0x1e, 0x7f, - 0x7c, 0xf8, 0xfa, 0x1d, 0x48, 0x5e, 0x67, 0xf8, 0xc3, 0xf0, 0xfa, 0x2e, - 0x03, 0x4d, 0x01, 0x00, 0xfc, 0xea, 0x2e, 0xea, 0xf1, 0xce, 0x06, 0x98, - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int greenplane_png_len = 1368; -unsigned char aisred_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, - 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, - 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0xf9, 0xfb, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, - 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, - 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, - 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, - 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, - 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x35, 0x30, 0xc5, 0xde, 0x43, 0xc9, - 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, - 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, - 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, - 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, - 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, - 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, - 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, - 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, - 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int aisred_png_len = 227; -unsigned char aistrans_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, - 0x01, 0x03, 0x00, 0x00, 0x00, 0x8a, 0xa6, 0x00, 0xc0, 0x00, 0x00, 0x00, - 0x06, 0x50, 0x4c, 0x54, 0x45, 0x6f, 0x72, 0x74, 0x00, 0x00, 0x00, 0x3b, - 0x8a, 0x80, 0x24, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, - 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, - 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, - 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, - 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, - 0x01, 0x09, 0x0a, 0x36, 0x08, 0xc6, 0xf1, 0xa8, 0x94, 0x00, 0x00, 0x00, - 0x43, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0x00, 0x01, 0x05, - 0x34, 0x5c, 0x00, 0xc4, 0x01, 0x50, 0x7c, 0x03, 0x88, 0x3b, 0x10, 0x98, - 0xb1, 0x07, 0x88, 0x59, 0x10, 0x98, 0x99, 0x8d, 0x81, 0x81, 0x89, 0x09, - 0x8a, 0x95, 0x20, 0x34, 0x1b, 0x33, 0x03, 0x03, 0x0b, 0x23, 0x26, 0xe6, - 0x61, 0x6c, 0x60, 0xe0, 0x60, 0x40, 0x60, 0x09, 0x86, 0x03, 0x0c, 0x02, - 0x0c, 0x0e, 0x0c, 0xf2, 0xff, 0x0f, 0x00, 0x00, 0x1c, 0xa6, 0x0a, 0x23, - 0x27, 0x5d, 0x69, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, - 0xae, 0x42, 0x60, 0x82 -}; -unsigned int aistrans_png_len = 208; -unsigned char plane_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, - 0x08, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x7d, 0xa1, 0x07, 0x00, 0x00, 0x03, - 0x00, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x19, 0x1b, 0x27, 0x2c, - 0x30, 0x41, 0x37, 0x44, 0x49, 0x42, 0x46, 0x48, 0x39, 0x4c, 0x51, 0x41, - 0x4b, 0x4c, 0x40, 0x4d, 0x53, 0x43, 0x4c, 0x58, 0x44, 0x4e, 0x4f, 0x44, - 0x51, 0x56, 0x4c, 0x50, 0x52, 0x4d, 0x51, 0x53, 0x41, 0x55, 0x59, 0x4e, - 0x52, 0x54, 0x49, 0x54, 0x54, 0x4f, 0x53, 0x55, 0x4b, 0x56, 0x57, 0x44, - 0x58, 0x5c, 0x45, 0x59, 0x5e, 0x4b, 0x58, 0x5e, 0x53, 0x57, 0x5a, 0x4f, - 0x59, 0x5a, 0x47, 0x5b, 0x60, 0x4e, 0x5c, 0x61, 0x56, 0x5a, 0x5c, 0x47, - 0x5e, 0x68, 0x4a, 0x5e, 0x62, 0x52, 0x5c, 0x5d, 0x58, 0x5c, 0x5e, 0x53, - 0x5e, 0x5e, 0x54, 0x5f, 0x5f, 0x5a, 0x5e, 0x60, 0x53, 0x60, 0x66, 0x55, - 0x60, 0x60, 0x56, 0x60, 0x61, 0x57, 0x61, 0x62, 0x5c, 0x60, 0x62, 0x50, - 0x64, 0x68, 0x5d, 0x61, 0x63, 0x56, 0x63, 0x69, 0x5e, 0x62, 0x64, 0x59, - 0x64, 0x64, 0x5f, 0x63, 0x65, 0x61, 0x65, 0x67, 0x5c, 0x67, 0x68, 0x63, - 0x66, 0x63, 0x62, 0x66, 0x68, 0x5b, 0x68, 0x6e, 0x55, 0x6a, 0x6e, 0x5d, - 0x68, 0x69, 0x62, 0x67, 0x69, 0x5e, 0x69, 0x6a, 0x5f, 0x6a, 0x6b, 0x64, - 0x68, 0x7c, 0x5e, 0x6b, 0x71, 0x58, 0x6d, 0x71, 0x66, 0x6a, 0x6c, 0x67, - 0x6b, 0x6e, 0x68, 0x6c, 0x6f, 0x63, 0x6e, 0x6f, 0x6d, 0x6b, 0x6f, 0x5c, - 0x70, 0x75, 0x69, 0x6d, 0x70, 0x62, 0x6f, 0x75, 0x65, 0x70, 0x71, 0x6b, - 0x6f, 0x71, 0x64, 0x71, 0x77, 0x66, 0x71, 0x72, 0x6e, 0x70, 0x6d, 0x60, - 0x74, 0x79, 0x6e, 0x72, 0x75, 0x70, 0x72, 0x6f, 0x67, 0x75, 0x7b, 0x6f, - 0x73, 0x76, 0x70, 0x74, 0x77, 0x71, 0x75, 0x78, 0x6d, 0x78, 0x78, 0x73, - 0x77, 0x79, 0x74, 0x78, 0x7b, 0x78, 0x7a, 0x77, 0x71, 0x7c, 0x7d, 0x77, - 0x7b, 0x7e, 0x78, 0x7c, 0x7f, 0x79, 0x7d, 0x80, 0x7c, 0x7e, 0x7b, 0x7b, - 0x7f, 0x82, 0x75, 0x81, 0x81, 0x74, 0x82, 0x88, 0x7c, 0x81, 0x83, 0x75, - 0x83, 0x89, 0x7e, 0x82, 0x85, 0x7f, 0x83, 0x86, 0x80, 0x84, 0x87, 0x83, - 0x85, 0x82, 0x7a, 0x88, 0x8e, 0x7c, 0x88, 0x88, 0x7b, 0x89, 0x8f, 0x83, - 0x87, 0x8a, 0x85, 0x87, 0x84, 0x7e, 0x8a, 0x8a, 0x7f, 0x8b, 0x8b, 0x87, - 0x89, 0x86, 0x86, 0x8a, 0x8c, 0x87, 0x8b, 0x8e, 0x89, 0x8b, 0x88, 0x8a, - 0x8c, 0x89, 0x89, 0x8d, 0x90, 0x84, 0x90, 0x91, 0x8c, 0x8f, 0x8c, 0x8b, - 0x8f, 0x91, 0x8e, 0x90, 0x8d, 0x86, 0x92, 0x93, 0x85, 0x93, 0x99, 0x8e, - 0x92, 0x95, 0x90, 0x92, 0x8f, 0x91, 0x93, 0x90, 0x90, 0x94, 0x97, 0x8b, - 0x97, 0x98, 0x92, 0x96, 0x99, 0x94, 0x96, 0x93, 0x93, 0x98, 0x9a, 0x95, - 0x97, 0xa6, 0x8f, 0x9a, 0x9b, 0x94, 0x99, 0x9b, 0x97, 0x99, 0x96, 0x95, - 0x9a, 0x9c, 0x97, 0x9b, 0x9e, 0x91, 0x9d, 0x9d, 0x99, 0x9c, 0x98, 0x93, - 0x9e, 0x9f, 0x98, 0x9d, 0x9f, 0x9b, 0x9e, 0x9a, 0x9b, 0x9f, 0xa2, 0x96, - 0xa1, 0xa2, 0x9d, 0xa0, 0x9d, 0x9c, 0xa1, 0xa3, 0x9e, 0xa1, 0x9e, 0x9d, - 0xa2, 0xa4, 0x98, 0xa4, 0xa4, 0xa0, 0xa2, 0x9f, 0xa1, 0xa3, 0xa0, 0xa3, - 0xa5, 0xa2, 0x9c, 0xa8, 0xa8, 0xa4, 0xa6, 0xa3, 0xa5, 0xa7, 0xa4, 0xa4, - 0xa8, 0xab, 0xa6, 0xa8, 0xa5, 0xa0, 0xac, 0xad, 0xa9, 0xab, 0xa8, 0xa8, - 0xac, 0xaf, 0xa3, 0xaf, 0xb0, 0xad, 0xaf, 0xac, 0xac, 0xb1, 0xb3, 0xaf, - 0xb2, 0xae, 0xae, 0xb3, 0xb5, 0xb0, 0xb3, 0xaf, 0xb1, 0xb4, 0xb0, 0xb0, - 0xb5, 0xb7, 0xb2, 0xb5, 0xb2, 0xb1, 0xb6, 0xb8, 0xb2, 0xb7, 0xba, 0xb4, - 0xb7, 0xb3, 0xb4, 0xb8, 0xbb, 0xb6, 0xb8, 0xb5, 0xb6, 0xba, 0xbd, 0xb8, - 0xba, 0xb7, 0xb7, 0xbb, 0xbe, 0xb9, 0xbb, 0xb8, 0xb8, 0xbc, 0xbf, 0xbb, - 0xbd, 0xba, 0xba, 0xbf, 0xc1, 0xbd, 0xbf, 0xbc, 0xbc, 0xc1, 0xc3, 0xbd, - 0xc2, 0xc4, 0xbf, 0xc2, 0xbe, 0xc0, 0xc3, 0xbf, 0xbf, 0xc4, 0xc6, 0xc1, - 0xc4, 0xc1, 0xc3, 0xc5, 0xc2, 0xc1, 0xc6, 0xc8, 0xc4, 0xc6, 0xc3, 0xc5, - 0xc7, 0xc4, 0xc3, 0xc8, 0xca, 0xc5, 0xc9, 0xcc, 0xc7, 0xc9, 0xc6, 0xc6, - 0xca, 0xcd, 0xc9, 0xcb, 0xc8, 0xc7, 0xcc, 0xce, 0xca, 0xcc, 0xc9, 0xc9, - 0xce, 0xd0, 0xcb, 0xce, 0xca, 0xca, 0xcf, 0xd1, 0xcb, 0xd0, 0xd2, 0xcd, - 0xd0, 0xcc, 0xcc, 0xd1, 0xd4, 0xce, 0xd1, 0xce, 0xcd, 0xd2, 0xd5, 0xd0, - 0xd2, 0xcf, 0xcf, 0xd3, 0xd6, 0xd1, 0xd3, 0xd0, 0xd0, 0xd4, 0xd7, 0xd3, - 0xd6, 0xd2, 0xd2, 0xd6, 0xd9, 0xd5, 0xd7, 0xd4, 0xd3, 0xd8, 0xda, 0xda, - 0xd7, 0xdb, 0xd4, 0xd9, 0xdb, 0xd6, 0xd9, 0xd5, 0xd5, 0xda, 0xdc, 0xd7, - 0xda, 0xd6, 0xd6, 0xdb, 0xde, 0xd8, 0xdb, 0xd7, 0xd7, 0xdc, 0xdf, 0xd9, - 0xdc, 0xd9, 0xd8, 0xdd, 0xe0, 0xdb, 0xdd, 0xda, 0xdc, 0xde, 0xdb, 0xdb, - 0xe0, 0xe2, 0xde, 0xe0, 0xdd, 0xdf, 0xe1, 0xde, 0xdd, 0xe2, 0xe4, 0xe0, - 0xe3, 0xdf, 0xdf, 0xe4, 0xe6, 0xe1, 0xe4, 0xe0, 0xe2, 0xe5, 0xe1, 0xe1, - 0xe6, 0xe9, 0xe3, 0xe6, 0xe2, 0xe5, 0xe7, 0xe4, 0xe3, 0xe8, 0xeb, 0xe5, - 0xe9, 0xec, 0xe7, 0xe9, 0xe6, 0xe6, 0xea, 0xed, 0xe8, 0xea, 0xe7, 0xe7, - 0xec, 0xee, 0xe8, 0xed, 0xef, 0xea, 0xed, 0xe9, 0xe9, 0xee, 0xf0, 0xeb, - 0xee, 0xea, 0xea, 0xef, 0xf2, 0xed, 0xf0, 0xed, 0xec, 0xf1, 0xf4, 0xef, - 0xf1, 0xee, 0xf0, 0xf2, 0xef, 0xf1, 0xf3, 0xf0, 0xf2, 0xf4, 0xf1, 0xf3, - 0xf6, 0xf2, 0xf4, 0xf7, 0xf3, 0xf5, 0xf8, 0xf4, 0xf6, 0xf9, 0xf6, 0xf8, - 0xfa, 0xf7, 0xf9, 0xfb, 0xf8, 0xfa, 0xfc, 0xf9, 0xfb, 0xfd, 0xfa, 0xfc, - 0xff, 0xfb, 0xff, 0xff, 0xff, 0x17, 0x03, 0x0e, 0x9e, 0x00, 0x00, 0x00, - 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, - 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xc4, 0x00, 0x00, - 0x0e, 0xc4, 0x01, 0x95, 0x2b, 0x0e, 0x1b, 0x00, 0x00, 0x00, 0x07, 0x74, - 0x49, 0x4d, 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x3b, 0x79, 0x21, - 0xc9, 0x82, 0x00, 0x00, 0x02, 0x78, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, - 0x63, 0x60, 0x20, 0x1b, 0x5c, 0x7c, 0x40, 0x94, 0xb2, 0x4a, 0x4b, 0x95, - 0x5d, 0x44, 0x28, 0x5b, 0xed, 0xd7, 0x13, 0xdc, 0x44, 0x58, 0xd9, 0x33, - 0x97, 0xd8, 0x3b, 0xd3, 0x44, 0x76, 0x10, 0x54, 0xd7, 0x2f, 0x31, 0xe9, - 0xd7, 0x21, 0xd5, 0x05, 0x04, 0xd5, 0xad, 0x34, 0x5d, 0xf2, 0xed, 0x90, - 0x7e, 0x35, 0x41, 0x75, 0x8b, 0xed, 0xb7, 0xfe, 0x38, 0xe3, 0xe4, 0x49, - 0x58, 0x9d, 0xd3, 0xce, 0xdf, 0x57, 0x22, 0xcd, 0x6f, 0x11, 0x52, 0xd7, - 0x6a, 0xb9, 0xf3, 0xf7, 0x83, 0x18, 0x83, 0x8b, 0x84, 0xd4, 0x2d, 0xb0, - 0xdf, 0xfb, 0xe3, 0x71, 0xba, 0xf3, 0x01, 0xbc, 0x8a, 0xee, 0x1f, 0x5f, - 0xeb, 0xa9, 0xbf, 0xf3, 0xd7, 0x83, 0x34, 0x91, 0xf8, 0x82, 0xb2, 0x7b, - 0x38, 0x95, 0xdd, 0xb0, 0xd0, 0xd5, 0x62, 0x56, 0xdd, 0xf6, 0xfb, 0x71, - 0x16, 0x9b, 0x98, 0x25, 0x7f, 0x04, 0x4e, 0x75, 0x07, 0x55, 0x27, 0x67, - 0xb1, 0xb8, 0x5e, 0xf8, 0xfd, 0x65, 0x89, 0x9e, 0xc7, 0xdc, 0x52, 0xc7, - 0x27, 0xb8, 0xd4, 0x6d, 0x34, 0xbe, 0xf0, 0xbc, 0x77, 0xf3, 0xe7, 0x7f, - 0x7f, 0x1f, 0xae, 0xd9, 0x76, 0x63, 0xae, 0xf6, 0x15, 0x1c, 0xca, 0xde, - 0x46, 0x89, 0x6e, 0xf8, 0xfa, 0xee, 0xfb, 0xdf, 0x7f, 0xff, 0xfe, 0xfd, - 0xf8, 0xfa, 0x79, 0x02, 0x67, 0xce, 0x69, 0xec, 0xea, 0x26, 0x09, 0x05, - 0x6c, 0xfe, 0xf5, 0x0f, 0x0a, 0xbe, 0x6d, 0x8d, 0x95, 0x73, 0x5b, 0x87, - 0x45, 0xd5, 0xe9, 0xd5, 0x22, 0xde, 0x9b, 0x3f, 0xfd, 0x83, 0x83, 0xaf, - 0xfb, 0x7b, 0xec, 0x03, 0xbb, 0x0f, 0xa2, 0x2b, 0x3b, 0x6f, 0x60, 0x5c, - 0x71, 0xec, 0x0b, 0x50, 0x1e, 0x64, 0x2d, 0x98, 0xf8, 0xf3, 0x78, 0x55, - 0x98, 0x8c, 0xdd, 0xf4, 0x77, 0x28, 0xa9, 0x69, 0x71, 0x90, 0xe5, 0xd4, - 0x07, 0x7f, 0xfe, 0xfc, 0x7e, 0xf3, 0xf8, 0xf9, 0x37, 0xa0, 0x59, 0x0f, - 0x6f, 0xbf, 0xfc, 0xfa, 0xfb, 0xc7, 0xe7, 0x9d, 0x3d, 0x9a, 0xd2, 0x5b, - 0x9e, 0x21, 0x94, 0xdd, 0xaa, 0x61, 0xcf, 0xdb, 0xfc, 0xf2, 0xcb, 0x93, - 0xcd, 0xb3, 0xea, 0x8a, 0x4b, 0x4f, 0xfd, 0xfe, 0xbc, 0x2d, 0x33, 0x29, - 0xbd, 0xa5, 0x77, 0xff, 0xbb, 0xcf, 0x67, 0x96, 0x66, 0xf8, 0x15, 0xee, - 0x81, 0xc7, 0x95, 0xa7, 0x7d, 0xe6, 0x89, 0xdb, 0x87, 0x96, 0x64, 0x98, - 0x8a, 0x49, 0x70, 0x71, 0xcf, 0xf8, 0xfa, 0xbd, 0x57, 0x80, 0x35, 0xdc, - 0x5f, 0xc2, 0x3a, 0xbf, 0x76, 0xe7, 0xcd, 0x25, 0x1d, 0x3a, 0xd9, 0xd0, - 0x80, 0xbc, 0xa5, 0x26, 0x39, 0x75, 0xfd, 0x8a, 0x09, 0x0a, 0x96, 0x21, - 0x5d, 0x07, 0xf6, 0x45, 0xf0, 0x2f, 0xf9, 0xfc, 0x65, 0xa2, 0x7c, 0xe2, - 0xdd, 0xd7, 0xcb, 0x9b, 0x4b, 0x35, 0x8c, 0xd3, 0x7a, 0xfa, 0xf2, 0x55, - 0x96, 0x42, 0xcd, 0xf3, 0x8f, 0xeb, 0x29, 0xe2, 0x31, 0x4d, 0x00, 0xbb, - 0xe4, 0xbc, 0xfb, 0xac, 0xcf, 0x5f, 0xfa, 0xb4, 0xf6, 0x41, 0x22, 0x28, - 0xd5, 0xd0, 0xc4, 0x52, 0x96, 0x73, 0x2d, 0x34, 0x1f, 0x1a, 0xa9, 0xf3, - 0x69, 0x94, 0x41, 0x64, 0x18, 0xce, 0x6a, 0xcd, 0xfc, 0xfa, 0x69, 0x82, - 0x12, 0x2c, 0xe1, 0x6f, 0xdc, 0x55, 0x2e, 0x1c, 0x0b, 0x65, 0xbf, 0x6a, - 0xb0, 0xdb, 0x72, 0x1d, 0xe6, 0xd6, 0x03, 0x62, 0x93, 0x3e, 0x7d, 0xed, - 0x51, 0x9a, 0x86, 0xf0, 0xe4, 0x76, 0x44, 0x9a, 0x3d, 0x8b, 0x10, 0xbd, - 0xab, 0xd8, 0xf1, 0xfc, 0xd7, 0x44, 0xc5, 0x29, 0x04, 0x13, 0x7e, 0x70, - 0xf2, 0xed, 0x4f, 0x8d, 0x06, 0xc7, 0x09, 0x16, 0x1a, 0xbe, 0x21, 0x57, - 0xbe, 0x94, 0xf0, 0xce, 0x23, 0xa0, 0xec, 0xf5, 0x6c, 0xa3, 0xe8, 0x8b, - 0x5f, 0xeb, 0xe5, 0x52, 0x08, 0x64, 0x90, 0x36, 0x0d, 0xbf, 0x65, 0x9f, - 0xfe, 0x5d, 0xca, 0xe3, 0xb7, 0x5b, 0x8a, 0x4f, 0xd9, 0x5a, 0x71, 0xaf, - 0x25, 0x1f, 0xff, 0xfd, 0xfe, 0x72, 0x6a, 0x82, 0xba, 0x14, 0x1e, 0x27, - 0x3e, 0xb0, 0x09, 0x3a, 0xf1, 0xe9, 0xce, 0xde, 0x8d, 0x87, 0x5e, 0x9e, - 0x5a, 0x1d, 0xd9, 0xfc, 0x1a, 0x77, 0x91, 0x26, 0xd7, 0xb7, 0x6b, 0x4e, - 0xba, 0xad, 0x8e, 0x75, 0xde, 0x8c, 0xb9, 0xa1, 0xbc, 0xeb, 0x70, 0xaa, - 0x9b, 0x2d, 0x1b, 0xed, 0xcf, 0xcf, 0xc1, 0xc4, 0xa8, 0x3d, 0xcd, 0xc7, - 0x4a, 0x20, 0xe6, 0x19, 0x4e, 0x75, 0x5b, 0x94, 0x03, 0x1b, 0x96, 0x9e, - 0xac, 0x32, 0x5b, 0xf9, 0xec, 0x74, 0xb5, 0xc3, 0x49, 0x7c, 0xa5, 0xdf, - 0x8b, 0x57, 0x40, 0xf2, 0x1c, 0xc3, 0x5b, 0x86, 0xfb, 0x17, 0x19, 0x68, - 0x0a, 0x00, 0x52, 0xa4, 0x31, 0x22, 0xfa, 0xbb, 0x4c, 0x44, 0x00, 0x00, - 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int plane_png_len = 1522; -unsigned char helicopter_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1f, - 0x04, 0x03, 0x00, 0x00, 0x00, 0xed, 0x80, 0x0b, 0x0a, 0x00, 0x00, 0x00, - 0x15, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, - 0xf8, 0xff, 0xff, 0x00, 0x00, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0xff, - 0xff, 0xff, 0x96, 0xba, 0xb8, 0x46, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, - 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2, - 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, - 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x2f, 0x63, 0xfb, 0x1d, 0xff, 0x00, 0x00, - 0x00, 0x70, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0x9d, 0xd1, 0xc1, 0x0d, - 0x80, 0x20, 0x0c, 0x05, 0x50, 0x2e, 0xde, 0x6d, 0x52, 0x16, 0x20, 0x6e, - 0x00, 0x0b, 0x54, 0x3a, 0x80, 0x3d, 0xb8, 0xff, 0x2a, 0x62, 0x89, 0x26, - 0x68, 0x7b, 0xd0, 0x7f, 0x21, 0x7d, 0x49, 0x39, 0xfc, 0x06, 0x18, 0x33, - 0x87, 0x3f, 0x10, 0xf7, 0x3b, 0x62, 0x83, 0x26, 0xca, 0xe3, 0x0f, 0x0f, - 0x90, 0x3c, 0x40, 0x66, 0xd2, 0xc7, 0x81, 0x36, 0xd6, 0xc2, 0xa4, 0x6a, - 0x02, 0x72, 0xcd, 0x39, 0x17, 0xd6, 0x35, 0x0b, 0xfa, 0x7c, 0x0a, 0x81, - 0x0d, 0x97, 0x14, 0x02, 0x0f, 0xba, 0xf4, 0x0d, 0x07, 0x00, 0xd7, 0x94, - 0x16, 0x02, 0x1f, 0x5a, 0x89, 0xdb, 0xa4, 0x05, 0x7a, 0xa0, 0x24, 0x43, - 0x85, 0x2f, 0xf8, 0x74, 0xca, 0x03, 0x65, 0xe5, 0x3e, 0xf8, 0xf3, 0x53, - 0x78, 0x63, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, - 0x60, 0x82 -}; -unsigned int helicopter_png_len = 242; -unsigned char aisnavaid_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, - 0x01, 0x03, 0x00, 0x00, 0x00, 0x93, 0xd9, 0x37, 0xd5, 0x00, 0x00, 0x00, - 0x06, 0x50, 0x4c, 0x54, 0x45, 0x63, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x0b, - 0x25, 0x74, 0xbd, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, - 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, - 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, - 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, - 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xe6, - 0x01, 0x09, 0x0a, 0x22, 0x0e, 0x01, 0x3c, 0xda, 0xf4, 0x00, 0x00, 0x00, - 0x16, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff, 0x07, - 0x03, 0x0c, 0x3f, 0x28, 0xb0, 0xc0, 0x8a, 0x49, 0x54, 0x03, 0x00, 0x6b, - 0xd8, 0x2d, 0x07, 0x1f, 0xad, 0xa8, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x49, - 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int aisnavaid_png_len = 163; - -unsigned char aiswhite_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x22, - 0x02, 0x03, 0x00, 0x00, 0x00, 0xcd, 0x06, 0x7a, 0x10, 0x00, 0x00, 0x00, - 0x09, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xff, 0x38, 0x24, 0x6b, 0x47, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, - 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x00, 0x01, 0x62, - 0x4b, 0x47, 0x44, 0x00, 0x88, 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, - 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b, 0x13, - 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, - 0x45, 0x07, 0xe6, 0x01, 0x09, 0x0a, 0x36, 0x16, 0x3c, 0xfe, 0x95, 0xf7, - 0x00, 0x00, 0x00, 0x53, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, - 0x80, 0x01, 0x16, 0x3c, 0x94, 0x28, 0x84, 0x92, 0x44, 0xa1, 0x22, 0x1d, - 0xc0, 0x54, 0x16, 0x16, 0x8a, 0x31, 0x2b, 0x00, 0x4c, 0xad, 0x9a, 0x80, - 0x49, 0xb1, 0xae, 0x9a, 0x02, 0xa2, 0xd8, 0x56, 0x2d, 0x41, 0xa6, 0x96, - 0x21, 0xf3, 0xc4, 0x56, 0x2d, 0x05, 0x51, 0x52, 0xab, 0x56, 0x12, 0xa2, - 0xa2, 0x56, 0xad, 0x04, 0x59, 0x98, 0xb5, 0x6a, 0x15, 0x06, 0xc5, 0x08, - 0xa4, 0x02, 0x40, 0xb6, 0xae, 0x02, 0xd9, 0xcb, 0x18, 0x1a, 0x1a, 0x1a, - 0xc0, 0x00, 0x00, 0xd1, 0xed, 0x24, 0x45, 0x35, 0x85, 0xab, 0x6c, 0x00, - 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; -unsigned int aiswhite_png_len = 227; -char * get_info_call() -{ - char Msg[] = - "" - "" - "" - "" - "" - "\r\n" - " \r\n" - "\r\n" - "##MY_CALLSIGN##'s BPQ32 Web Server\r\n" - "\r\n" - - "\r\n" - - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "Home\r\n" - "All Stations\r\n" - "RF Stations\r\n" - "All WX Stations\r\n" - "RF WX Stations\r\n" - "All Mobile Stations\r\n" - "RF Mobile Stations\r\n" - "All Objects\r\n" - "RF Objects\r\n" - "Information\r\n" - "Node Pages\r\n" - "\r\n" - "\r\n" - - "\r\n" - "

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

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

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

\r\n" - - "

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

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

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

\r\n" - - "

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

Information about Object ##CALLSIGN##

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

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

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

\r\n" - - "

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

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

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

\r\n" - - "

Weather Data

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

All Stations

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - "

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

All Mobile Stations

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - "

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

All Objects

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - "

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - "

No information is available for ##CALLSIGN##

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

All WX Stations

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - "

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

Information

\r\n" - - "\r\n" - "
\r\n" - "

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

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

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

" - - "

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

RF Stations

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - - "

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

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

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

Mobile RF Stations

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - - "

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

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

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

RF Objects

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - - "

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

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

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

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

##MY_CALLSIGN##'s BPQ32 APRS Web Server

\r\n" - - "

RF WX Stations

\r\n" - - "

(This page will automatically refresh every five minutes)

\r\n" - - "

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

" - - "

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

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

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

" - - "\r\n" - "\r\n" - "\r\n" - "##STATION_TABLE##
CallsignSymbolLocation##MILES_KM##BearingLast heard
\r\n" - - "\r\n" - "\r\n"; - - return _strdup(Msg);; -} - -char * get_favicon(int * Len) -{ - unsigned char * ptr; - - unsigned char Msg[] = - { - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0xC0, 0xC0, - 0xC0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0x00, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xB0, 0x0B, 0xB0, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, - 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, 0x0B, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x0B, 0xBB, 0xB0, - 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, - 0xBB, 0x00, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xB0, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0x00, 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, - 0x00, 0x00, 0x0B, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0x00, 0x00, 0x00, 0x0B, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, - 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0x00, 0x00, 0x0B, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, - 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0xBB, 0xBB, 0x00, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, - 0x00, 0x00, 0x0B, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, - 0xBB, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - -#define favicon_LENGTH 0x000002FE - - ptr = malloc(favicon_LENGTH); - memcpy(ptr, Msg, favicon_LENGTH); - - return ptr; -} - -static unsigned char Clouds[] = -{ - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, - 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xE1, 0x08, 0x03, - 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x07, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x62, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x6A, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x72, 0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x86, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x48, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, - 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, - 0x68, 0x6F, 0x70, 0x20, 0x37, 0x2E, 0x30, 0x00, 0x32, 0x30, 0x31, 0x32, - 0x3A, 0x30, 0x35, 0x3A, 0x33, 0x30, 0x20, 0x31, 0x32, 0x3A, 0x33, 0x38, - 0x3A, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03, 0xA0, 0x01, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0xA0, 0x02, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF4, 0xA0, 0x03, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x06, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x16, 0x01, 0x1B, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x1E, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x26, 0x02, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x06, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, - 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xED, 0x00, 0x0C, - 0x41, 0x64, 0x6F, 0x62, 0x65, 0x5F, 0x43, 0x4D, 0x00, 0x02, 0xFF, 0xEE, - 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, - 0x00, 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, - 0x08, 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, - 0x0C, 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, - 0x0D, 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, - 0x0E, 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, - 0x80, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, - 0xDD, 0x00, 0x04, 0x00, 0x08, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, - 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, - 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, - 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, - 0x23, 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, - 0x07, 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, - 0xB2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, - 0x17, 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, - 0xF3, 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, - 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, - 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, - 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, - 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, - 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, - 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, - 0x52, 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, - 0x15, 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, - 0x26, 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, - 0x36, 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, - 0x46, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, - 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, - 0xD6, 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, - 0xB7, 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, - 0x11, 0x00, 0x3F, 0x00, 0xF4, 0x22, 0x09, 0x44, 0xAC, 0x18, 0xD1, 0x40, - 0x14, 0x7A, 0x84, 0x85, 0x29, 0x61, 0x0C, 0x60, 0xF8, 0x28, 0xB8, 0x4A, - 0xB0, 0x5A, 0x10, 0x8B, 0x7D, 0xDE, 0x09, 0x02, 0x92, 0x1A, 0xEE, 0x08, - 0x64, 0x42, 0xB2, 0xF0, 0x10, 0x6C, 0x6C, 0x09, 0x1A, 0xA2, 0x0A, 0xD2, - 0x1A, 0xEE, 0x74, 0x21, 0xEE, 0x51, 0xB9, 0xE4, 0x18, 0x00, 0x94, 0x22, - 0xF7, 0x07, 0x42, 0x78, 0x0B, 0x09, 0x4F, 0x29, 0xA5, 0x44, 0x6E, 0x70, - 0xD0, 0x29, 0x32, 0xB7, 0x1E, 0x78, 0x49, 0x4A, 0xE5, 0x49, 0xA0, 0xA9, - 0x06, 0x34, 0x09, 0xF0, 0x4C, 0xDE, 0x50, 0x4D, 0x32, 0x68, 0x46, 0x63, - 0x47, 0x7D, 0x14, 0x1A, 0xE8, 0x10, 0x8A, 0xD3, 0x25, 0x02, 0x90, 0xCB, - 0x60, 0x09, 0xE5, 0x46, 0x4F, 0x8A, 0x52, 0x7B, 0xA0, 0x9B, 0x7F, 0xFF, - 0xD0, 0xF4, 0x26, 0xB4, 0xF7, 0x56, 0x2B, 0x8F, 0x04, 0x1D, 0xE0, 0xF0, - 0x21, 0x15, 0xA6, 0x02, 0x98, 0xB0, 0x84, 0xC3, 0x85, 0x02, 0x07, 0x25, - 0x20, 0xFD, 0x14, 0x6C, 0x70, 0x23, 0x44, 0xDA, 0x4A, 0x2B, 0x1D, 0xAA, - 0x03, 0xAC, 0x04, 0xC1, 0x45, 0xB1, 0x84, 0x83, 0xE0, 0xAA, 0xB9, 0xAE, - 0x13, 0xF9, 0x53, 0x82, 0xD2, 0x56, 0x24, 0xB9, 0xDA, 0x08, 0x08, 0xAD, - 0xC6, 0x6C, 0x4B, 0xBE, 0xF5, 0x0A, 0xD8, 0x49, 0x93, 0xC0, 0xE0, 0x23, - 0x4F, 0xB7, 0x50, 0x89, 0x40, 0x44, 0xF0, 0xD6, 0x98, 0x89, 0xF3, 0x50, - 0xB2, 0xC0, 0xC0, 0x48, 0x10, 0xA5, 0x6B, 0xD8, 0xC3, 0x3A, 0x94, 0x17, - 0x58, 0xCB, 0x3E, 0x7D, 0x91, 0x08, 0x25, 0x1F, 0xAE, 0x1C, 0x76, 0xEB, - 0x25, 0x11, 0xB3, 0xA4, 0x4A, 0x42, 0x80, 0x06, 0xE5, 0x30, 0xD8, 0x8D, - 0x11, 0x40, 0xBE, 0xA9, 0x2B, 0x73, 0x46, 0x84, 0x4C, 0xA2, 0xFB, 0x47, - 0x65, 0x0A, 0xF9, 0x82, 0x34, 0x45, 0x21, 0xA1, 0x34, 0xAE, 0x0C, 0x80, - 0xDD, 0xC0, 0x48, 0x80, 0x07, 0x1A, 0xA8, 0xB2, 0xC6, 0x8E, 0xFA, 0x29, - 0x7A, 0x80, 0xA0, 0x97, 0xFF, 0xD1, 0xF4, 0x1A, 0xCC, 0x8D, 0x51, 0x37, - 0x82, 0x15, 0x2A, 0xAE, 0x9E, 0x55, 0x86, 0x49, 0x20, 0x8E, 0x14, 0xE4, - 0x35, 0xC1, 0x6C, 0x86, 0xCB, 0x7C, 0xD0, 0x5C, 0xE8, 0x3A, 0xA2, 0x87, - 0x10, 0x39, 0x55, 0xAD, 0x81, 0x24, 0x94, 0x02, 0x4B, 0x37, 0x5B, 0xB4, - 0x6B, 0xAC, 0xA1, 0xB5, 0xBB, 0xFE, 0x03, 0x55, 0x16, 0x89, 0x1A, 0xF7, - 0xE1, 0x11, 0xB0, 0xC6, 0xA2, 0x8B, 0x58, 0x80, 0x38, 0xF6, 0xA8, 0x97, - 0x1D, 0xB3, 0xBA, 0x0F, 0x82, 0x4E, 0xD7, 0xC8, 0x1E, 0xC9, 0xBD, 0x22, - 0x62, 0x52, 0x55, 0xA2, 0xB2, 0xB9, 0x1A, 0x19, 0x43, 0x2C, 0xF2, 0xD5, - 0x5D, 0x35, 0xD7, 0xB7, 0x68, 0x10, 0x50, 0x36, 0x41, 0x23, 0xF1, 0x4E, - 0x05, 0x04, 0x2E, 0xC7, 0x43, 0x7C, 0xD2, 0x0E, 0x6C, 0x68, 0xA0, 0x46, - 0xC9, 0xF1, 0x48, 0x81, 0x00, 0xFD, 0xE9, 0x52, 0x2D, 0x6F, 0x59, 0xD3, - 0x00, 0xC2, 0x9F, 0xAA, 0xF8, 0xFA, 0x48, 0x24, 0x7B, 0x92, 0x6D, 0x80, - 0x18, 0x76, 0x90, 0x95, 0x22, 0xD9, 0xFA, 0x86, 0x79, 0xD4, 0x27, 0x6B, - 0xDC, 0x4F, 0x2A, 0xBB, 0xAD, 0x6C, 0x94, 0x99, 0x76, 0xB2, 0x8D, 0x2A, - 0xDF, 0xFF, 0xD2, 0xED, 0x68, 0x68, 0x8F, 0x35, 0x66, 0xB7, 0x96, 0x13, - 0xAE, 0x8A, 0xB3, 0x76, 0x31, 0x93, 0x30, 0x67, 0x84, 0xDE, 0xA8, 0x2E, - 0xD4, 0xAB, 0x24, 0x5B, 0x50, 0x1A, 0x6F, 0x7A, 0xA1, 0xDD, 0xE5, 0x02, - 0xE7, 0x01, 0xA3, 0x8F, 0xC9, 0x53, 0xB3, 0x20, 0xB5, 0xE4, 0xB7, 0x8F, - 0x04, 0x3F, 0x5D, 0xD6, 0x76, 0x44, 0x41, 0x46, 0x6D, 0xC6, 0xD9, 0xAE, - 0x9C, 0x22, 0x87, 0xB7, 0xBF, 0x2A, 0xA3, 0x09, 0xDB, 0x10, 0x3F, 0x8A, - 0x98, 0x7B, 0x82, 0x44, 0x28, 0x49, 0xB6, 0x4B, 0x42, 0x94, 0x83, 0x0A, - 0xA8, 0xB0, 0x11, 0x12, 0x65, 0x19, 0x91, 0x09, 0xB4, 0xB8, 0x16, 0x76, - 0x4F, 0xE6, 0xEA, 0x7B, 0x21, 0x7B, 0x86, 0xAE, 0xD3, 0xE2, 0xA7, 0xEA, - 0x11, 0x12, 0x9A, 0xDB, 0x18, 0x5B, 0xA9, 0x94, 0x94, 0x85, 0xF1, 0x3E, - 0xEE, 0xE8, 0x86, 0xB9, 0x60, 0x11, 0x03, 0xC5, 0x0C, 0x34, 0x1D, 0x47, - 0x6E, 0xC5, 0x11, 0xB6, 0xB8, 0x0D, 0x46, 0x83, 0x84, 0x4A, 0x02, 0x27, - 0x8F, 0x4C, 0x6B, 0xCA, 0xA9, 0x6D, 0x85, 0xDF, 0x04, 0x7B, 0x8E, 0xF3, - 0xA6, 0x84, 0x94, 0xDE, 0x80, 0x03, 0xDC, 0x7E, 0x49, 0xC1, 0x69, 0xD5, - 0xA2, 0x5E, 0x67, 0x5F, 0xBD, 0x16, 0xA0, 0x4E, 0xBF, 0x72, 0xB4, 0xCC, - 0x6A, 0xCB, 0x74, 0x4E, 0x68, 0xD9, 0x03, 0x90, 0x8F, 0x10, 0x5B, 0xC2, - 0x5F, 0xFF, 0xD3, 0xEA, 0xCD, 0xA7, 0xE8, 0x84, 0xDB, 0xFC, 0x79, 0x4F, - 0xE9, 0x38, 0xA7, 0xF4, 0x65, 0x5C, 0xD1, 0xA1, 0xAA, 0x33, 0xA9, 0x48, - 0x13, 0xD9, 0x17, 0xD1, 0x69, 0xD4, 0x9D, 0x14, 0x0E, 0x86, 0x3C, 0x38, - 0x45, 0x54, 0xB7, 0xA8, 0x41, 0xD3, 0x94, 0x41, 0x69, 0xEE, 0x50, 0x5C, - 0xD2, 0x99, 0xAC, 0x74, 0xF9, 0x25, 0xA2, 0xAC, 0xB6, 0x1B, 0x71, 0x07, - 0x8F, 0x9A, 0x28, 0xB1, 0xC6, 0x35, 0x55, 0x64, 0x8E, 0x79, 0x44, 0x63, - 0x87, 0x32, 0x81, 0x09, 0x05, 0x3B, 0x9C, 0xE3, 0xAC, 0xA6, 0x13, 0x3A, - 0x73, 0xE2, 0xA2, 0x1C, 0x0F, 0x74, 0x4A, 0xE0, 0x90, 0x0F, 0x74, 0x17, - 0x32, 0x60, 0x23, 0xCD, 0x4C, 0x26, 0x88, 0x30, 0x91, 0x29, 0xA9, 0x46, - 0xE6, 0x80, 0xE9, 0x8E, 0x39, 0x51, 0x71, 0xDD, 0xA7, 0x08, 0xA5, 0x30, - 0x69, 0x3C, 0x04, 0x6D, 0x0C, 0x01, 0xD9, 0xC7, 0x3E, 0x2A, 0x0F, 0x79, - 0x8E, 0x54, 0x9E, 0xD2, 0x0A, 0x0B, 0xDA, 0x4A, 0x21, 0x05, 0xFF, 0xD4, - 0xEC, 0xC3, 0x44, 0x6A, 0x96, 0xE0, 0x3C, 0xD5, 0x6F, 0xB4, 0x87, 0x1D, - 0x24, 0xA2, 0x56, 0x4B, 0xCC, 0x42, 0xB7, 0x4D, 0x3B, 0x1D, 0x12, 0x68, - 0x75, 0xF0, 0x50, 0x73, 0x9B, 0xD8, 0x29, 0xBD, 0x9B, 0x47, 0x28, 0x71, - 0xDD, 0x20, 0xA2, 0xC6, 0x0F, 0x60, 0x9F, 0x67, 0x92, 0x93, 0x66, 0x51, - 0x01, 0x8E, 0x44, 0xA5, 0x6A, 0xA4, 0x05, 0x93, 0xCA, 0x67, 0x6D, 0x6A, - 0x23, 0xCE, 0xBA, 0x28, 0x47, 0xF2, 0x44, 0xA2, 0x82, 0x15, 0x59, 0x9F, - 0x29, 0xF1, 0x56, 0x2B, 0x60, 0xE4, 0x99, 0xF2, 0x55, 0xB7, 0x86, 0xC4, - 0x85, 0x2F, 0xB4, 0xBF, 0x81, 0xF8, 0xA0, 0x6D, 0x40, 0x80, 0xDB, 0x0E, - 0x05, 0x22, 0x55, 0x56, 0xD9, 0xAC, 0xCA, 0x28, 0xB0, 0x21, 0x4B, 0xAD, - 0x28, 0x1B, 0xB8, 0x45, 0xAD, 0xA5, 0xBC, 0xF7, 0x55, 0x3E, 0xD0, 0x1A, - 0xA4, 0xCC, 0xC6, 0x9D, 0x0F, 0x28, 0x10, 0x54, 0x08, 0x4D, 0x68, 0x07, - 0x8D, 0x4A, 0x09, 0x69, 0xF0, 0x4E, 0xEB, 0xC7, 0x68, 0x3E, 0x68, 0x67, - 0x29, 0xAD, 0x69, 0x9F, 0x92, 0x20, 0x15, 0x12, 0x1F, 0xFF, 0xD5, 0xEB, - 0x46, 0x33, 0x87, 0x01, 0x3F, 0xA7, 0x6B, 0x75, 0x06, 0x07, 0xE2, 0xAD, - 0x8D, 0xC3, 0x8E, 0xE9, 0xB6, 0x13, 0xCA, 0xB5, 0xC4, 0xD4, 0xE1, 0x0D, - 0x33, 0xEA, 0x18, 0x92, 0xA5, 0xC0, 0x8E, 0xEA, 0xDF, 0xA2, 0xD3, 0xD9, - 0x2F, 0x48, 0x01, 0xC2, 0x5C, 0x4A, 0xE1, 0x2D, 0x51, 0x1F, 0x34, 0xE6, - 0xC0, 0x02, 0x31, 0xAC, 0x14, 0x3B, 0x18, 0x47, 0x01, 0x2B, 0x0A, 0xA2, - 0x87, 0x79, 0x9D, 0x54, 0x1D, 0x6F, 0x75, 0x37, 0x53, 0x61, 0xD6, 0x14, - 0x0D, 0x64, 0x09, 0x21, 0x3B, 0x45, 0xA6, 0xD1, 0xBD, 0xFB, 0xB5, 0x01, - 0x38, 0x32, 0x3F, 0x82, 0x44, 0x38, 0xE8, 0x02, 0x23, 0x2B, 0x9F, 0x2F, - 0x8A, 0x56, 0x8A, 0x60, 0x1D, 0x1A, 0xA4, 0x6D, 0x8F, 0x8A, 0x28, 0xA9, - 0xDD, 0xB5, 0x1F, 0x82, 0x4E, 0xA8, 0x1D, 0x74, 0x94, 0xAC, 0x2A, 0x8A, - 0x22, 0xF9, 0x52, 0x68, 0xF2, 0x92, 0xA4, 0x6B, 0x20, 0x79, 0xF9, 0x26, - 0x1B, 0x87, 0x23, 0x44, 0xAD, 0x34, 0xC1, 0xAC, 0x71, 0x76, 0xB2, 0x8A, - 0x71, 0x89, 0xD7, 0x52, 0x54, 0x98, 0x78, 0x85, 0x65, 0x80, 0xC2, 0x06, - 0x49, 0x11, 0x0F, 0xFF, 0xD6, 0xEF, 0x9A, 0xDF, 0x15, 0x20, 0xDF, 0x24, - 0xFC, 0x15, 0x36, 0x89, 0x53, 0x5B, 0x05, 0x30, 0xDA, 0xA2, 0x5A, 0x8E, - 0x5A, 0xA0, 0x5A, 0x95, 0xA6, 0x90, 0x96, 0xC2, 0x81, 0x00, 0xA2, 0xB8, - 0x28, 0x22, 0xB5, 0x19, 0x60, 0x88, 0x84, 0x27, 0xD6, 0x22, 0x00, 0xE1, - 0x58, 0x25, 0x0C, 0xA2, 0x10, 0x40, 0x43, 0xB1, 0xA7, 0xE9, 0x69, 0x0A, - 0x62, 0xB1, 0x13, 0x32, 0x12, 0x21, 0x38, 0x24, 0x70, 0x8D, 0xA2, 0x91, - 0x19, 0x07, 0x44, 0xA1, 0xDF, 0xEC, 0x44, 0x30, 0x90, 0x84, 0xAD, 0x54, - 0xC1, 0xAD, 0x77, 0x8A, 0x23, 0x6B, 0x61, 0x3E, 0xED, 0x54, 0x80, 0x08, - 0x83, 0x6F, 0x64, 0x09, 0x48, 0x0C, 0x05, 0x4D, 0xEC, 0x11, 0x03, 0x23, - 0x84, 0xED, 0x08, 0x80, 0x21, 0x69, 0x01, 0xFF, 0xD9, 0xFF, 0xED, 0x0C, - 0x98, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, 0x68, 0x6F, 0x70, 0x20, 0x33, - 0x2E, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x25, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x03, - 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x38, - 0x42, 0x49, 0x4D, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, - 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x1E, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x19, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1E, 0x38, 0x42, 0x49, - 0x4D, 0x03, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x42, 0x49, - 0x4D, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x03, - 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x2F, 0x66, 0x66, 0x00, - 0x01, 0x00, 0x6C, 0x66, 0x66, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x2F, 0x66, 0x66, 0x00, 0x01, 0x00, 0xA1, 0x99, 0x9A, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x03, - 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x00, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, - 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xE8, 0x00, 0x00, 0x38, - 0x42, 0x49, 0x4D, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, - 0x1A, 0x00, 0x00, 0x00, 0x00, 0x03, 0x41, 0x00, 0x00, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, - 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x06, 0x00, 0x63, 0x00, 0x6C, 0x00, - 0x6F, 0x00, 0x75, 0x00, 0x64, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x01, 0xF4, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x75, - 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6F, - 0x75, 0x6E, 0x64, 0x73, 0x4F, 0x62, 0x6A, 0x63, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6F, 0x70, 0x20, 0x6C, 0x6F, - 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x65, - 0x66, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x42, 0x74, 0x6F, 0x6D, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, - 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6C, 0x6F, - 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x06, 0x73, 0x6C, - 0x69, 0x63, 0x65, 0x73, 0x56, 0x6C, 0x4C, 0x73, 0x00, 0x00, 0x00, 0x01, - 0x4F, 0x62, 0x6A, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x00, 0x00, 0x07, 0x73, 0x6C, 0x69, 0x63, 0x65, 0x49, 0x44, 0x6C, 0x6F, - 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x67, 0x72, - 0x6F, 0x75, 0x70, 0x49, 0x44, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x6F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x65, - 0x6E, 0x75, 0x6D, 0x00, 0x00, 0x00, 0x0C, 0x45, 0x53, 0x6C, 0x69, 0x63, - 0x65, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x0D, 0x61, - 0x75, 0x74, 0x6F, 0x47, 0x65, 0x6E, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x00, 0x00, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6E, 0x75, 0x6D, - 0x00, 0x00, 0x00, 0x0A, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x54, 0x79, - 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6D, 0x67, 0x20, 0x00, 0x00, - 0x00, 0x06, 0x62, 0x6F, 0x75, 0x6E, 0x64, 0x73, 0x4F, 0x62, 0x6A, 0x63, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, - 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6F, - 0x70, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x4C, 0x65, 0x66, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6F, 0x6D, 0x6C, 0x6F, - 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, - 0x68, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, - 0x00, 0x03, 0x75, 0x72, 0x6C, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x75, 0x6C, 0x6C, 0x54, - 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x4D, 0x73, 0x67, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x61, 0x6C, 0x74, 0x54, 0x61, - 0x67, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0E, 0x63, 0x65, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x49, - 0x73, 0x48, 0x54, 0x4D, 0x4C, 0x62, 0x6F, 0x6F, 0x6C, 0x01, 0x00, 0x00, - 0x00, 0x08, 0x63, 0x65, 0x6C, 0x6C, 0x54, 0x65, 0x78, 0x74, 0x54, 0x45, - 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x68, 0x6F, 0x72, 0x7A, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x65, 0x6E, 0x75, - 0x6D, 0x00, 0x00, 0x00, 0x0F, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x48, - 0x6F, 0x72, 0x7A, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x00, 0x00, 0x00, 0x07, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x00, 0x09, 0x76, - 0x65, 0x72, 0x74, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x65, 0x6E, 0x75, 0x6D, - 0x00, 0x00, 0x00, 0x0F, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x56, 0x65, - 0x72, 0x74, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x00, 0x00, 0x00, 0x07, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x00, 0x0B, 0x62, 0x67, - 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6E, 0x75, - 0x6D, 0x00, 0x00, 0x00, 0x11, 0x45, 0x53, 0x6C, 0x69, 0x63, 0x65, 0x42, - 0x47, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, - 0x00, 0x00, 0x4E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x09, 0x74, 0x6F, - 0x70, 0x4F, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x6C, 0x65, 0x66, 0x74, 0x4F, - 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0C, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0x4F, - 0x75, 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0B, 0x72, 0x69, 0x67, 0x68, 0x74, 0x4F, 0x75, - 0x74, 0x73, 0x65, 0x74, 0x6C, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4D, 0x04, - 0x0C, 0x00, 0x00, 0x00, 0x00, 0x06, 0xF1, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, - 0x00, 0xC0, 0x00, 0x00, 0x00, 0x06, 0xD5, 0x00, 0x18, 0x00, 0x01, 0xFF, - 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, - 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xED, 0x00, 0x0C, 0x41, - 0x64, 0x6F, 0x62, 0x65, 0x5F, 0x43, 0x4D, 0x00, 0x02, 0xFF, 0xEE, 0x00, - 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, - 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, 0x08, - 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, 0x0C, - 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, 0x0D, - 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, 0x0E, - 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0x80, - 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDD, - 0x00, 0x04, 0x00, 0x08, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, 0x05, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, - 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, - 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, 0x01, - 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, - 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, 0x23, - 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, 0x07, - 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, 0xB2, - 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, 0x17, - 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, - 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, - 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, - 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, - 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, - 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, - 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, - 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, 0x52, - 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, - 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, 0x26, - 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, 0x36, - 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, 0x46, - 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, 0xC5, - 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, - 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, - 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xF4, 0x22, 0x09, 0x44, 0xAC, 0x18, 0xD1, 0x40, 0x14, - 0x7A, 0x84, 0x85, 0x29, 0x61, 0x0C, 0x60, 0xF8, 0x28, 0xB8, 0x4A, 0xB0, - 0x5A, 0x10, 0x8B, 0x7D, 0xDE, 0x09, 0x02, 0x92, 0x1A, 0xEE, 0x08, 0x64, - 0x42, 0xB2, 0xF0, 0x10, 0x6C, 0x6C, 0x09, 0x1A, 0xA2, 0x0A, 0xD2, 0x1A, - 0xEE, 0x74, 0x21, 0xEE, 0x51, 0xB9, 0xE4, 0x18, 0x00, 0x94, 0x22, 0xF7, - 0x07, 0x42, 0x78, 0x0B, 0x09, 0x4F, 0x29, 0xA5, 0x44, 0x6E, 0x70, 0xD0, - 0x29, 0x32, 0xB7, 0x1E, 0x78, 0x49, 0x4A, 0xE5, 0x49, 0xA0, 0xA9, 0x06, - 0x34, 0x09, 0xF0, 0x4C, 0xDE, 0x50, 0x4D, 0x32, 0x68, 0x46, 0x63, 0x47, - 0x7D, 0x14, 0x1A, 0xE8, 0x10, 0x8A, 0xD3, 0x25, 0x02, 0x90, 0xCB, 0x60, - 0x09, 0xE5, 0x46, 0x4F, 0x8A, 0x52, 0x7B, 0xA0, 0x9B, 0x7F, 0xFF, 0xD0, - 0xF4, 0x26, 0xB4, 0xF7, 0x56, 0x2B, 0x8F, 0x04, 0x1D, 0xE0, 0xF0, 0x21, - 0x15, 0xA6, 0x02, 0x98, 0xB0, 0x84, 0xC3, 0x85, 0x02, 0x07, 0x25, 0x20, - 0xFD, 0x14, 0x6C, 0x70, 0x23, 0x44, 0xDA, 0x4A, 0x2B, 0x1D, 0xAA, 0x03, - 0xAC, 0x04, 0xC1, 0x45, 0xB1, 0x84, 0x83, 0xE0, 0xAA, 0xB9, 0xAE, 0x13, - 0xF9, 0x53, 0x82, 0xD2, 0x56, 0x24, 0xB9, 0xDA, 0x08, 0x08, 0xAD, 0xC6, - 0x6C, 0x4B, 0xBE, 0xF5, 0x0A, 0xD8, 0x49, 0x93, 0xC0, 0xE0, 0x23, 0x4F, - 0xB7, 0x50, 0x89, 0x40, 0x44, 0xF0, 0xD6, 0x98, 0x89, 0xF3, 0x50, 0xB2, - 0xC0, 0xC0, 0x48, 0x10, 0xA5, 0x6B, 0xD8, 0xC3, 0x3A, 0x94, 0x17, 0x58, - 0xCB, 0x3E, 0x7D, 0x91, 0x08, 0x25, 0x1F, 0xAE, 0x1C, 0x76, 0xEB, 0x25, - 0x11, 0xB3, 0xA4, 0x4A, 0x42, 0x80, 0x06, 0xE5, 0x30, 0xD8, 0x8D, 0x11, - 0x40, 0xBE, 0xA9, 0x2B, 0x73, 0x46, 0x84, 0x4C, 0xA2, 0xFB, 0x47, 0x65, - 0x0A, 0xF9, 0x82, 0x34, 0x45, 0x21, 0xA1, 0x34, 0xAE, 0x0C, 0x80, 0xDD, - 0xC0, 0x48, 0x80, 0x07, 0x1A, 0xA8, 0xB2, 0xC6, 0x8E, 0xFA, 0x29, 0x7A, - 0x80, 0xA0, 0x97, 0xFF, 0xD1, 0xF4, 0x1A, 0xCC, 0x8D, 0x51, 0x37, 0x82, - 0x15, 0x2A, 0xAE, 0x9E, 0x55, 0x86, 0x49, 0x20, 0x8E, 0x14, 0xE4, 0x35, - 0xC1, 0x6C, 0x86, 0xCB, 0x7C, 0xD0, 0x5C, 0xE8, 0x3A, 0xA2, 0x87, 0x10, - 0x39, 0x55, 0xAD, 0x81, 0x24, 0x94, 0x02, 0x4B, 0x37, 0x5B, 0xB4, 0x6B, - 0xAC, 0xA1, 0xB5, 0xBB, 0xFE, 0x03, 0x55, 0x16, 0x89, 0x1A, 0xF7, 0xE1, - 0x11, 0xB0, 0xC6, 0xA2, 0x8B, 0x58, 0x80, 0x38, 0xF6, 0xA8, 0x97, 0x1D, - 0xB3, 0xBA, 0x0F, 0x82, 0x4E, 0xD7, 0xC8, 0x1E, 0xC9, 0xBD, 0x22, 0x62, - 0x52, 0x55, 0xA2, 0xB2, 0xB9, 0x1A, 0x19, 0x43, 0x2C, 0xF2, 0xD5, 0x5D, - 0x35, 0xD7, 0xB7, 0x68, 0x10, 0x50, 0x36, 0x41, 0x23, 0xF1, 0x4E, 0x05, - 0x04, 0x2E, 0xC7, 0x43, 0x7C, 0xD2, 0x0E, 0x6C, 0x68, 0xA0, 0x46, 0xC9, - 0xF1, 0x48, 0x81, 0x00, 0xFD, 0xE9, 0x52, 0x2D, 0x6F, 0x59, 0xD3, 0x00, - 0xC2, 0x9F, 0xAA, 0xF8, 0xFA, 0x48, 0x24, 0x7B, 0x92, 0x6D, 0x80, 0x18, - 0x76, 0x90, 0x95, 0x22, 0xD9, 0xFA, 0x86, 0x79, 0xD4, 0x27, 0x6B, 0xDC, - 0x4F, 0x2A, 0xBB, 0xAD, 0x6C, 0x94, 0x99, 0x76, 0xB2, 0x8D, 0x2A, 0xDF, - 0xFF, 0xD2, 0xED, 0x68, 0x68, 0x8F, 0x35, 0x66, 0xB7, 0x96, 0x13, 0xAE, - 0x8A, 0xB3, 0x76, 0x31, 0x93, 0x30, 0x67, 0x84, 0xDE, 0xA8, 0x2E, 0xD4, - 0xAB, 0x24, 0x5B, 0x50, 0x1A, 0x6F, 0x7A, 0xA1, 0xDD, 0xE5, 0x02, 0xE7, - 0x01, 0xA3, 0x8F, 0xC9, 0x53, 0xB3, 0x20, 0xB5, 0xE4, 0xB7, 0x8F, 0x04, - 0x3F, 0x5D, 0xD6, 0x76, 0x44, 0x41, 0x46, 0x6D, 0xC6, 0xD9, 0xAE, 0x9C, - 0x22, 0x87, 0xB7, 0xBF, 0x2A, 0xA3, 0x09, 0xDB, 0x10, 0x3F, 0x8A, 0x98, - 0x7B, 0x82, 0x44, 0x28, 0x49, 0xB6, 0x4B, 0x42, 0x94, 0x83, 0x0A, 0xA8, - 0xB0, 0x11, 0x12, 0x65, 0x19, 0x91, 0x09, 0xB4, 0xB8, 0x16, 0x76, 0x4F, - 0xE6, 0xEA, 0x7B, 0x21, 0x7B, 0x86, 0xAE, 0xD3, 0xE2, 0xA7, 0xEA, 0x11, - 0x12, 0x9A, 0xDB, 0x18, 0x5B, 0xA9, 0x94, 0x94, 0x85, 0xF1, 0x3E, 0xEE, - 0xE8, 0x86, 0xB9, 0x60, 0x11, 0x03, 0xC5, 0x0C, 0x34, 0x1D, 0x47, 0x6E, - 0xC5, 0x11, 0xB6, 0xB8, 0x0D, 0x46, 0x83, 0x84, 0x4A, 0x02, 0x27, 0x8F, - 0x4C, 0x6B, 0xCA, 0xA9, 0x6D, 0x85, 0xDF, 0x04, 0x7B, 0x8E, 0xF3, 0xA6, - 0x84, 0x94, 0xDE, 0x80, 0x03, 0xDC, 0x7E, 0x49, 0xC1, 0x69, 0xD5, 0xA2, - 0x5E, 0x67, 0x5F, 0xBD, 0x16, 0xA0, 0x4E, 0xBF, 0x72, 0xB4, 0xCC, 0x6A, - 0xCB, 0x74, 0x4E, 0x68, 0xD9, 0x03, 0x90, 0x8F, 0x10, 0x5B, 0xC2, 0x5F, - 0xFF, 0xD3, 0xEA, 0xCD, 0xA7, 0xE8, 0x84, 0xDB, 0xFC, 0x79, 0x4F, 0xE9, - 0x38, 0xA7, 0xF4, 0x65, 0x5C, 0xD1, 0xA1, 0xAA, 0x33, 0xA9, 0x48, 0x13, - 0xD9, 0x17, 0xD1, 0x69, 0xD4, 0x9D, 0x14, 0x0E, 0x86, 0x3C, 0x38, 0x45, - 0x54, 0xB7, 0xA8, 0x41, 0xD3, 0x94, 0x41, 0x69, 0xEE, 0x50, 0x5C, 0xD2, - 0x99, 0xAC, 0x74, 0xF9, 0x25, 0xA2, 0xAC, 0xB6, 0x1B, 0x71, 0x07, 0x8F, - 0x9A, 0x28, 0xB1, 0xC6, 0x35, 0x55, 0x64, 0x8E, 0x79, 0x44, 0x63, 0x87, - 0x32, 0x81, 0x09, 0x05, 0x3B, 0x9C, 0xE3, 0xAC, 0xA6, 0x13, 0x3A, 0x73, - 0xE2, 0xA2, 0x1C, 0x0F, 0x74, 0x4A, 0xE0, 0x90, 0x0F, 0x74, 0x17, 0x32, - 0x60, 0x23, 0xCD, 0x4C, 0x26, 0x88, 0x30, 0x91, 0x29, 0xA9, 0x46, 0xE6, - 0x80, 0xE9, 0x8E, 0x39, 0x51, 0x71, 0xDD, 0xA7, 0x08, 0xA5, 0x30, 0x69, - 0x3C, 0x04, 0x6D, 0x0C, 0x01, 0xD9, 0xC7, 0x3E, 0x2A, 0x0F, 0x79, 0x8E, - 0x54, 0x9E, 0xD2, 0x0A, 0x0B, 0xDA, 0x4A, 0x21, 0x05, 0xFF, 0xD4, 0xEC, - 0xC3, 0x44, 0x6A, 0x96, 0xE0, 0x3C, 0xD5, 0x6F, 0xB4, 0x87, 0x1D, 0x24, - 0xA2, 0x56, 0x4B, 0xCC, 0x42, 0xB7, 0x4D, 0x3B, 0x1D, 0x12, 0x68, 0x75, - 0xF0, 0x50, 0x73, 0x9B, 0xD8, 0x29, 0xBD, 0x9B, 0x47, 0x28, 0x71, 0xDD, - 0x20, 0xA2, 0xC6, 0x0F, 0x60, 0x9F, 0x67, 0x92, 0x93, 0x66, 0x51, 0x01, - 0x8E, 0x44, 0xA5, 0x6A, 0xA4, 0x05, 0x93, 0xCA, 0x67, 0x6D, 0x6A, 0x23, - 0xCE, 0xBA, 0x28, 0x47, 0xF2, 0x44, 0xA2, 0x82, 0x15, 0x59, 0x9F, 0x29, - 0xF1, 0x56, 0x2B, 0x60, 0xE4, 0x99, 0xF2, 0x55, 0xB7, 0x86, 0xC4, 0x85, - 0x2F, 0xB4, 0xBF, 0x81, 0xF8, 0xA0, 0x6D, 0x40, 0x80, 0xDB, 0x0E, 0x05, - 0x22, 0x55, 0x56, 0xD9, 0xAC, 0xCA, 0x28, 0xB0, 0x21, 0x4B, 0xAD, 0x28, - 0x1B, 0xB8, 0x45, 0xAD, 0xA5, 0xBC, 0xF7, 0x55, 0x3E, 0xD0, 0x1A, 0xA4, - 0xCC, 0xC6, 0x9D, 0x0F, 0x28, 0x10, 0x54, 0x08, 0x4D, 0x68, 0x07, 0x8D, - 0x4A, 0x09, 0x69, 0xF0, 0x4E, 0xEB, 0xC7, 0x68, 0x3E, 0x68, 0x67, 0x29, - 0xAD, 0x69, 0x9F, 0x92, 0x20, 0x15, 0x12, 0x1F, 0xFF, 0xD5, 0xEB, 0x46, - 0x33, 0x87, 0x01, 0x3F, 0xA7, 0x6B, 0x75, 0x06, 0x07, 0xE2, 0xAD, 0x8D, - 0xC3, 0x8E, 0xE9, 0xB6, 0x13, 0xCA, 0xB5, 0xC4, 0xD4, 0xE1, 0x0D, 0x33, - 0xEA, 0x18, 0x92, 0xA5, 0xC0, 0x8E, 0xEA, 0xDF, 0xA2, 0xD3, 0xD9, 0x2F, - 0x48, 0x01, 0xC2, 0x5C, 0x4A, 0xE1, 0x2D, 0x51, 0x1F, 0x34, 0xE6, 0xC0, - 0x02, 0x31, 0xAC, 0x14, 0x3B, 0x18, 0x47, 0x01, 0x2B, 0x0A, 0xA2, 0x87, - 0x79, 0x9D, 0x54, 0x1D, 0x6F, 0x75, 0x37, 0x53, 0x61, 0xD6, 0x14, 0x0D, - 0x64, 0x09, 0x21, 0x3B, 0x45, 0xA6, 0xD1, 0xBD, 0xFB, 0xB5, 0x01, 0x38, - 0x32, 0x3F, 0x82, 0x44, 0x38, 0xE8, 0x02, 0x23, 0x2B, 0x9F, 0x2F, 0x8A, - 0x56, 0x8A, 0x60, 0x1D, 0x1A, 0xA4, 0x6D, 0x8F, 0x8A, 0x28, 0xA9, 0xDD, - 0xB5, 0x1F, 0x82, 0x4E, 0xA8, 0x1D, 0x74, 0x94, 0xAC, 0x2A, 0x8A, 0x22, - 0xF9, 0x52, 0x68, 0xF2, 0x92, 0xA4, 0x6B, 0x20, 0x79, 0xF9, 0x26, 0x1B, - 0x87, 0x23, 0x44, 0xAD, 0x34, 0xC1, 0xAC, 0x71, 0x76, 0xB2, 0x8A, 0x71, - 0x89, 0xD7, 0x52, 0x54, 0x98, 0x78, 0x85, 0x65, 0x80, 0xC2, 0x06, 0x49, - 0x11, 0x0F, 0xFF, 0xD6, 0xEF, 0x9A, 0xDF, 0x15, 0x20, 0xDF, 0x24, 0xFC, - 0x15, 0x36, 0x89, 0x53, 0x5B, 0x05, 0x30, 0xDA, 0xA2, 0x5A, 0x8E, 0x5A, - 0xA0, 0x5A, 0x95, 0xA6, 0x90, 0x96, 0xC2, 0x81, 0x00, 0xA2, 0xB8, 0x28, - 0x22, 0xB5, 0x19, 0x60, 0x88, 0x84, 0x27, 0xD6, 0x22, 0x00, 0xE1, 0x58, - 0x25, 0x0C, 0xA2, 0x10, 0x40, 0x43, 0xB1, 0xA7, 0xE9, 0x69, 0x0A, 0x62, - 0xB1, 0x13, 0x32, 0x12, 0x21, 0x38, 0x24, 0x70, 0x8D, 0xA2, 0x91, 0x19, - 0x07, 0x44, 0xA1, 0xDF, 0xEC, 0x44, 0x30, 0x90, 0x84, 0xAD, 0x54, 0xC1, - 0xAD, 0x77, 0x8A, 0x23, 0x6B, 0x61, 0x3E, 0xED, 0x54, 0x80, 0x08, 0x83, - 0x6F, 0x64, 0x09, 0x48, 0x0C, 0x05, 0x4D, 0xEC, 0x11, 0x03, 0x23, 0x84, - 0xED, 0x08, 0x80, 0x21, 0x69, 0x01, 0xFF, 0xD9, 0x00, 0x38, 0x42, 0x49, - 0x4D, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6F, - 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, - 0x00, 0x74, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x13, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x62, - 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x74, - 0x00, 0x6F, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x70, 0x00, 0x20, - 0x00, 0x37, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, - 0x42, 0x49, 0x4D, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xFF, 0xE1, 0x12, 0x48, 0x68, - 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, - 0x62, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, 0x2F, 0x31, - 0x2E, 0x30, 0x2F, 0x00, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, - 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x3D, 0x27, 0xEF, 0xBB, 0xBF, - 0x27, 0x20, 0x69, 0x64, 0x3D, 0x27, 0x57, 0x35, 0x4D, 0x30, 0x4D, 0x70, - 0x43, 0x65, 0x68, 0x69, 0x48, 0x7A, 0x72, 0x65, 0x53, 0x7A, 0x4E, 0x54, - 0x63, 0x7A, 0x6B, 0x63, 0x39, 0x64, 0x27, 0x3F, 0x3E, 0x0A, 0x3C, 0x3F, - 0x61, 0x64, 0x6F, 0x62, 0x65, 0x2D, 0x78, 0x61, 0x70, 0x2D, 0x66, 0x69, - 0x6C, 0x74, 0x65, 0x72, 0x73, 0x20, 0x65, 0x73, 0x63, 0x3D, 0x22, 0x43, - 0x52, 0x22, 0x3F, 0x3E, 0x0A, 0x3C, 0x78, 0x3A, 0x78, 0x61, 0x70, 0x6D, - 0x65, 0x74, 0x61, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x3D, - 0x27, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x3A, 0x6E, 0x73, 0x3A, 0x6D, 0x65, - 0x74, 0x61, 0x2F, 0x27, 0x20, 0x78, 0x3A, 0x78, 0x61, 0x70, 0x74, 0x6B, - 0x3D, 0x27, 0x58, 0x4D, 0x50, 0x20, 0x74, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, - 0x74, 0x20, 0x32, 0x2E, 0x38, 0x2E, 0x32, 0x2D, 0x33, 0x33, 0x2C, 0x20, - 0x66, 0x72, 0x61, 0x6D, 0x65, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x31, 0x2E, - 0x35, 0x27, 0x3E, 0x0A, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, - 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x72, 0x64, 0x66, 0x3D, 0x27, - 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, - 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x31, 0x39, 0x39, 0x39, 0x2F, 0x30, - 0x32, 0x2F, 0x32, 0x32, 0x2D, 0x72, 0x64, 0x66, 0x2D, 0x73, 0x79, 0x6E, - 0x74, 0x61, 0x78, 0x2D, 0x6E, 0x73, 0x23, 0x27, 0x20, 0x78, 0x6D, 0x6C, - 0x6E, 0x73, 0x3A, 0x69, 0x58, 0x3D, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, - 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x2E, 0x63, - 0x6F, 0x6D, 0x2F, 0x69, 0x58, 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x27, 0x3E, - 0x0A, 0x0A, 0x20, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x61, 0x62, 0x6F, 0x75, - 0x74, 0x3D, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3A, 0x65, 0x35, 0x35, 0x35, - 0x61, 0x39, 0x65, 0x35, 0x2D, 0x61, 0x61, 0x38, 0x65, 0x2D, 0x31, 0x31, - 0x65, 0x31, 0x2D, 0x62, 0x30, 0x62, 0x33, 0x2D, 0x65, 0x62, 0x31, 0x39, - 0x32, 0x32, 0x62, 0x38, 0x35, 0x34, 0x30, 0x35, 0x27, 0x0A, 0x20, 0x20, - 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x61, 0x70, 0x4D, 0x4D, 0x3D, - 0x27, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, - 0x64, 0x6F, 0x62, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, - 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x6D, 0x6D, 0x2F, 0x27, 0x3E, 0x0A, 0x20, - 0x20, 0x3C, 0x78, 0x61, 0x70, 0x4D, 0x4D, 0x3A, 0x44, 0x6F, 0x63, 0x75, - 0x6D, 0x65, 0x6E, 0x74, 0x49, 0x44, 0x3E, 0x61, 0x64, 0x6F, 0x62, 0x65, - 0x3A, 0x64, 0x6F, 0x63, 0x69, 0x64, 0x3A, 0x70, 0x68, 0x6F, 0x74, 0x6F, - 0x73, 0x68, 0x6F, 0x70, 0x3A, 0x65, 0x35, 0x35, 0x35, 0x61, 0x39, 0x65, - 0x31, 0x2D, 0x61, 0x61, 0x38, 0x65, 0x2D, 0x31, 0x31, 0x65, 0x31, 0x2D, - 0x62, 0x30, 0x62, 0x33, 0x2D, 0x65, 0x62, 0x31, 0x39, 0x32, 0x32, 0x62, - 0x38, 0x35, 0x34, 0x30, 0x35, 0x3C, 0x2F, 0x78, 0x61, 0x70, 0x4D, 0x4D, - 0x3A, 0x44, 0x6F, 0x63, 0x75, 0x6D, 0x65, 0x6E, 0x74, 0x49, 0x44, 0x3E, - 0x0A, 0x20, 0x3C, 0x2F, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x3E, 0x0A, 0x0A, 0x3C, 0x2F, - 0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, 0x3E, 0x0A, 0x3C, 0x2F, 0x78, - 0x3A, 0x78, 0x61, 0x70, 0x6D, 0x65, 0x74, 0x61, 0x3E, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x0A, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, 0x74, 0x20, - 0x65, 0x6E, 0x64, 0x3D, 0x27, 0x77, 0x27, 0x3F, 0x3E, 0xFF, 0xEE, 0x00, - 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, - 0x01, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x09, 0x08, - 0x0C, 0x09, 0x09, 0x0C, 0x11, 0x0B, 0x0A, 0x0B, 0x11, 0x15, 0x0F, 0x0C, - 0x0C, 0x0F, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x01, 0x0D, - 0x0B, 0x0B, 0x0D, 0x0E, 0x0D, 0x10, 0x0E, 0x0E, 0x10, 0x14, 0x0E, 0x0E, - 0x0E, 0x14, 0x14, 0x0E, 0x0E, 0x0E, 0x0E, 0x14, 0x11, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x11, 0x11, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x11, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0xF4, 0x01, 0xF4, - 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xDD, - 0x00, 0x04, 0x00, 0x20, 0xFF, 0xC4, 0x01, 0x3F, 0x00, 0x00, 0x01, 0x05, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, - 0x0B, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, - 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0C, 0x33, 0x01, - 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, - 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xA1, 0xB1, 0x42, 0x23, - 0x24, 0x15, 0x52, 0xC1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xD1, 0x43, 0x07, - 0x25, 0x92, 0x53, 0xF0, 0xE1, 0xF1, 0x63, 0x73, 0x35, 0x16, 0xA2, 0xB2, - 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xC2, 0xA3, 0x74, 0x36, 0x17, - 0xD2, 0x55, 0xE2, 0x65, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, - 0x46, 0x27, 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, - 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, - 0xC6, 0xD6, 0xE6, 0xF6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, - 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, - 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, - 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, - 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xA1, 0xB1, 0x42, 0x23, 0xC1, 0x52, - 0xD1, 0xF0, 0x33, 0x24, 0x62, 0xE1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, - 0x63, 0x73, 0x34, 0xF1, 0x25, 0x06, 0x16, 0xA2, 0xB2, 0x83, 0x07, 0x26, - 0x35, 0xC2, 0xD2, 0x44, 0x93, 0x54, 0xA3, 0x17, 0x64, 0x45, 0x55, 0x36, - 0x74, 0x65, 0xE2, 0xF2, 0xB3, 0x84, 0xC3, 0xD3, 0x75, 0xE3, 0xF3, 0x46, - 0x94, 0xA4, 0x85, 0xB4, 0x95, 0xC4, 0xD4, 0xE4, 0xF4, 0xA5, 0xB5, 0xC5, - 0xD5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, - 0xE6, 0xF6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, - 0xC7, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00, 0xF4, 0x24, 0xC9, 0xD3, 0x15, 0x33, 0x02, 0xC5, 0x20, - 0x98, 0xA7, 0x09, 0x29, 0x90, 0x53, 0x0A, 0x01, 0x4C, 0x24, 0x96, 0x61, - 0x24, 0xC9, 0xD0, 0x4A, 0x92, 0x49, 0x24, 0x94, 0xA4, 0xD0, 0x9D, 0x24, - 0x94, 0xB2, 0x89, 0x52, 0x2A, 0x25, 0x24, 0x2C, 0x53, 0x27, 0x29, 0x8A, - 0x2A, 0x59, 0x44, 0xA9, 0x14, 0xC5, 0x24, 0x31, 0x2A, 0x25, 0x49, 0x32, - 0x28, 0x62, 0x92, 0x74, 0xC9, 0x29, 0x64, 0x92, 0x4C, 0x52, 0x52, 0xC5, - 0x32, 0x72, 0x98, 0xA2, 0x85, 0x93, 0x14, 0x89, 0x4C, 0x4A, 0x4A, 0x5D, - 0x34, 0xA6, 0x49, 0x24, 0x2F, 0x29, 0x4A, 0x69, 0x49, 0x15, 0x29, 0x24, - 0x92, 0x49, 0x4B, 0x24, 0x92, 0x49, 0x29, 0x49, 0x24, 0x92, 0x4A, 0x52, - 0x49, 0x24, 0x92, 0x94, 0x99, 0x24, 0x92, 0x52, 0x92, 0x49, 0x3A, 0x4A, - 0x5D, 0x38, 0x4C, 0x13, 0xA4, 0xA5, 0x27, 0x4C, 0x9C, 0x20, 0x95, 0xD3, - 0xA6, 0x09, 0xD2, 0x52, 0xE9, 0x24, 0x14, 0xA1, 0x25, 0x28, 0x05, 0x28, - 0x48, 0x05, 0x20, 0xD4, 0x12, 0xB4, 0x25, 0x0A, 0x61, 0xA5, 0x3E, 0xD4, - 0x13, 0x4C, 0x00, 0x52, 0x4A, 0x13, 0x80, 0x92, 0x94, 0x02, 0x78, 0x49, - 0x24, 0x92, 0xA4, 0x92, 0x49, 0x25, 0x2C, 0x92, 0x7E, 0xE9, 0x24, 0xA7, - 0xFF, 0xD0, 0xF4, 0x24, 0x88, 0x4A, 0x12, 0x85, 0x33, 0x0B, 0x18, 0x4E, - 0x02, 0x78, 0x4E, 0x92, 0x95, 0x0A, 0x4D, 0x09, 0x94, 0xDA, 0x82, 0x97, - 0x84, 0xA1, 0x48, 0x27, 0x84, 0x92, 0xC1, 0x25, 0x22, 0x13, 0x24, 0xA5, - 0x93, 0x29, 0x42, 0x45, 0x25, 0x31, 0x4C, 0xA4, 0x99, 0x24, 0x31, 0x84, - 0xC4, 0x29, 0x24, 0x42, 0x4A, 0x60, 0x42, 0x89, 0x0A, 0x70, 0x98, 0x84, - 0x50, 0xC0, 0x85, 0x12, 0x11, 0x08, 0x51, 0x29, 0x29, 0x81, 0x4C, 0xA4, - 0x53, 0x22, 0x86, 0x29, 0x8A, 0x91, 0x51, 0x29, 0x29, 0x89, 0x51, 0x25, - 0x39, 0x51, 0x25, 0x14, 0x28, 0x94, 0xD2, 0x98, 0x94, 0xD2, 0x8A, 0x17, - 0x94, 0xA5, 0x46, 0x53, 0xA4, 0x85, 0xD2, 0x4D, 0x29, 0x24, 0xA5, 0xD2, - 0x4C, 0x94, 0xA4, 0x95, 0xD2, 0x4D, 0x29, 0x4A, 0x4A, 0x5D, 0x32, 0x49, - 0x12, 0x92, 0x95, 0x29, 0x26, 0x4A, 0x52, 0x52, 0xE9, 0x24, 0x9D, 0x25, - 0x29, 0x28, 0x4E, 0x13, 0xA0, 0xA5, 0x04, 0x92, 0x52, 0x09, 0x25, 0x68, - 0x4E, 0x94, 0x27, 0xDA, 0x92, 0x94, 0x13, 0xA5, 0xB4, 0xA7, 0x0D, 0x29, - 0x29, 0x41, 0x48, 0x24, 0x1A, 0x9F, 0x6A, 0x09, 0x5C, 0x15, 0x36, 0xA8, - 0x06, 0x95, 0x2D, 0x40, 0x41, 0x21, 0x20, 0x21, 0x3E, 0x8A, 0x0D, 0x53, - 0x49, 0x2B, 0x10, 0xA2, 0xA4, 0x98, 0xA4, 0xA5, 0x4A, 0x64, 0x92, 0x49, - 0x0A, 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, 0xFF, 0x00, 0xFF, - 0xD1, 0xF4, 0x24, 0xEA, 0x29, 0xC2, 0x99, 0x81, 0x92, 0x50, 0x94, 0x84, - 0x92, 0x4A, 0xE0, 0x22, 0x00, 0x86, 0x11, 0x1A, 0x50, 0x2A, 0x0C, 0x80, - 0x4F, 0x09, 0xC2, 0x94, 0x04, 0x12, 0xC2, 0x12, 0x85, 0x32, 0xD4, 0xD0, - 0x95, 0xA5, 0x86, 0xD4, 0x88, 0x52, 0x84, 0x92, 0x42, 0x32, 0x21, 0x45, - 0x11, 0xCA, 0x05, 0x15, 0x2C, 0x9F, 0x94, 0x80, 0x4F, 0x10, 0x92, 0x98, - 0x90, 0xA2, 0x42, 0x99, 0xE5, 0x31, 0x09, 0x21, 0x19, 0x0A, 0x24, 0x22, - 0x15, 0x12, 0x25, 0x15, 0x23, 0x51, 0x44, 0x2D, 0x51, 0x20, 0x84, 0x51, - 0x4C, 0x0A, 0x89, 0x52, 0x2A, 0x05, 0x24, 0x31, 0x28, 0x6E, 0x2A, 0x6E, - 0x42, 0x71, 0x44, 0x2D, 0x2A, 0x94, 0xC4, 0xA6, 0x95, 0x12, 0x8A, 0x19, - 0x4A, 0x79, 0x43, 0x94, 0xB7, 0x22, 0xA4, 0x92, 0x90, 0x2A, 0x1B, 0x93, - 0x82, 0x92, 0x99, 0xA4, 0x9A, 0x52, 0x94, 0x14, 0xBA, 0x69, 0x4C, 0x4A, - 0x69, 0x49, 0x4C, 0xE5, 0x28, 0x25, 0x44, 0x4A, 0x23, 0x02, 0x49, 0x63, - 0x05, 0x38, 0x0A, 0x65, 0xA1, 0x20, 0x12, 0xB5, 0x53, 0x18, 0x29, 0xC0, - 0x28, 0x81, 0xA9, 0xC8, 0x08, 0x5A, 0x69, 0x84, 0x42, 0x8A, 0x21, 0x1A, - 0x28, 0x10, 0x92, 0x94, 0x14, 0x82, 0x82, 0x90, 0x49, 0x4C, 0x82, 0x70, - 0x52, 0x09, 0xD2, 0x53, 0x30, 0x9E, 0x14, 0x01, 0x85, 0x2D, 0xC8, 0x25, - 0x74, 0xE1, 0x44, 0x15, 0x24, 0x92, 0xBC, 0xA4, 0x13, 0x27, 0x08, 0x29, - 0x96, 0x81, 0x2D, 0xC5, 0x32, 0x64, 0x94, 0xCB, 0x72, 0x45, 0xC1, 0x45, - 0x24, 0x95, 0x6C, 0xA5, 0x31, 0x29, 0xA5, 0x32, 0x4A, 0x5E, 0x53, 0xCA, - 0x64, 0x92, 0x52, 0xF2, 0x92, 0x49, 0x24, 0xA7, 0xFF, 0xD2, 0xF4, 0x10, - 0x9F, 0x44, 0xC9, 0xD4, 0xCC, 0x0B, 0xA7, 0x05, 0x45, 0x3A, 0x4A, 0x66, - 0x35, 0x44, 0x6B, 0x50, 0x9A, 0x8A, 0xD2, 0x50, 0x29, 0x09, 0x00, 0x52, - 0x50, 0x05, 0x48, 0x14, 0x0A, 0x57, 0x48, 0x94, 0xD2, 0x92, 0x09, 0x51, - 0x51, 0x2A, 0x69, 0x8A, 0x4A, 0x46, 0xE5, 0x15, 0x22, 0xA2, 0x9C, 0x85, - 0x04, 0xE5, 0x32, 0x49, 0x29, 0x68, 0x4C, 0x53, 0xA8, 0x94, 0x94, 0xC4, - 0xA4, 0x92, 0x74, 0x50, 0xC6, 0x54, 0x5C, 0x25, 0x48, 0x84, 0xC4, 0xA4, - 0xA4, 0x2E, 0x69, 0x43, 0x76, 0x88, 0xEE, 0xE1, 0x09, 0xC1, 0x10, 0xB4, - 0xA1, 0x79, 0x41, 0x71, 0x56, 0x0B, 0x25, 0x09, 0xD5, 0xA7, 0x05, 0xA5, - 0x0C, 0x95, 0x30, 0xA5, 0xE9, 0x78, 0x27, 0xD8, 0x95, 0xA2, 0x91, 0x38, - 0x1E, 0xC8, 0x7A, 0xCA, 0x3B, 0x99, 0xE0, 0xA1, 0xB0, 0xA2, 0x82, 0x18, - 0x82, 0xA4, 0x13, 0xB6, 0xB5, 0x31, 0x5A, 0x49, 0xA6, 0x20, 0x15, 0x2D, - 0xA5, 0x48, 0x30, 0x85, 0x30, 0xD0, 0x82, 0x69, 0x16, 0xC4, 0xE1, 0xA8, - 0xBB, 0x53, 0x6D, 0x4A, 0xD5, 0x4C, 0x43, 0x41, 0x52, 0x0D, 0x85, 0x26, - 0x85, 0x20, 0x02, 0x16, 0x96, 0x30, 0x9C, 0x00, 0xA4, 0x40, 0x4C, 0x52, - 0x52, 0xE2, 0x13, 0x14, 0xA5, 0x31, 0x28, 0x29, 0x44, 0xA8, 0x92, 0x98, - 0xB9, 0x34, 0x94, 0x54, 0xB8, 0x52, 0xF8, 0x28, 0xEA, 0x53, 0x8D, 0x12, - 0x53, 0x24, 0xF2, 0x98, 0x14, 0x81, 0x49, 0x4B, 0x82, 0xA4, 0xA2, 0x9C, - 0x20, 0x96, 0x40, 0xA9, 0x4A, 0x8A, 0x49, 0x29, 0x98, 0x52, 0x10, 0xA0, - 0x14, 0x81, 0x41, 0x2C, 0xE0, 0x28, 0x94, 0xFA, 0x26, 0x49, 0x4B, 0x24, - 0x9D, 0x24, 0x94, 0xB2, 0x65, 0x28, 0x4C, 0x8A, 0x14, 0x92, 0x78, 0x4D, - 0x09, 0x29, 0x49, 0x25, 0xA2, 0x49, 0x29, 0xFF, 0xD3, 0xF4, 0x09, 0x48, - 0x14, 0xC9, 0xC0, 0x53, 0xB5, 0xD7, 0x95, 0x21, 0xAA, 0x88, 0x53, 0x01, - 0x04, 0xB2, 0x01, 0x4C, 0x4A, 0x88, 0x85, 0x36, 0xA0, 0x96, 0x40, 0x14, - 0x82, 0x79, 0x4E, 0x82, 0x54, 0x13, 0xA6, 0x09, 0xD0, 0x4A, 0x93, 0x14, - 0xE9, 0x92, 0x53, 0x17, 0x28, 0xA9, 0x9D, 0x53, 0x42, 0x28, 0x63, 0x09, - 0x29, 0x42, 0x62, 0x8A, 0x98, 0x15, 0x12, 0xA4, 0x54, 0x0A, 0x48, 0x50, - 0x48, 0xA6, 0x9D, 0x14, 0x0B, 0x91, 0x53, 0x22, 0x54, 0x09, 0x48, 0x94, - 0xD2, 0x92, 0x16, 0x82, 0x98, 0xB4, 0x29, 0x4A, 0x8B, 0x9C, 0x11, 0x53, - 0x12, 0x04, 0x21, 0x18, 0x95, 0x37, 0xB8, 0x42, 0x14, 0xF7, 0x44, 0x2D, - 0x2C, 0xE0, 0x28, 0x9D, 0x52, 0x12, 0x54, 0xDA, 0xD4, 0x94, 0xC0, 0x34, - 0xA7, 0x35, 0xA2, 0x86, 0x29, 0x86, 0x4A, 0x16, 0x9A, 0x6B, 0x0A, 0xE1, - 0x4D, 0xAD, 0x47, 0x35, 0xC2, 0x6D, 0xA9, 0x5A, 0xA9, 0x1F, 0xA6, 0x0A, - 0x6D, 0x84, 0x22, 0xBB, 0x45, 0x02, 0xE0, 0x92, 0x98, 0xC2, 0x7D, 0xA9, - 0xE5, 0x38, 0x84, 0x94, 0xC2, 0x0A, 0x92, 0x4E, 0x50, 0x2E, 0x80, 0x92, - 0x19, 0x39, 0xC0, 0x04, 0x32, 0xE5, 0x17, 0x3D, 0x36, 0xE4, 0x69, 0x56, - 0xCE, 0x53, 0x12, 0xA3, 0xB9, 0x44, 0x90, 0x8A, 0x2D, 0x72, 0x52, 0x92, - 0x9A, 0x25, 0x38, 0x09, 0x29, 0x70, 0xE2, 0x13, 0x87, 0x25, 0xB4, 0x26, - 0x49, 0x4C, 0xC1, 0x52, 0x0A, 0x0D, 0x53, 0x08, 0x25, 0x9B, 0x42, 0x9E, - 0xD5, 0x06, 0x14, 0x50, 0xE0, 0x82, 0x43, 0x12, 0xC4, 0xB6, 0xA2, 0x03, - 0x29, 0x42, 0x09, 0xA6, 0x00, 0x27, 0x88, 0x52, 0xD1, 0x32, 0x4A, 0x5C, - 0x29, 0x6D, 0x51, 0x05, 0x4C, 0x1D, 0x12, 0x53, 0x12, 0x13, 0x15, 0x22, - 0x52, 0x84, 0x94, 0xC1, 0x3E, 0xD4, 0xF0, 0xA4, 0x21, 0x25, 0x31, 0x84, - 0xC4, 0x22, 0x40, 0x50, 0x29, 0x29, 0x8F, 0x74, 0x92, 0xEE, 0x92, 0x28, - 0x7F, 0xFF, 0xD4, 0xF4, 0x18, 0x94, 0xFB, 0x54, 0x83, 0x52, 0x2A, 0x66, - 0x0A, 0x58, 0x05, 0x24, 0xC1, 0x48, 0x24, 0xA5, 0xC2, 0x94, 0xA8, 0xA9, - 0x35, 0x04, 0xAE, 0x25, 0x48, 0x25, 0x01, 0x24, 0x92, 0xCA, 0x53, 0x85, - 0x1D, 0x14, 0x82, 0x05, 0x4B, 0xA8, 0x95, 0x24, 0xD0, 0x82, 0x58, 0xA6, - 0x52, 0x21, 0x32, 0x28, 0x59, 0x45, 0xC5, 0x39, 0x2A, 0x04, 0xA2, 0xA6, - 0x2E, 0x2A, 0x04, 0xA9, 0x15, 0x02, 0x8A, 0x16, 0x95, 0x12, 0x53, 0x98, - 0x51, 0x25, 0x24, 0x30, 0x25, 0x2D, 0xC9, 0x9C, 0x54, 0x51, 0x43, 0x30, - 0xF2, 0xA2, 0xE7, 0xA6, 0xEC, 0x98, 0xA2, 0x86, 0x26, 0x4A, 0x40, 0x15, - 0x20, 0x97, 0x09, 0x29, 0x70, 0x11, 0x58, 0xD5, 0x06, 0x19, 0x46, 0x10, - 0x81, 0x48, 0x5C, 0x31, 0x48, 0x02, 0x12, 0x0E, 0x4E, 0x5C, 0x10, 0x5C, - 0xB1, 0x1E, 0x29, 0x88, 0x09, 0x17, 0x28, 0x12, 0x52, 0x43, 0x17, 0x84, - 0x2E, 0xE8, 0x84, 0xA8, 0x10, 0x88, 0x41, 0x5B, 0x72, 0x7D, 0xC5, 0x46, - 0x14, 0xA0, 0x22, 0x86, 0x25, 0xCA, 0x2E, 0x70, 0x53, 0x20, 0x21, 0x3C, - 0x88, 0x49, 0x05, 0x13, 0xDC, 0x86, 0x5E, 0xE5, 0x23, 0x2A, 0x24, 0x27, - 0x2D, 0x56, 0xF4, 0xFB, 0xE4, 0xA8, 0x10, 0x53, 0xB4, 0x19, 0x49, 0x49, - 0xDA, 0x4C, 0x29, 0x02, 0xA0, 0xD4, 0x46, 0xE8, 0x82, 0xE0, 0xAD, 0xC7, - 0x84, 0xE0, 0x27, 0x89, 0xE1, 0x38, 0x49, 0x4A, 0x01, 0x38, 0x49, 0x21, - 0x28, 0x25, 0x90, 0x4E, 0x0A, 0x68, 0x29, 0xC0, 0x49, 0x49, 0x1A, 0x4A, - 0x91, 0x25, 0x45, 0xAA, 0x52, 0x82, 0x56, 0xDC, 0x9E, 0x54, 0x53, 0x80, - 0x92, 0x97, 0x95, 0x20, 0x54, 0x42, 0x98, 0x84, 0x12, 0xB8, 0x52, 0x01, - 0x33, 0x54, 0xF4, 0x84, 0x92, 0xC2, 0x0A, 0x68, 0x2A, 0x64, 0xA6, 0x84, - 0x94, 0xC4, 0xCA, 0x89, 0x95, 0x32, 0x13, 0x6D, 0x49, 0x0C, 0x35, 0x49, - 0x4B, 0x6A, 0x49, 0x2A, 0x9F, 0xFF, 0xD5, 0xF4, 0x60, 0x12, 0xDA, 0x12, - 0xDC, 0x9B, 0x72, 0x99, 0x85, 0x47, 0x44, 0xA5, 0x31, 0x29, 0x82, 0x48, - 0x66, 0x35, 0x44, 0x6A, 0x1B, 0x54, 0xC2, 0x09, 0x67, 0x29, 0x4A, 0x8A, - 0x52, 0x92, 0x59, 0x05, 0x20, 0x54, 0x42, 0x52, 0x82, 0x99, 0xCA, 0x69, - 0x51, 0x25, 0x29, 0x4A, 0x94, 0xC9, 0x45, 0xC9, 0xE5, 0x33, 0x92, 0x53, - 0x02, 0x54, 0x0A, 0x91, 0x4C, 0x8A, 0x18, 0x94, 0x37, 0x29, 0x95, 0x12, - 0x35, 0x45, 0x08, 0xDD, 0xC2, 0x19, 0x25, 0x19, 0xCD, 0x42, 0x70, 0x45, - 0x05, 0x84, 0xA5, 0xC2, 0x63, 0x29, 0x6B, 0x08, 0xA1, 0x44, 0x84, 0xDC, - 0xEA, 0x9A, 0x13, 0xA4, 0xA5, 0xB5, 0x09, 0xF5, 0x29, 0x19, 0x52, 0x63, - 0x67, 0x54, 0x94, 0xCA, 0xB6, 0x9E, 0x51, 0x44, 0x26, 0x1A, 0x04, 0xDB, - 0xA5, 0x04, 0xA4, 0x1C, 0x68, 0x9E, 0x09, 0x0A, 0x2D, 0x30, 0x96, 0xE2, - 0x82, 0x54, 0x41, 0x50, 0x32, 0xA5, 0xBB, 0x54, 0xFB, 0x41, 0x49, 0x48, - 0x88, 0x3D, 0x93, 0x06, 0xF8, 0xA2, 0x3B, 0x40, 0xA0, 0x4A, 0x28, 0x5F, - 0x68, 0x4C, 0x40, 0x0A, 0x32, 0x91, 0x72, 0x4A, 0x62, 0xE5, 0x02, 0x25, - 0x49, 0xCA, 0x28, 0xAD, 0x28, 0xDC, 0x02, 0x8C, 0x22, 0x91, 0x29, 0xBD, - 0x39, 0x45, 0x14, 0x8F, 0x64, 0xA4, 0x1A, 0x8C, 0x18, 0x91, 0x6A, 0x56, - 0xAA, 0x60, 0x1A, 0xA6, 0x02, 0x70, 0x13, 0x80, 0x92, 0x54, 0x04, 0x25, - 0x09, 0xCA, 0x64, 0x12, 0xC9, 0xA1, 0x11, 0xA0, 0x28, 0x37, 0x44, 0xF2, - 0x52, 0x53, 0x38, 0x0A, 0x41, 0xA8, 0x61, 0x11, 0xA5, 0x04, 0xAF, 0xB5, - 0x2D, 0xAA, 0x4D, 0x0A, 0x44, 0x20, 0x96, 0x00, 0x25, 0x0A, 0x6A, 0x25, - 0x25, 0x31, 0x21, 0x38, 0x4E, 0x92, 0x4A, 0x64, 0xD5, 0x30, 0x86, 0x0A, - 0x98, 0x29, 0x29, 0x74, 0x93, 0x12, 0x9B, 0x72, 0x49, 0x64, 0x99, 0x2D, - 0xC9, 0x89, 0x49, 0x4A, 0x84, 0x92, 0x94, 0x92, 0x43, 0xFF, 0xD6, 0xF4, - 0x1D, 0xC9, 0xE5, 0x44, 0xC2, 0x75, 0x3B, 0x5D, 0x79, 0x52, 0x01, 0x40, - 0x29, 0x04, 0x12, 0x90, 0x29, 0x05, 0x16, 0xA7, 0xD5, 0x04, 0xAF, 0x29, - 0x4A, 0x8A, 0x52, 0x92, 0x99, 0x02, 0x9E, 0x54, 0x41, 0x49, 0x25, 0x32, - 0x4A, 0x54, 0x65, 0x22, 0x52, 0x53, 0x3D, 0xCA, 0x24, 0xA8, 0x92, 0x9B, - 0x72, 0x54, 0xAB, 0x5E, 0x53, 0x26, 0x94, 0xE8, 0xA9, 0x44, 0x26, 0xDA, - 0x9E, 0x52, 0x25, 0x25, 0x31, 0x23, 0x44, 0x37, 0x34, 0x22, 0xF6, 0x42, - 0x78, 0x84, 0x90, 0x50, 0xB9, 0xA9, 0x80, 0x21, 0x12, 0x67, 0xB2, 0x73, - 0xF4, 0x74, 0x46, 0xD1, 0x48, 0x5D, 0xA7, 0x29, 0xB9, 0x0A, 0x4E, 0x32, - 0x91, 0x6E, 0x82, 0x11, 0x42, 0xC0, 0x4F, 0x6E, 0x11, 0x18, 0x21, 0x30, - 0x02, 0x25, 0x4C, 0x44, 0x20, 0x95, 0x41, 0x51, 0x1F, 0x49, 0x14, 0x44, - 0x6A, 0x91, 0x0D, 0x41, 0x34, 0xB0, 0x88, 0xD1, 0x47, 0x50, 0x53, 0x81, - 0x1C, 0x27, 0x29, 0x29, 0x62, 0x53, 0x6F, 0x01, 0x28, 0x51, 0x73, 0x80, - 0x45, 0x0A, 0x7B, 0xA5, 0x57, 0x71, 0x32, 0xA6, 0x4A, 0x83, 0xA0, 0xA2, - 0x10, 0x58, 0x7A, 0x84, 0x26, 0x0F, 0x32, 0x99, 0xDC, 0xA6, 0x82, 0x78, - 0x45, 0x6D, 0xA4, 0xF5, 0x12, 0xDE, 0x10, 0xA0, 0x85, 0x20, 0x95, 0x2A, - 0xD2, 0xB4, 0xCA, 0x20, 0x08, 0x0C, 0x3A, 0xA3, 0x02, 0x91, 0x48, 0x65, - 0x09, 0x88, 0x53, 0x6C, 0x14, 0xE5, 0x92, 0x82, 0x50, 0x93, 0xAA, 0x70, - 0x14, 0x8D, 0x71, 0xAA, 0x5A, 0x24, 0xA5, 0x94, 0x60, 0xA2, 0x42, 0x68, - 0x49, 0x4B, 0x34, 0x15, 0x28, 0x48, 0x0D, 0x54, 0xB4, 0x49, 0x4B, 0x34, - 0x22, 0x00, 0xA2, 0x21, 0x48, 0x10, 0x82, 0x43, 0x36, 0xA9, 0x97, 0x04, - 0x30, 0x42, 0x79, 0x94, 0x12, 0xBC, 0xA8, 0x92, 0x9E, 0x53, 0x14, 0x94, - 0xC7, 0x74, 0x24, 0x5E, 0x53, 0x39, 0x42, 0x75, 0x45, 0x09, 0x1A, 0xE5, - 0x20, 0xF4, 0x12, 0xE4, 0xB7, 0x25, 0x4A, 0xB4, 0xDB, 0xA5, 0x29, 0x43, - 0x0E, 0x4F, 0xBD, 0x2A, 0x55, 0xA4, 0x94, 0xA5, 0x0F, 0x78, 0x4B, 0x70, - 0x49, 0x56, 0x93, 0x71, 0x49, 0x43, 0x70, 0x49, 0x25, 0x3F, 0xFF, 0xD7, - 0xEF, 0x9A, 0x88, 0x10, 0x5A, 0x51, 0x01, 0x53, 0x96, 0xB8, 0x66, 0x21, - 0x38, 0x4C, 0x9C, 0x20, 0x96, 0x63, 0x84, 0xE0, 0xA1, 0xEE, 0x52, 0x05, - 0x24, 0xA8, 0xA4, 0x98, 0x94, 0x92, 0x43, 0x20, 0xA4, 0x02, 0x66, 0xA9, - 0x84, 0x12, 0xC6, 0x14, 0x5C, 0x88, 0x50, 0xDC, 0x92, 0x98, 0x95, 0x14, - 0x89, 0x4D, 0x28, 0xA1, 0x94, 0xA5, 0x2A, 0x32, 0x94, 0xA4, 0xA6, 0x52, - 0x94, 0xA8, 0xA4, 0x4A, 0x4A, 0x67, 0x28, 0x4F, 0xE7, 0x54, 0x8B, 0xA1, - 0x45, 0xCE, 0x25, 0x25, 0x5A, 0xA5, 0x24, 0xC1, 0x2E, 0x51, 0x42, 0xC1, - 0xB3, 0xAA, 0x90, 0xAE, 0x75, 0x4E, 0x01, 0x52, 0x69, 0x09, 0x29, 0x89, - 0x61, 0xE1, 0x37, 0x05, 0x49, 0xC5, 0x30, 0x49, 0x2B, 0x13, 0x01, 0x29, - 0x48, 0xB4, 0x95, 0x12, 0x08, 0x49, 0x0C, 0xF7, 0x42, 0x89, 0x77, 0x82, - 0x60, 0x09, 0x4E, 0x6B, 0x29, 0x29, 0x8E, 0xF3, 0x08, 0x66, 0x49, 0xD5, - 0x4C, 0x88, 0xD1, 0x3E, 0xD0, 0x42, 0x28, 0x44, 0x65, 0x41, 0xC0, 0xA3, - 0x16, 0xE8, 0xA0, 0x44, 0xE8, 0x92, 0x0B, 0x5E, 0x25, 0x4C, 0x08, 0x4E, - 0x5A, 0x25, 0x2E, 0xE8, 0xA1, 0x50, 0x98, 0x80, 0xA7, 0x1A, 0x26, 0x21, - 0x25, 0x31, 0x68, 0x84, 0x46, 0x92, 0x86, 0x7C, 0x94, 0xDB, 0x21, 0x24, - 0x84, 0xC0, 0xE8, 0xA6, 0x0A, 0x1B, 0x22, 0x75, 0x53, 0x25, 0x04, 0xA8, - 0x92, 0x74, 0x51, 0x80, 0x91, 0x29, 0x83, 0x92, 0x53, 0x20, 0x14, 0xA0, - 0x42, 0x8C, 0xA5, 0x29, 0x29, 0x62, 0x96, 0xE0, 0x13, 0x39, 0xC8, 0x72, - 0x51, 0x45, 0xA5, 0x2E, 0x4D, 0xBD, 0x0C, 0x98, 0x4D, 0x32, 0x95, 0x2A, - 0xD2, 0x7A, 0xA5, 0x48, 0x5A, 0x84, 0x9C, 0x25, 0x4A, 0xB4, 0x9E, 0xA9, - 0x4B, 0xD5, 0x50, 0x48, 0xA5, 0x4A, 0xB6, 0x46, 0xC4, 0xDB, 0x94, 0x0A, - 0x64, 0xA9, 0x56, 0xCC, 0x94, 0xD2, 0x53, 0x4A, 0x52, 0x92, 0x99, 0x6F, - 0x29, 0xB7, 0x15, 0x19, 0x4D, 0x32, 0x92, 0x2D, 0x9E, 0xE2, 0xA4, 0x1C, - 0x54, 0x02, 0x90, 0x49, 0x36, 0xCE, 0x74, 0x49, 0x46, 0x74, 0x49, 0x04, - 0xBF, 0xFF, 0xD0, 0xEE, 0xDA, 0x88, 0x10, 0xC1, 0x53, 0x05, 0x58, 0x6B, - 0x33, 0x05, 0x3C, 0xA8, 0x82, 0x94, 0xA0, 0x96, 0x52, 0x9C, 0x15, 0x09, - 0x4A, 0x52, 0x55, 0xB3, 0x94, 0xE0, 0xA1, 0xCA, 0x93, 0x42, 0x4A, 0x4C, - 0xD5, 0x30, 0x14, 0x18, 0x54, 0xC1, 0x4D, 0x2B, 0x94, 0x42, 0x83, 0x82, - 0x20, 0x51, 0x74, 0x24, 0xA4, 0x04, 0x28, 0x95, 0x27, 0x94, 0x2D, 0xC9, - 0xCB, 0x4B, 0x29, 0x48, 0x15, 0x09, 0x4B, 0x74, 0x24, 0xA4, 0xB2, 0xA0, - 0xE2, 0xA3, 0xBE, 0x52, 0x71, 0x94, 0xA9, 0x56, 0xB1, 0x71, 0x51, 0x94, - 0xD0, 0x65, 0x39, 0x28, 0xA1, 0x98, 0x3A, 0x29, 0x35, 0xB2, 0x14, 0x1A, - 0x8A, 0xDD, 0x10, 0x29, 0x0C, 0x83, 0x40, 0x0A, 0x24, 0x29, 0x82, 0x3B, - 0xA8, 0xBB, 0x94, 0x12, 0xB4, 0x28, 0xE8, 0x91, 0x72, 0x89, 0xE5, 0x14, - 0x32, 0x04, 0x05, 0x07, 0x9D, 0x52, 0x71, 0x51, 0x99, 0x4A, 0x91, 0x6C, - 0xDA, 0xEF, 0x15, 0x22, 0xFD, 0x10, 0x8A, 0x89, 0x72, 0x34, 0xAB, 0x66, - 0xE2, 0x0A, 0x40, 0x00, 0x84, 0x5C, 0x65, 0x3B, 0x49, 0x4A, 0x95, 0x69, - 0x49, 0x04, 0x28, 0x6D, 0x4E, 0x13, 0xED, 0x29, 0x29, 0x0B, 0x80, 0xEC, - 0x9B, 0x6A, 0x9B, 0x80, 0x09, 0xA5, 0x14, 0x2C, 0x98, 0xF9, 0x29, 0x25, - 0x09, 0x21, 0x18, 0x06, 0x54, 0xC0, 0x4E, 0x02, 0x53, 0x09, 0x29, 0x93, - 0x74, 0x53, 0x90, 0x50, 0x83, 0x94, 0x81, 0x42, 0x93, 0x6B, 0xB9, 0x46, - 0x40, 0x49, 0xDA, 0xA8, 0xC2, 0x2A, 0x48, 0x1C, 0x0A, 0x45, 0x0D, 0x3C, - 0x94, 0x95, 0x6A, 0x72, 0x8C, 0xA7, 0x95, 0x12, 0x92, 0x16, 0x71, 0x4C, - 0x0A, 0x63, 0xCA, 0x78, 0x45, 0x0C, 0x81, 0x52, 0x0A, 0x01, 0x48, 0x24, - 0x96, 0x52, 0x91, 0x4C, 0x9C, 0x20, 0xA5, 0xB5, 0x51, 0x28, 0x88, 0x6F, - 0xE5, 0x25, 0x2C, 0x0A, 0x97, 0x65, 0x0E, 0x13, 0xCA, 0x2A, 0x5D, 0x20, - 0x9A, 0x52, 0x94, 0x94, 0xC8, 0x15, 0x20, 0x50, 0xE4, 0xCA, 0x9B, 0x4A, - 0x4A, 0x66, 0x92, 0x7E, 0xC9, 0x20, 0x97, 0xFF, 0xD1, 0xEE, 0x03, 0x94, - 0x83, 0x90, 0x03, 0x94, 0xC3, 0x95, 0x9A, 0x6A, 0x02, 0x9C, 0x15, 0x20, - 0x84, 0xD2, 0xA6, 0x1C, 0x82, 0xE6, 0x49, 0xA5, 0x2E, 0x52, 0x84, 0x94, - 0xC9, 0xA5, 0x11, 0xA5, 0x09, 0xA1, 0x15, 0x81, 0x02, 0x90, 0xC8, 0x15, - 0x30, 0x54, 0x21, 0x3E, 0xA1, 0x05, 0xCC, 0xCB, 0xA0, 0x28, 0x39, 0xC9, - 0x6A, 0x98, 0x8D, 0x12, 0x52, 0x27, 0x21, 0x98, 0x45, 0x70, 0x95, 0x02, - 0xCD, 0x51, 0x5A, 0x58, 0xC4, 0x85, 0x12, 0xA6, 0x44, 0x21, 0xB9, 0x10, - 0x85, 0x82, 0x9B, 0x44, 0xA1, 0xB6, 0x65, 0x10, 0x24, 0xA5, 0xCB, 0x61, - 0x46, 0x24, 0xA9, 0x1E, 0x13, 0x00, 0x92, 0x97, 0x68, 0xF1, 0x46, 0x6C, - 0x14, 0x36, 0x89, 0x53, 0x80, 0x10, 0x5C, 0x19, 0x18, 0x0A, 0x24, 0x4F, - 0x09, 0x89, 0x4C, 0x5E, 0x21, 0x05, 0x5A, 0xCE, 0x30, 0xA0, 0x5C, 0x53, - 0xF2, 0xA2, 0x9C, 0xB5, 0x6F, 0x8A, 0x8F, 0x74, 0x43, 0x0A, 0x1C, 0xA4, - 0xA5, 0xFB, 0x28, 0x16, 0xA2, 0x80, 0x13, 0x16, 0xCA, 0x4A, 0x46, 0x1A, - 0x49, 0x45, 0x15, 0x24, 0xD6, 0xC2, 0x33, 0x48, 0x29, 0x12, 0xA0, 0x18, - 0x8A, 0xCA, 0x45, 0x88, 0xA0, 0x84, 0xC5, 0x0B, 0x5D, 0x4D, 0x67, 0xB5, - 0x08, 0x85, 0x65, 0xE2, 0x4A, 0x13, 0x9B, 0x08, 0x82, 0xB4, 0x84, 0x60, - 0x27, 0x00, 0xA4, 0xA4, 0x11, 0x43, 0x12, 0x21, 0x44, 0xA2, 0x6D, 0x94, - 0xC5, 0xA9, 0x29, 0x80, 0x10, 0xA4, 0x13, 0x42, 0x49, 0x21, 0x79, 0x09, - 0x93, 0x6A, 0xA4, 0xD1, 0xE2, 0x92, 0x56, 0xDA, 0x98, 0x84, 0x4D, 0x14, - 0x1D, 0x29, 0x28, 0xAD, 0x09, 0x88, 0x09, 0xF5, 0x4C, 0xED, 0x02, 0x48, - 0x62, 0x40, 0x4A, 0x14, 0x75, 0x52, 0x01, 0x14, 0x2B, 0xB2, 0x70, 0x53, - 0x14, 0xD2, 0x92, 0x52, 0x4A, 0x90, 0x84, 0x20, 0x51, 0x1A, 0x82, 0x82, - 0xEE, 0x43, 0x71, 0x52, 0x79, 0x43, 0x73, 0xD2, 0x0A, 0x2B, 0xCC, 0xA6, - 0x85, 0x02, 0xEF, 0x05, 0x20, 0xF0, 0x8A, 0x14, 0x4C, 0x26, 0xDC, 0x99, - 0xC5, 0x46, 0x51, 0xA5, 0x5A, 0x50, 0x42, 0x93, 0x4A, 0x0B, 0x4A, 0x90, - 0x30, 0x85, 0x2A, 0xD3, 0xEE, 0xD1, 0x24, 0x3D, 0xC1, 0x24, 0xA9, 0x36, - 0xFF, 0x00, 0xFF, 0xD2, 0xEB, 0xC1, 0x45, 0x08, 0x2D, 0x44, 0x0A, 0xD9, - 0x69, 0x84, 0x8D, 0x72, 0x98, 0x28, 0x60, 0x29, 0x84, 0xD4, 0x84, 0x8D, - 0x72, 0x20, 0x84, 0x10, 0x11, 0x18, 0x25, 0x02, 0xB8, 0x24, 0x01, 0x4C, - 0x04, 0x9A, 0xD4, 0xE4, 0x26, 0xAE, 0x0B, 0xA7, 0x27, 0x44, 0xC1, 0x22, - 0x42, 0x49, 0x50, 0x72, 0x62, 0x54, 0x49, 0x51, 0x94, 0x91, 0x6B, 0xA6, - 0x2E, 0x09, 0x89, 0x1E, 0x2A, 0x2E, 0xF1, 0x45, 0x16, 0xC5, 0xCE, 0x01, - 0x08, 0x9D, 0x53, 0xB8, 0xCA, 0x84, 0x90, 0x9C, 0x02, 0xD2, 0x52, 0x35, - 0x4F, 0x44, 0x30, 0x54, 0xA6, 0x50, 0x4B, 0x25, 0x28, 0x0A, 0x21, 0x3C, - 0xA4, 0xA6, 0x61, 0x31, 0x25, 0x46, 0x61, 0x29, 0x41, 0x36, 0xB9, 0x2A, - 0x30, 0x94, 0xA4, 0x25, 0x14, 0x28, 0x02, 0x91, 0x01, 0x3A, 0x62, 0x0A, - 0x4A, 0x59, 0x38, 0x6A, 0x60, 0x35, 0x44, 0x68, 0x09, 0x29, 0x6D, 0xA9, - 0x6D, 0x53, 0x51, 0x28, 0x25, 0x41, 0xA9, 0xD2, 0x09, 0xD2, 0x52, 0xC4, - 0x94, 0x83, 0x8A, 0x45, 0x45, 0x25, 0x32, 0x9D, 0x54, 0x1F, 0xC2, 0x74, - 0x8A, 0x48, 0x43, 0x09, 0xE1, 0x48, 0xC4, 0xA5, 0xCA, 0x2A, 0x58, 0x6A, - 0x91, 0x09, 0xE2, 0x13, 0x3C, 0xA4, 0xA6, 0x05, 0x46, 0x42, 0x91, 0x43, - 0x74, 0xA2, 0xB4, 0xAE, 0x4A, 0x76, 0xA8, 0x29, 0xB5, 0x25, 0x32, 0x94, - 0xC5, 0x3C, 0x26, 0x76, 0x89, 0x25, 0x64, 0xC6, 0x0A, 0x69, 0x4C, 0x8A, - 0x15, 0x09, 0xC0, 0x4A, 0x12, 0x49, 0x4B, 0x11, 0xD9, 0x41, 0xC2, 0x11, - 0x44, 0x26, 0x70, 0x05, 0x25, 0x22, 0x69, 0xD7, 0x55, 0x2D, 0xEA, 0x2E, - 0xE5, 0x40, 0xB9, 0x1A, 0x45, 0xB3, 0x2F, 0x25, 0x44, 0x95, 0x02, 0xE5, - 0x12, 0xE2, 0x95, 0x22, 0xD9, 0x94, 0x83, 0x90, 0xCB, 0xBB, 0x26, 0xDC, - 0x8D, 0x22, 0xD9, 0xB9, 0xDA, 0xE8, 0x90, 0x72, 0x82, 0x53, 0x08, 0xA2, - 0xD2, 0x87, 0x27, 0x94, 0x30, 0x54, 0x82, 0x09, 0xB4, 0x92, 0x92, 0x82, - 0x49, 0x2A, 0xDF, 0xFF, 0xD3, 0xEB, 0x9A, 0x11, 0x58, 0x14, 0x1B, 0x08, - 0xAD, 0x56, 0x8B, 0x4C, 0x32, 0x01, 0x4B, 0xB2, 0x68, 0x53, 0x6B, 0x74, - 0xD5, 0x05, 0xC1, 0x6D, 0x54, 0xD8, 0x60, 0xA8, 0xB8, 0x24, 0x01, 0x41, - 0x29, 0xDB, 0x62, 0x9E, 0xF4, 0x01, 0x3D, 0xD4, 0xC6, 0xA8, 0x52, 0x6D, - 0x26, 0xE0, 0x94, 0x94, 0x30, 0x42, 0x96, 0xE0, 0x38, 0x49, 0x36, 0xB2, - 0x62, 0x0A, 0x5B, 0xF5, 0x48, 0xBA, 0x42, 0x48, 0x60, 0x65, 0x44, 0xEE, - 0x53, 0x95, 0x12, 0x51, 0x42, 0x32, 0x13, 0x10, 0x93, 0x8A, 0x62, 0x74, - 0x45, 0x0A, 0x94, 0xA6, 0x14, 0x4B, 0x92, 0xDC, 0x92, 0x2D, 0x28, 0x72, - 0x79, 0x43, 0x69, 0x52, 0x05, 0x2A, 0x4D, 0xA4, 0x02, 0x53, 0xC2, 0x83, - 0x5C, 0xA7, 0xB9, 0x04, 0xAF, 0x01, 0x3C, 0x28, 0x87, 0x29, 0xC8, 0x84, - 0x14, 0xB7, 0x29, 0x14, 0xF2, 0xA2, 0x4A, 0x49, 0x5E, 0x13, 0xB5, 0x46, - 0x53, 0x82, 0x92, 0x19, 0x15, 0x12, 0xA5, 0xCA, 0x68, 0x49, 0x2A, 0x49, - 0x3A, 0x74, 0x94, 0xC0, 0xA6, 0x52, 0x72, 0x81, 0x45, 0x0B, 0x17, 0x28, - 0x97, 0x26, 0x71, 0x51, 0x25, 0x2A, 0x45, 0xB2, 0x05, 0x38, 0x50, 0x68, - 0x25, 0x48, 0x22, 0xA6, 0x52, 0x14, 0x0A, 0x91, 0x0A, 0x25, 0x25, 0x31, - 0x25, 0x47, 0x95, 0x3D, 0x23, 0x54, 0xD0, 0x3B, 0x24, 0x86, 0x30, 0xA6, - 0xC6, 0xA4, 0x14, 0x80, 0x29, 0x24, 0x32, 0xDA, 0x21, 0x41, 0xCD, 0x95, - 0x36, 0x92, 0x98, 0xF2, 0x82, 0x5A, 0xE5, 0xA4, 0x27, 0x1A, 0x1D, 0x51, - 0x8B, 0x74, 0x43, 0x2D, 0xD5, 0x1B, 0x5B, 0x4A, 0xD3, 0x94, 0xDA, 0x29, - 0x46, 0xD0, 0xA1, 0x29, 0x29, 0x72, 0x44, 0x28, 0x13, 0x09, 0x38, 0xA1, - 0x39, 0xC6, 0x51, 0x01, 0x04, 0xA9, 0xE4, 0x21, 0x94, 0x89, 0x51, 0x94, - 0xE5, 0xA4, 0xA8, 0xA8, 0x97, 0x29, 0x1E, 0x10, 0x89, 0x44, 0x21, 0x79, - 0x94, 0xE8, 0x61, 0xCA, 0x5B, 0x91, 0xA4, 0x32, 0x05, 0x29, 0x4C, 0x0A, - 0x48, 0x29, 0x9B, 0x51, 0x02, 0x1B, 0x25, 0x15, 0x02, 0x90, 0xAE, 0xC9, - 0x25, 0x26, 0x12, 0x49, 0x2F, 0xFF, 0xD4, 0xEC, 0x5B, 0x0A, 0x6C, 0xE5, - 0x04, 0x13, 0xCA, 0x2B, 0x78, 0x56, 0x8B, 0x4D, 0x30, 0x2A, 0x60, 0xA1, - 0x84, 0xED, 0x3E, 0x28, 0x2E, 0x64, 0xE3, 0x29, 0x4A, 0x62, 0x3C, 0x14, - 0x4B, 0x88, 0x49, 0x49, 0x03, 0x93, 0xEE, 0x84, 0x1D, 0xDA, 0xA4, 0x49, - 0x4A, 0x95, 0x69, 0x0B, 0xF5, 0x52, 0x6B, 0x87, 0x28, 0x0E, 0x72, 0x61, - 0x62, 0x54, 0xAB, 0x6C, 0xC8, 0x2A, 0x4D, 0x21, 0x00, 0x3D, 0x3E, 0xF8, - 0x08, 0x52, 0x6D, 0x33, 0xA3, 0x94, 0x17, 0xB8, 0x04, 0xCE, 0xB4, 0xA1, - 0x3D, 0xC4, 0xA2, 0x02, 0x09, 0x51, 0x7A, 0x62, 0xF4, 0x3D, 0xCA, 0x24, - 0xCA, 0x75, 0x2C, 0xB4, 0xBB, 0xA5, 0x38, 0x50, 0x68, 0x2A, 0x44, 0x14, - 0x92, 0xC8, 0x15, 0x30, 0x50, 0x81, 0x52, 0x05, 0x05, 0x24, 0x94, 0xE0, - 0xA8, 0x02, 0x9C, 0x14, 0x13, 0x6C, 0xC1, 0x44, 0x69, 0xD1, 0x09, 0x3C, - 0xA5, 0x49, 0xB4, 0xA4, 0x85, 0x12, 0xA2, 0x0A, 0x72, 0x50, 0x4A, 0xA5, - 0x38, 0x2A, 0x12, 0x90, 0x72, 0x34, 0x8B, 0x4C, 0x1C, 0x9B, 0x72, 0x80, - 0x29, 0xD0, 0x4D, 0xB3, 0x0E, 0x52, 0x94, 0x36, 0x85, 0x20, 0x50, 0x52, - 0xE5, 0x40, 0xA2, 0x26, 0x84, 0x94, 0x88, 0xD7, 0x29, 0x7A, 0x68, 0xD2, - 0x13, 0xE8, 0x8D, 0xAA, 0x90, 0x06, 0x42, 0x5A, 0x22, 0x90, 0x86, 0xF6, - 0xF8, 0x24, 0xA6, 0x24, 0xA8, 0x98, 0x2A, 0x2E, 0x4C, 0x1C, 0x42, 0x2B, - 0x6D, 0x93, 0xB8, 0x43, 0x2E, 0x85, 0x27, 0x19, 0x51, 0x21, 0x15, 0x15, - 0x35, 0xC4, 0x95, 0x66, 0xBE, 0x15, 0x76, 0x84, 0x7A, 0xA4, 0x20, 0x52, - 0x19, 0x96, 0xA8, 0x96, 0xED, 0xE5, 0x1B, 0x70, 0x08, 0x64, 0x97, 0x26, - 0xAE, 0x63, 0x1B, 0x93, 0x6D, 0x85, 0x30, 0x08, 0x3A, 0xA1, 0xD8, 0xE2, - 0x25, 0x14, 0x23, 0xB3, 0x94, 0x27, 0x10, 0x02, 0x91, 0x71, 0x28, 0x6F, - 0x4E, 0x0B, 0x09, 0x46, 0xF7, 0x6A, 0xA0, 0x49, 0xE5, 0x4C, 0x85, 0x10, - 0x13, 0x96, 0xB1, 0x25, 0x44, 0x8E, 0xE8, 0xA5, 0xA0, 0x26, 0x20, 0x94, - 0x95, 0x48, 0xA4, 0xA8, 0x22, 0x3B, 0x42, 0x96, 0xDD, 0xC3, 0x44, 0x50, - 0x84, 0xB6, 0x38, 0x4B, 0x58, 0x46, 0xD8, 0x78, 0x4F, 0xB5, 0xBB, 0x7C, - 0xD2, 0xB5, 0x52, 0x1D, 0x54, 0xD8, 0x27, 0x44, 0xFE, 0x9A, 0x9B, 0x18, - 0x67, 0x54, 0x89, 0x55, 0x2E, 0x1A, 0x42, 0x94, 0x29, 0xE8, 0x98, 0x93, - 0x28, 0x5A, 0xEA, 0x5F, 0x6E, 0x89, 0x26, 0x97, 0x24, 0x82, 0x9F, 0xFF, - 0xD5, 0xEC, 0x83, 0x77, 0x71, 0xA2, 0x9B, 0x1B, 0xA2, 0x61, 0xCC, 0x84, - 0x66, 0x01, 0xB5, 0x5A, 0x2D, 0x40, 0x11, 0xEA, 0x0A, 0x70, 0x7C, 0x14, - 0xDC, 0x04, 0x4F, 0x74, 0x2D, 0xC3, 0x80, 0x82, 0x97, 0xDE, 0x5B, 0xCA, - 0x8B, 0x9C, 0x49, 0xF2, 0x4F, 0xA1, 0xE5, 0x41, 0xCE, 0x00, 0xA2, 0x86, - 0x6D, 0xD7, 0x95, 0x28, 0x50, 0x04, 0x15, 0x30, 0x44, 0x24, 0x96, 0x2F, - 0x28, 0x45, 0xD0, 0xA4, 0xF7, 0x4A, 0x0B, 0x89, 0x44, 0x2D, 0x25, 0x2B, - 0x6C, 0x4E, 0x6D, 0x0A, 0xB6, 0xE2, 0x13, 0x7A, 0x84, 0x23, 0x48, 0xE2, - 0x6C, 0x17, 0x26, 0xDE, 0xAB, 0x9B, 0x49, 0xE5, 0x36, 0xE4, 0xA9, 0x5C, - 0x49, 0xCB, 0x82, 0x8C, 0xEA, 0xA1, 0xB9, 0x38, 0x49, 0x16, 0x9D, 0x8E, - 0x08, 0x9B, 0x82, 0xAE, 0x14, 0xDB, 0x28, 0x10, 0xB8, 0x14, 0x9A, 0x14, - 0x81, 0x50, 0xD5, 0x3C, 0xA4, 0xA6, 0x60, 0xA7, 0x94, 0x39, 0x4E, 0x0A, - 0x49, 0x49, 0xB9, 0x20, 0x50, 0xE5, 0x48, 0x14, 0x14, 0x94, 0x25, 0xAA, - 0x80, 0x2A, 0x5B, 0x90, 0x4A, 0xC4, 0x14, 0xED, 0x6F, 0x9A, 0x53, 0x29, - 0xC2, 0x4A, 0x64, 0x02, 0x9C, 0x28, 0x29, 0x83, 0x28, 0x25, 0x74, 0xDB, - 0xA1, 0x2D, 0x53, 0x77, 0x49, 0x2C, 0xC3, 0x92, 0x2E, 0x51, 0xDD, 0xA2, - 0x89, 0x72, 0x54, 0xAB, 0x48, 0x35, 0x2A, 0x53, 0x08, 0x2D, 0x72, 0x20, - 0x74, 0xA4, 0xA5, 0xCE, 0xA1, 0x40, 0x95, 0x30, 0x54, 0x1F, 0x11, 0x28, - 0x05, 0x23, 0x70, 0xEE, 0x86, 0x42, 0x99, 0x2A, 0x33, 0x29, 0xCB, 0x4B, - 0x18, 0x29, 0x44, 0x15, 0x38, 0x4C, 0xE6, 0xA2, 0xA5, 0x08, 0x44, 0x6B, - 0xC0, 0x90, 0x85, 0x09, 0x4C, 0x21, 0x4A, 0x05, 0x36, 0xE0, 0x88, 0x1C, - 0x15, 0x5D, 0xC5, 0x4D, 0xA5, 0x22, 0x12, 0x0A, 0x77, 0x38, 0x76, 0x41, - 0xB2, 0x0F, 0x74, 0xC5, 0xE5, 0x0C, 0xBB, 0x54, 0x80, 0x51, 0x2B, 0x3B, - 0xCB, 0xEF, 0x41, 0x79, 0x32, 0x8A, 0x78, 0x41, 0x70, 0x32, 0x9C, 0x16, - 0x16, 0x32, 0x65, 0x48, 0xC9, 0x09, 0x35, 0xA8, 0x81, 0xBE, 0x48, 0xA8, - 0x23, 0x0D, 0x31, 0xAA, 0x5B, 0x62, 0x04, 0x23, 0x86, 0xA7, 0x2D, 0x1F, - 0x34, 0x2D, 0x34, 0xD6, 0x75, 0x40, 0xF6, 0x4E, 0xDA, 0xA0, 0xEA, 0x8A, - 0xEE, 0x53, 0xB4, 0x18, 0x94, 0xAD, 0x14, 0xC0, 0xD6, 0x99, 0xD5, 0x77, - 0x89, 0x47, 0x00, 0x42, 0x72, 0xD3, 0xC1, 0x28, 0x5A, 0x69, 0xAB, 0xB3, - 0x58, 0x4E, 0x18, 0x40, 0x56, 0x3D, 0x30, 0x9B, 0x66, 0x88, 0xDA, 0xA9, - 0x0E, 0x91, 0xE6, 0xA5, 0x09, 0xCB, 0x61, 0x3B, 0x48, 0x8D, 0x52, 0x53, - 0x18, 0xD2, 0x23, 0x54, 0x94, 0xF4, 0x49, 0x05, 0x3F, 0xFF, 0xD6, 0xED, - 0x41, 0x0A, 0x5B, 0xFC, 0x10, 0x37, 0x6B, 0xE6, 0x9C, 0x38, 0xC2, 0xB5, - 0x4D, 0x3B, 0x66, 0xF7, 0x94, 0x30, 0xED, 0x53, 0xB9, 0xC2, 0x10, 0xC1, - 0xD5, 0x10, 0x10, 0x4B, 0x32, 0x4A, 0x89, 0x24, 0xA7, 0x26, 0x54, 0x51, - 0x43, 0x36, 0xBA, 0x13, 0x97, 0x21, 0x02, 0x9E, 0x52, 0xA5, 0x5A, 0xEE, - 0x72, 0x19, 0x76, 0xA9, 0xC9, 0x43, 0x29, 0x20, 0x95, 0x12, 0xA2, 0x4A, - 0x92, 0x68, 0x45, 0x0C, 0x21, 0x48, 0x04, 0xF0, 0xA4, 0x20, 0x23, 0x6A, - 0x5C, 0x30, 0xA9, 0x06, 0xA4, 0x1C, 0x53, 0xEE, 0x09, 0xA9, 0x64, 0xD1, - 0xE2, 0xA6, 0x21, 0x08, 0xBD, 0x20, 0xE8, 0x49, 0x36, 0x9A, 0x25, 0x34, - 0x28, 0x0B, 0x0F, 0x75, 0x20, 0xF0, 0x82, 0x6D, 0x96, 0xD2, 0x42, 0x68, - 0x52, 0x0F, 0x85, 0x12, 0xE0, 0x52, 0x52, 0x93, 0xA8, 0xCA, 0x79, 0x49, - 0x4C, 0x81, 0x4F, 0x2A, 0x20, 0xA7, 0x00, 0xA0, 0x96, 0x60, 0x92, 0xA4, - 0x09, 0x0A, 0x21, 0x4B, 0x44, 0x92, 0xC8, 0x12, 0x54, 0x9A, 0xE8, 0x43, - 0x05, 0x22, 0xE0, 0x82, 0xAD, 0x3C, 0xC8, 0x51, 0x3A, 0xA1, 0x8B, 0x14, - 0xB7, 0x21, 0x49, 0xB5, 0xCE, 0x8A, 0x24, 0x84, 0xC6, 0xC0, 0xA0, 0xE7, - 0xA3, 0x48, 0x25, 0x90, 0x76, 0xAA, 0x61, 0xD0, 0x83, 0x23, 0x94, 0xC5, - 0xE8, 0xD2, 0xAD, 0x39, 0x79, 0x50, 0x2E, 0x28, 0x7B, 0xCA, 0x40, 0x99, - 0xD5, 0x2A, 0x55, 0xB2, 0x2E, 0x4D, 0xF0, 0x48, 0x90, 0x9A, 0x42, 0x48, - 0x66, 0x09, 0x52, 0xEC, 0x86, 0x0A, 0x23, 0x50, 0x48, 0x58, 0xC4, 0x28, - 0xC2, 0x24, 0x04, 0xC9, 0x29, 0x88, 0x6E, 0xAA, 0x70, 0x98, 0x1D, 0x54, - 0xB4, 0x49, 0x41, 0x19, 0x25, 0x0E, 0x61, 0x15, 0xC1, 0x08, 0x84, 0x42, - 0x0A, 0xC5, 0xCA, 0x1A, 0x12, 0x9D, 0xC0, 0xA7, 0x6B, 0x51, 0x43, 0x26, - 0x80, 0x88, 0x1A, 0x14, 0x40, 0x52, 0xEC, 0x82, 0x42, 0xC7, 0x45, 0x1D, - 0xC9, 0xDC, 0x86, 0x52, 0x52, 0xE5, 0xC9, 0xDA, 0x44, 0xA8, 0x04, 0xFC, - 0x22, 0x84, 0xC2, 0x09, 0xF2, 0x53, 0x20, 0x46, 0x88, 0x2D, 0x28, 0x93, - 0xA1, 0x09, 0xAB, 0x97, 0x01, 0x3A, 0x8E, 0xE0, 0x02, 0x6D, 0xE9, 0x2A, - 0xD4, 0xE6, 0xA8, 0x6D, 0xD7, 0xC2, 0x14, 0x8D, 0x8A, 0x25, 0xC1, 0x14, - 0x2A, 0x04, 0xCA, 0x4A, 0x3B, 0xF5, 0x84, 0x92, 0x55, 0xBF, 0xFF, 0xD7, - 0xEB, 0xA6, 0x12, 0xDE, 0xA0, 0x5C, 0xA2, 0x4A, 0xB9, 0x4D, 0x1B, 0x66, - 0x4A, 0x8C, 0xA8, 0xEE, 0x4D, 0x24, 0xA3, 0x48, 0xB4, 0x9B, 0x92, 0x25, - 0x40, 0x12, 0x9E, 0x52, 0x53, 0x29, 0x4D, 0x29, 0xA5, 0x29, 0x49, 0x4A, - 0x29, 0x92, 0x29, 0x24, 0xA5, 0x24, 0x92, 0x49, 0x29, 0x49, 0x92, 0x29, - 0x24, 0xA5, 0xD3, 0xA8, 0xA7, 0x49, 0x4B, 0xA4, 0x9A, 0x52, 0x94, 0x94, - 0xBC, 0xA9, 0x07, 0x21, 0x92, 0x90, 0x25, 0x25, 0x5A, 0x69, 0x4E, 0x0A, - 0x10, 0x25, 0x4E, 0x50, 0x4D, 0xB3, 0x05, 0x29, 0x50, 0x94, 0x89, 0x49, - 0x56, 0xCF, 0x74, 0x29, 0x36, 0xC4, 0x09, 0x4E, 0x0A, 0x54, 0xAB, 0x6C, - 0x6F, 0x94, 0xB7, 0x94, 0x10, 0xE5, 0x20, 0x50, 0xA4, 0xDA, 0x5D, 0xE5, - 0x34, 0xA8, 0xCA, 0x79, 0x49, 0x4B, 0x82, 0x65, 0x4A, 0x4A, 0x80, 0x52, - 0x41, 0x2B, 0x19, 0x4C, 0xA4, 0x98, 0xA4, 0xA6, 0x29, 0xC2, 0x49, 0xA1, - 0x14, 0x32, 0x81, 0xE2, 0x91, 0xF0, 0x51, 0x4A, 0x12, 0x52, 0xE9, 0x00, - 0x52, 0x01, 0x4C, 0x04, 0x92, 0xA0, 0xA6, 0x14, 0x42, 0x70, 0x82, 0x99, - 0xA6, 0x4C, 0x9D, 0x04, 0xA8, 0x27, 0x4C, 0xA4, 0x12, 0x53, 0x12, 0x0A, - 0x81, 0x08, 0xA6, 0x21, 0x40, 0xA4, 0xA4, 0x4E, 0x09, 0x82, 0x99, 0x0A, - 0x31, 0xAA, 0x28, 0x66, 0x08, 0x48, 0x90, 0x54, 0x7B, 0x68, 0xA2, 0x4E, - 0xA9, 0x29, 0x99, 0x28, 0x66, 0x0F, 0x09, 0xF5, 0x29, 0x35, 0xBA, 0xA2, - 0xA6, 0x20, 0x42, 0x90, 0x6E, 0xA9, 0xF6, 0xA7, 0x02, 0x35, 0x49, 0x4B, - 0xB4, 0x01, 0xAA, 0x45, 0xDA, 0x28, 0x92, 0x12, 0x88, 0x12, 0x82, 0x94, - 0x5C, 0x90, 0x04, 0xEA, 0x99, 0x3E, 0xE8, 0x45, 0x4B, 0x3B, 0x45, 0x02, - 0x54, 0xDC, 0x54, 0x1D, 0xC2, 0x48, 0x2C, 0x65, 0x25, 0x19, 0x49, 0x14, - 0x5B, 0xFF, 0xD0, 0xEA, 0x25, 0x31, 0x94, 0xE9, 0x95, 0xD7, 0x3D, 0x88, - 0x52, 0x10, 0x98, 0x27, 0x49, 0x4B, 0xA6, 0x25, 0x24, 0x88, 0x49, 0x4A, - 0x94, 0xE0, 0xA8, 0xC2, 0x74, 0x94, 0xB9, 0x4C, 0x9D, 0x34, 0xA4, 0x95, - 0x24, 0x94, 0xA4, 0x92, 0x16, 0x4E, 0xA4, 0x04, 0xA4, 0x52, 0x4A, 0xC0, - 0x24, 0x42, 0x70, 0x91, 0x49, 0x4C, 0x13, 0x4A, 0x72, 0x99, 0x14, 0x29, - 0x48, 0x28, 0x25, 0x30, 0x92, 0x92, 0x82, 0x12, 0x94, 0x30, 0x52, 0xDC, - 0x85, 0x2A, 0xD2, 0x4A, 0x52, 0xA0, 0x0A, 0x98, 0x29, 0x25, 0x6D, 0x53, - 0xC2, 0x49, 0x24, 0xA6, 0x41, 0x48, 0x28, 0x05, 0x29, 0x41, 0x21, 0x98, - 0x2A, 0x41, 0x0E, 0x54, 0x81, 0x41, 0x2C, 0xD3, 0xCA, 0x8C, 0xA7, 0x49, - 0x4B, 0xA4, 0x53, 0x4A, 0x52, 0x82, 0x54, 0x92, 0x49, 0x22, 0xA5, 0x27, - 0x01, 0x24, 0xE1, 0x05, 0x2E, 0x02, 0x78, 0x48, 0x27, 0x09, 0x29, 0x50, - 0x9E, 0x12, 0x4E, 0x82, 0x54, 0x92, 0x74, 0x92, 0x52, 0xC9, 0xD3, 0x24, - 0x92, 0x97, 0x51, 0x21, 0x3A, 0x62, 0x92, 0x96, 0x2D, 0x4C, 0x42, 0x94, - 0x26, 0x45, 0x08, 0xCC, 0x84, 0xA1, 0x4C, 0xA8, 0xC2, 0x4A, 0x58, 0x18, - 0x4E, 0x94, 0x27, 0x84, 0x94, 0xAE, 0x12, 0x24, 0x26, 0x29, 0x8A, 0x4A, - 0x62, 0x60, 0x14, 0xE5, 0xC9, 0x26, 0x28, 0xA1, 0x69, 0x48, 0x19, 0x49, - 0x34, 0xA4, 0xA5, 0x38, 0xE8, 0xA1, 0x29, 0x12, 0xA0, 0x4E, 0xA8, 0x80, - 0x82, 0x57, 0x9D, 0x52, 0x51, 0x92, 0x92, 0x28, 0x7F, 0xFF, 0xD1, 0xEA, - 0x08, 0x48, 0x05, 0x38, 0x4D, 0x0A, 0xEB, 0x41, 0x84, 0x27, 0x85, 0x28, - 0x4E, 0x18, 0x52, 0x55, 0x31, 0x00, 0x24, 0x42, 0x28, 0x68, 0xF9, 0xA6, - 0x2D, 0x42, 0xD5, 0x48, 0xB6, 0x9F, 0x04, 0xE1, 0x88, 0xA1, 0xA9, 0xF6, - 0xA5, 0x69, 0xA4, 0x5B, 0x02, 0x62, 0xC2, 0x8C, 0x1B, 0xAA, 0x7D, 0xA0, - 0xA5, 0x6A, 0xA4, 0x02, 0xA9, 0x52, 0xF4, 0xA3, 0x94, 0x70, 0x00, 0x51, - 0x72, 0x56, 0xAA, 0x47, 0x11, 0xC2, 0x81, 0xD5, 0x49, 0xCA, 0x29, 0x20, - 0xA8, 0x24, 0x52, 0x48, 0xA2, 0xA6, 0x05, 0x32, 0x9C, 0x14, 0xC4, 0x24, - 0x86, 0x2A, 0x30, 0xA7, 0x09, 0x8A, 0x2A, 0x63, 0xC2, 0x61, 0xCA, 0x91, - 0x09, 0xC3, 0x52, 0x52, 0xE1, 0x38, 0x48, 0x35, 0x39, 0x08, 0x29, 0x70, - 0x52, 0x4D, 0x05, 0x2D, 0x52, 0x4A, 0xEA, 0x41, 0x41, 0x38, 0x29, 0x29, - 0x9A, 0x90, 0x51, 0x69, 0x52, 0x08, 0x25, 0x90, 0x49, 0x32, 0x79, 0x41, - 0x2B, 0xA7, 0x4C, 0x0A, 0x79, 0x49, 0x4A, 0x84, 0xA1, 0x34, 0xA7, 0x05, - 0x24, 0xAE, 0x14, 0x80, 0x4D, 0x29, 0xC2, 0x0A, 0x66, 0x02, 0x50, 0xA4, - 0xD4, 0xEE, 0x08, 0x25, 0x82, 0x70, 0x53, 0x24, 0x92, 0x99, 0x24, 0x99, - 0x24, 0x94, 0xA4, 0xC9, 0x14, 0xC9, 0x29, 0x74, 0x93, 0x05, 0x24, 0x94, - 0xB1, 0x4C, 0x9D, 0x32, 0x2A, 0x59, 0x28, 0x49, 0x24, 0x90, 0xA4, 0xC9, - 0xD2, 0x49, 0x4B, 0x15, 0x12, 0x14, 0x93, 0x10, 0x92, 0x98, 0x26, 0x2A, - 0x64, 0x26, 0x84, 0x54, 0xC5, 0x44, 0xA9, 0xC2, 0x89, 0x09, 0x21, 0x19, - 0x51, 0x21, 0x13, 0x6A, 0x68, 0x45, 0x14, 0xC2, 0x12, 0x52, 0x84, 0x91, - 0x45, 0x3F, 0xFF, 0xD2, 0xEB, 0xA1, 0x48, 0x34, 0x42, 0x50, 0x9C, 0x2B, - 0x76, 0xD2, 0x63, 0x0D, 0x09, 0xE5, 0x22, 0xD4, 0x82, 0x4A, 0x50, 0x09, - 0x42, 0x98, 0x88, 0x48, 0x42, 0x56, 0xAA, 0x62, 0x02, 0x44, 0x29, 0x81, - 0x29, 0xF6, 0xA1, 0x69, 0xA4, 0x62, 0x52, 0x06, 0x14, 0xA0, 0x28, 0x91, - 0x1C, 0x24, 0xA5, 0x12, 0xA2, 0x52, 0x24, 0xCA, 0x70, 0x09, 0x45, 0x0C, - 0x1C, 0x02, 0x81, 0x08, 0xA5, 0xA9, 0x06, 0x23, 0x6A, 0xA4, 0x30, 0x53, - 0xC2, 0x2C, 0x05, 0x03, 0x09, 0x5A, 0x29, 0x8C, 0x26, 0x21, 0x49, 0x24, - 0x94, 0xC6, 0x13, 0x6D, 0x53, 0x49, 0x2B, 0x45, 0x30, 0xDA, 0x9C, 0x05, - 0x28, 0x4A, 0x12, 0x55, 0x2D, 0x09, 0xE1, 0x20, 0x92, 0x49, 0x58, 0x84, - 0xCA, 0x44, 0x28, 0x90, 0x92, 0x16, 0x4E, 0xA2, 0x9C, 0x04, 0x54, 0xC8, - 0x15, 0x2D, 0xCA, 0x21, 0x24, 0x12, 0xCB, 0x72, 0x6D, 0xC9, 0x8A, 0x52, - 0x92, 0x19, 0x87, 0x29, 0x02, 0x86, 0x0A, 0x90, 0x29, 0x25, 0x94, 0xA9, - 0x05, 0x10, 0x92, 0x09, 0x66, 0x9C, 0x39, 0x40, 0x14, 0xE0, 0xC9, 0x49, - 0x49, 0xD8, 0xE5, 0x32, 0x84, 0xD4, 0x40, 0x0A, 0x69, 0x5C, 0x16, 0x29, - 0x24, 0x44, 0x24, 0x92, 0x94, 0x92, 0x50, 0x92, 0x49, 0x59, 0x34, 0x29, - 0x42, 0x64, 0x90, 0xA8, 0x49, 0x24, 0x92, 0x52, 0x93, 0x27, 0x49, 0x25, - 0x2D, 0x09, 0x27, 0x49, 0x25, 0x28, 0x09, 0x52, 0xD8, 0x9D, 0x81, 0x11, - 0x0B, 0x48, 0x08, 0xBD, 0x35, 0x12, 0xC4, 0x78, 0x43, 0x72, 0x56, 0xAA, - 0x42, 0x5A, 0x98, 0x84, 0x42, 0x14, 0x0A, 0x28, 0xA6, 0x24, 0x28, 0x90, - 0xA6, 0x98, 0x84, 0x50, 0xC0, 0x85, 0x12, 0x11, 0x21, 0x34, 0x22, 0x86, - 0x10, 0x92, 0x9C, 0x24, 0x92, 0xA9, 0xFF, 0xD3, 0xEC, 0x53, 0xB5, 0x25, - 0x20, 0xD5, 0x69, 0xA6, 0xA8, 0x05, 0x2D, 0x89, 0xE2, 0x12, 0x98, 0x49, - 0x2B, 0x6D, 0x84, 0xB4, 0x4A, 0x74, 0x4C, 0x4C, 0x24, 0xA6, 0x40, 0xA4, - 0x4A, 0x81, 0x72, 0x6D, 0xC9, 0x29, 0x92, 0x64, 0xD2, 0x9C, 0x10, 0x92, - 0x15, 0x09, 0x27, 0x91, 0xD9, 0x31, 0x29, 0x29, 0x7D, 0xAA, 0x26, 0x42, - 0x45, 0xE4, 0x28, 0x97, 0x92, 0x92, 0x94, 0x75, 0x51, 0x20, 0x26, 0x25, - 0x34, 0x94, 0x50, 0xBA, 0x62, 0x94, 0x14, 0xA1, 0x14, 0x2C, 0xA4, 0x13, - 0x42, 0x70, 0x12, 0x52, 0xE0, 0x25, 0x09, 0x27, 0x41, 0x2B, 0x42, 0x65, - 0x24, 0xA1, 0x25, 0x30, 0x84, 0x8B, 0x54, 0xE1, 0x32, 0x2A, 0xA6, 0x01, - 0x8A, 0x41, 0x8A, 0x41, 0x29, 0x4A, 0xD5, 0x4C, 0x4B, 0x53, 0x42, 0x91, - 0x4D, 0xB9, 0x24, 0x31, 0x21, 0x32, 0x44, 0xA6, 0x94, 0x90, 0xC8, 0x29, - 0x88, 0x50, 0x12, 0x78, 0x52, 0x00, 0xA4, 0x90, 0xCA, 0x53, 0xCA, 0x4D, - 0x68, 0x8D, 0x54, 0x83, 0x42, 0x09, 0xA5, 0x80, 0x44, 0x63, 0x0A, 0x76, - 0x88, 0x44, 0x05, 0x02, 0x52, 0x02, 0x80, 0x4F, 0x2A, 0x25, 0xCA, 0x32, - 0x82, 0x52, 0x13, 0x29, 0x94, 0x41, 0x4F, 0x29, 0x25, 0x74, 0x92, 0x4D, - 0x29, 0x29, 0x72, 0x99, 0x32, 0x49, 0x21, 0x49, 0x4A, 0x69, 0x4A, 0x52, - 0x52, 0xE9, 0x15, 0x20, 0x13, 0xC0, 0x49, 0x54, 0xC1, 0x48, 0x04, 0xF0, - 0x9F, 0x69, 0xEC, 0x92, 0x69, 0x41, 0x4D, 0xA9, 0x9A, 0x13, 0x80, 0x82, - 0x57, 0x3C, 0x28, 0x10, 0x89, 0x09, 0x89, 0x08, 0x29, 0x11, 0x6A, 0x19, - 0x84, 0x67, 0x4A, 0x11, 0x09, 0xC1, 0x05, 0x82, 0x62, 0xA5, 0xB4, 0xA8, - 0xC2, 0x28, 0x59, 0x24, 0xF0, 0x94, 0x14, 0x90, 0xB2, 0x49, 0xE1, 0x24, - 0x94, 0xFF, 0x00, 0xFF, 0xD4, 0xEC, 0xD3, 0xEE, 0x51, 0x71, 0x0A, 0x3B, - 0x95, 0xA6, 0xA2, 0x49, 0x51, 0x73, 0x94, 0x65, 0x34, 0xA5, 0x48, 0xB6, - 0x5B, 0x93, 0xB8, 0xCA, 0x1C, 0xA9, 0x07, 0x24, 0xA5, 0x42, 0x49, 0x12, - 0x9B, 0x59, 0x45, 0x4B, 0xEA, 0x92, 0x53, 0x1A, 0x26, 0x28, 0x29, 0x94, - 0xA6, 0x2A, 0x29, 0x4A, 0x4A, 0x51, 0x4C, 0x91, 0x29, 0xA5, 0x14, 0x29, - 0x20, 0x0A, 0x49, 0xE4, 0x22, 0xA5, 0x04, 0xE9, 0x93, 0xA0, 0xA5, 0x42, - 0x96, 0xD4, 0x82, 0x70, 0x82, 0x56, 0xDA, 0x96, 0xD5, 0x24, 0xF0, 0x52, - 0x55, 0x30, 0x84, 0xB6, 0xA2, 0x06, 0x94, 0xCE, 0x05, 0x2B, 0x55, 0x30, - 0xDA, 0x13, 0x42, 0x72, 0x9A, 0x52, 0x52, 0xD0, 0x98, 0x94, 0xE9, 0x88, - 0x45, 0x0C, 0x4B, 0x82, 0x81, 0x72, 0x99, 0x62, 0x60, 0xCD, 0x75, 0x45, - 0x06, 0xD8, 0x4C, 0xF0, 0x90, 0x61, 0x94, 0x5D, 0x80, 0x26, 0xD1, 0x2B, - 0x45, 0x2D, 0x04, 0x27, 0xDC, 0x53, 0x81, 0x29, 0xF6, 0xC2, 0x49, 0x53, - 0x49, 0xEE, 0x8A, 0xD5, 0x06, 0x80, 0xA7, 0xB8, 0x04, 0x0A, 0x43, 0x31, - 0x2A, 0x4A, 0x2D, 0x7A, 0x94, 0xCA, 0x6A, 0xE5, 0x1F, 0x35, 0x02, 0x61, - 0x3B, 0x8F, 0x9A, 0x13, 0x88, 0x05, 0x10, 0x82, 0xCF, 0x72, 0x90, 0x72, - 0x00, 0x2A, 0x61, 0xC9, 0x52, 0xAD, 0x34, 0xA5, 0x28, 0x60, 0xA7, 0x94, - 0x12, 0xCA, 0x52, 0x94, 0xC9, 0xD2, 0x52, 0xD0, 0x9C, 0x24, 0x92, 0x4A, - 0x5C, 0x15, 0x31, 0x08, 0x63, 0x94, 0x49, 0xD1, 0x24, 0x86, 0x41, 0x4D, - 0xA0, 0x20, 0xEE, 0x0A, 0x4D, 0x72, 0x09, 0x09, 0x0F, 0x29, 0x4A, 0x8C, - 0xCA, 0x79, 0x41, 0x4B, 0x92, 0x86, 0x65, 0x48, 0x94, 0xDA, 0x22, 0xA6, - 0x24, 0xA6, 0x2A, 0x44, 0x05, 0x14, 0x90, 0xB2, 0x8E, 0xC9, 0x4E, 0x4C, - 0x26, 0x0F, 0x45, 0x4A, 0xDA, 0x02, 0x73, 0x09, 0xC1, 0x4C, 0x52, 0x53, - 0x0E, 0xFC, 0x24, 0x9E, 0x44, 0xA4, 0x92, 0x1F, 0xFF, 0xD5, 0xEB, 0x8B, - 0xB5, 0x4C, 0x5C, 0x10, 0x4D, 0x89, 0x7A, 0x81, 0x5C, 0xA6, 0x8D, 0xA6, - 0xDC, 0x13, 0x6E, 0x41, 0xDE, 0x13, 0x87, 0x25, 0x4A, 0xB4, 0xB3, 0x29, - 0x03, 0xAA, 0x80, 0x72, 0x94, 0x94, 0x93, 0x69, 0x01, 0x95, 0x30, 0xA0, - 0xD9, 0x44, 0x03, 0x54, 0xD2, 0x95, 0xB6, 0x84, 0xE5, 0xA2, 0x14, 0xC3, - 0x53, 0x38, 0x14, 0x93, 0x48, 0x88, 0x51, 0x32, 0xA6, 0xE2, 0xA1, 0x25, - 0x14, 0x2D, 0x09, 0xA1, 0x49, 0x24, 0x90, 0xC6, 0x12, 0x52, 0x84, 0xD0, - 0x92, 0x94, 0x92, 0x49, 0xD2, 0x52, 0xE1, 0x38, 0x4C, 0x14, 0xE1, 0x04, - 0xAC, 0xA4, 0x1C, 0xA3, 0x09, 0x88, 0x29, 0x29, 0x30, 0x72, 0x89, 0xD5, - 0x40, 0x27, 0xDC, 0x61, 0x2A, 0x4D, 0xAC, 0xE0, 0x50, 0xD4, 0x89, 0x51, - 0x21, 0x14, 0x15, 0x12, 0x98, 0x3B, 0x54, 0x93, 0x42, 0x48, 0x65, 0x25, - 0x29, 0x0A, 0x24, 0x25, 0x09, 0x29, 0x96, 0xE0, 0xA2, 0x4C, 0xA6, 0x84, - 0xA1, 0x14, 0x32, 0x07, 0x44, 0x89, 0x50, 0xE1, 0x22, 0xE2, 0x92, 0xAD, - 0x9B, 0x5C, 0x9C, 0xBA, 0x50, 0xC3, 0x92, 0x2F, 0x09, 0x2A, 0xD2, 0x6E, - 0x29, 0x7A, 0x87, 0xC5, 0x0B, 0x7A, 0x60, 0x65, 0x2A, 0x55, 0xA4, 0x2F, - 0x25, 0x29, 0x51, 0x0A, 0x40, 0x68, 0x92, 0x99, 0x02, 0xA6, 0x10, 0x82, - 0x90, 0x28, 0x25, 0x28, 0x29, 0x4A, 0x80, 0x29, 0xC3, 0x90, 0x4B, 0x39, - 0x4F, 0x2A, 0x12, 0x94, 0xA4, 0xA6, 0x72, 0x9E, 0x50, 0xE5, 0x36, 0xE4, - 0x95, 0x69, 0x66, 0x12, 0x0F, 0x42, 0xDC, 0x53, 0x17, 0x25, 0x4A, 0xB4, - 0xA5, 0xFA, 0xA7, 0x0F, 0xF1, 0x55, 0xF7, 0xC1, 0x4B, 0xD5, 0x08, 0xD2, - 0xAD, 0xB4, 0x1E, 0xA5, 0xBD, 0x55, 0x6D, 0xA1, 0x4C, 0x58, 0x10, 0xA4, - 0xF1, 0x36, 0x37, 0x26, 0xDC, 0x84, 0x2C, 0x52, 0x2E, 0x04, 0x21, 0x49, - 0xB5, 0xDC, 0xF8, 0x4D, 0xBE, 0x50, 0xDC, 0xE2, 0x9A, 0x7C, 0x51, 0xA4, - 0x5B, 0x32, 0x65, 0x32, 0x81, 0x78, 0x0A, 0x2E, 0xB2, 0x78, 0x4A, 0x91, - 0x69, 0xC3, 0x82, 0x77, 0x3F, 0x45, 0x5C, 0x3D, 0x27, 0x5B, 0x09, 0x52, - 0xAD, 0x9E, 0xED, 0x52, 0x55, 0xFD, 0x5D, 0x52, 0x46, 0x91, 0xC4, 0xFF, - 0x00, 0xFF, 0xD6, 0xE8, 0x35, 0x52, 0x82, 0xA4, 0x1A, 0xA5, 0xB6, 0x15, - 0xDB, 0x73, 0xE9, 0x88, 0x0A, 0x40, 0x29, 0x00, 0x13, 0xC0, 0x4A, 0xD3, - 0x4B, 0x00, 0xA6, 0xD0, 0x98, 0x27, 0x0E, 0x01, 0x04, 0xA7, 0xAC, 0x0E, - 0xE8, 0x82, 0x15, 0x60, 0xF8, 0x4B, 0xD5, 0x84, 0xDA, 0x5D, 0x6D, 0xA2, - 0xE0, 0xA2, 0xE7, 0x20, 0x8B, 0x27, 0x95, 0x17, 0x58, 0x95, 0x26, 0xD9, - 0xB9, 0xC1, 0x46, 0x50, 0xF7, 0xEA, 0x9C, 0x39, 0x1A, 0x5B, 0x6C, 0xD2, - 0x4D, 0x29, 0xC2, 0x49, 0x5E, 0x13, 0x27, 0x4C, 0x92, 0x96, 0x4E, 0x92, - 0x50, 0x92, 0x17, 0x05, 0x48, 0x15, 0x10, 0x9C, 0x20, 0x96, 0x69, 0x68, - 0xA3, 0x29, 0xD2, 0x4A, 0x88, 0x4C, 0x94, 0xA6, 0x2E, 0x49, 0x0A, 0x85, - 0x12, 0x96, 0xE4, 0xB7, 0x22, 0xA5, 0x42, 0x50, 0x94, 0xA4, 0x92, 0x95, - 0x0A, 0x3A, 0x27, 0x24, 0x26, 0x90, 0x92, 0x14, 0x91, 0x4B, 0x44, 0x8C, - 0x24, 0xA6, 0x25, 0x0D, 0xC7, 0xC1, 0x4D, 0xCA, 0x05, 0x15, 0xA5, 0x69, - 0x4A, 0x0A, 0x62, 0x9C, 0x14, 0x54, 0xBC, 0x14, 0x80, 0x84, 0xF3, 0x09, - 0x72, 0x92, 0x95, 0x29, 0xF7, 0x15, 0x12, 0x13, 0x80, 0x92, 0x99, 0x02, - 0xA4, 0x10, 0xD4, 0x9A, 0x82, 0x43, 0x30, 0x53, 0x82, 0xA2, 0x9A, 0x52, - 0x4A, 0x49, 0x4C, 0x1C, 0xA3, 0x29, 0xA6, 0x10, 0x55, 0xA5, 0x2A, 0x24, - 0xA8, 0xEE, 0x4B, 0x70, 0x49, 0x4B, 0xEE, 0x51, 0x2E, 0x29, 0x12, 0x14, - 0x09, 0x45, 0x05, 0x72, 0xE5, 0x12, 0x53, 0xCA, 0x6E, 0xE9, 0x21, 0x40, - 0x90, 0x54, 0xDA, 0xE2, 0xA3, 0xB7, 0x45, 0x26, 0x88, 0x49, 0x4C, 0xC3, - 0x93, 0xEF, 0x3E, 0x28, 0x4E, 0x74, 0x28, 0x1B, 0x09, 0xE1, 0x2A, 0x4D, - 0xB6, 0x0B, 0xC7, 0x8A, 0x6D, 0xE3, 0xB2, 0xAF, 0x2E, 0x3C, 0xA9, 0x00, - 0x52, 0xA4, 0x5A, 0x57, 0x3A, 0x54, 0x0A, 0x61, 0x29, 0x14, 0x94, 0xAD, - 0xCA, 0x0E, 0x24, 0xA7, 0x2A, 0x3A, 0xA2, 0x86, 0x3E, 0xE9, 0x49, 0x4D, - 0x24, 0x6D, 0x54, 0xFF, 0x00, 0xFF, 0xD7, 0xEA, 0x76, 0x27, 0xD8, 0x8A, - 0x18, 0xA5, 0xB3, 0x45, 0x6E, 0xDA, 0x54, 0x80, 0x35, 0x3C, 0x22, 0x6C, - 0x4B, 0x6A, 0x56, 0xAA, 0x44, 0x42, 0x81, 0x06, 0x55, 0x80, 0xC9, 0x48, - 0xD4, 0x07, 0x64, 0xAD, 0x54, 0x80, 0x07, 0x14, 0xFB, 0x61, 0x17, 0x6C, - 0x28, 0xBA, 0x51, 0xB5, 0x53, 0x0E, 0xC9, 0xB5, 0x52, 0x2C, 0x29, 0xC3, - 0x60, 0x24, 0x8A, 0x47, 0x05, 0x4C, 0x04, 0xA3, 0x55, 0x20, 0x92, 0xA9, - 0x93, 0x53, 0xA6, 0x09, 0x1D, 0x10, 0x5C, 0xA2, 0xA3, 0x25, 0x24, 0xE9, - 0x21, 0x70, 0x9E, 0x13, 0x04, 0xF2, 0x92, 0x57, 0x49, 0x34, 0xA5, 0x28, - 0x29, 0x92, 0x52, 0xA1, 0xB9, 0x2D, 0xC9, 0x52, 0x99, 0x12, 0xA0, 0xE2, - 0x98, 0x95, 0x12, 0x51, 0xA4, 0x12, 0xBC, 0xA5, 0xB9, 0x42, 0x4A, 0x70, - 0x8A, 0x2D, 0x98, 0x29, 0xC9, 0x51, 0x4C, 0x4A, 0x49, 0x51, 0x70, 0x4D, - 0xB9, 0x46, 0x53, 0x4A, 0x48, 0xB6, 0x5B, 0xD3, 0xEE, 0x42, 0x25, 0x29, - 0xD1, 0x14, 0x5B, 0x32, 0x65, 0x44, 0x94, 0xC4, 0x94, 0xDA, 0xA4, 0xA5, - 0x12, 0x9D, 0xAE, 0x95, 0x02, 0x13, 0xB5, 0x24, 0x24, 0x29, 0x00, 0x9B, - 0x54, 0xFA, 0xC2, 0x49, 0x52, 0x79, 0x51, 0x82, 0x90, 0x09, 0x29, 0x79, - 0x52, 0x06, 0x14, 0x53, 0xA4, 0xA5, 0xF7, 0x25, 0x2A, 0x25, 0xC9, 0x20, - 0xA6, 0x41, 0xD0, 0x9C, 0xBC, 0x76, 0x43, 0x77, 0x0A, 0x32, 0x95, 0x2A, - 0xD2, 0x6E, 0x4E, 0x20, 0xA1, 0x84, 0xB7, 0x24, 0xAB, 0x48, 0x48, 0x09, - 0xB4, 0x2A, 0x32, 0x94, 0xA4, 0xA6, 0x49, 0x0F, 0x34, 0xC1, 0x49, 0x24, - 0xAE, 0x9E, 0x54, 0x64, 0xA4, 0x64, 0xA4, 0xA5, 0x39, 0xBD, 0xD0, 0xC8, - 0x85, 0x32, 0x0A, 0x6D, 0xA5, 0x24, 0x10, 0xC4, 0x29, 0xB5, 0x20, 0xD5, - 0x36, 0x35, 0x25, 0x00, 0xB6, 0xD4, 0xE1, 0x88, 0x80, 0x29, 0x86, 0xA1, - 0x6B, 0xA9, 0x07, 0xA4, 0x9F, 0xD3, 0x01, 0x1A, 0x13, 0x16, 0xCA, 0x56, - 0xAA, 0x43, 0xB4, 0x4A, 0x48, 0x9B, 0x12, 0x4A, 0xD5, 0x4F, 0xFF, 0xD0, - 0xED, 0x8B, 0x52, 0x0C, 0x48, 0xA7, 0x69, 0x56, 0x5A, 0xAB, 0x8A, 0xD2, - 0x35, 0x85, 0x30, 0xE4, 0xCE, 0x72, 0x09, 0x47, 0xB4, 0x04, 0xA1, 0x39, - 0x29, 0xB7, 0x14, 0x50, 0xC1, 0xC0, 0x28, 0x96, 0xA2, 0x19, 0x29, 0x6D, - 0x29, 0x5A, 0x29, 0x09, 0x61, 0x4B, 0x69, 0xEE, 0x8D, 0xB1, 0x36, 0xC4, - 0x6D, 0x54, 0x84, 0xB5, 0x3C, 0x04, 0x5D, 0x85, 0x37, 0xA6, 0x95, 0xAA, - 0x91, 0x41, 0x4B, 0x69, 0x46, 0x0C, 0x4F, 0xB5, 0x2B, 0x55, 0x21, 0xDA, - 0x52, 0xDA, 0x8D, 0xB5, 0x2D, 0xA9, 0x5A, 0xA9, 0x0E, 0xD4, 0xD0, 0x51, - 0x8B, 0x53, 0x40, 0x4A, 0xD5, 0x48, 0xA0, 0xA5, 0x05, 0x13, 0x6A, 0x50, - 0x95, 0xA2, 0x91, 0x42, 0x62, 0x11, 0x4B, 0x53, 0x10, 0x8D, 0xAA, 0x91, - 0x96, 0xA6, 0xDA, 0xA6, 0x54, 0x4A, 0x48, 0x62, 0x42, 0x8C, 0xA9, 0x39, - 0x40, 0x84, 0x50, 0x57, 0xDC, 0x9A, 0x54, 0x53, 0xF0, 0x92, 0x14, 0x99, - 0x4B, 0x94, 0xD0, 0x92, 0x98, 0x99, 0x4D, 0x05, 0x4A, 0x14, 0xA1, 0x1B, - 0x53, 0x00, 0xDF, 0x14, 0xA0, 0xA9, 0x42, 0x45, 0x2B, 0x53, 0x08, 0x4A, - 0x13, 0x92, 0x13, 0x84, 0x94, 0xC7, 0x50, 0xA4, 0x25, 0x38, 0x0A, 0x40, - 0x04, 0x95, 0x4B, 0x76, 0x4C, 0xA4, 0x79, 0x4D, 0xDD, 0x05, 0x2B, 0x6A, - 0x7D, 0xBA, 0x25, 0xB8, 0xA7, 0x0E, 0x49, 0x28, 0xF6, 0x92, 0x54, 0xB6, - 0xE8, 0x88, 0x1A, 0x0A, 0x62, 0xD0, 0x95, 0xAA, 0x90, 0xBA, 0x41, 0x4D, - 0x05, 0x14, 0xB5, 0x47, 0x6A, 0x36, 0x8A, 0x61, 0x05, 0x48, 0x35, 0x48, - 0x35, 0x3C, 0x21, 0x6A, 0xA6, 0x10, 0x52, 0xDA, 0x89, 0xB5, 0x23, 0x09, - 0x5A, 0x69, 0x88, 0x10, 0x9C, 0x04, 0x8A, 0x70, 0xE4, 0x94, 0xA8, 0x52, - 0x01, 0x30, 0x29, 0xD0, 0x4A, 0x93, 0x42, 0x74, 0xE1, 0x25, 0x28, 0x35, - 0x38, 0x09, 0xC0, 0x4E, 0x02, 0x16, 0x95, 0xDA, 0x11, 0x00, 0x4C, 0x14, - 0x82, 0x09, 0x5B, 0x6A, 0x70, 0xD9, 0x4E, 0x13, 0xA4, 0x9A, 0x5B, 0xD3, - 0x09, 0x27, 0x49, 0x25, 0x3F, 0xFF, 0xD1, 0xED, 0xE1, 0x34, 0x29, 0xC2, - 0x50, 0xAC, 0x35, 0x98, 0xA6, 0x32, 0xA5, 0x09, 0x6D, 0x94, 0x94, 0xC5, - 0x3C, 0x27, 0x0D, 0x4F, 0x09, 0x29, 0x68, 0x4F, 0x09, 0xE1, 0x3C, 0x24, - 0xA6, 0x30, 0x94, 0x29, 0x42, 0x78, 0x41, 0x2C, 0x21, 0x28, 0x53, 0x84, - 0xA1, 0x25, 0x30, 0x84, 0xA1, 0x4E, 0x13, 0x42, 0x4A, 0x63, 0x09, 0x88, - 0x53, 0x85, 0x12, 0x8A, 0x18, 0x90, 0x9A, 0x14, 0x92, 0x84, 0x94, 0xC2, - 0x12, 0x85, 0x38, 0x4C, 0x42, 0x36, 0x8A, 0x60, 0x42, 0x81, 0x45, 0x20, - 0xA8, 0x16, 0xA4, 0x82, 0x89, 0xC5, 0x40, 0xCA, 0x29, 0x6A, 0x8E, 0xD4, - 0xE4, 0x23, 0x32, 0xA0, 0x8E, 0x6B, 0x94, 0xBD, 0x24, 0xAD, 0x14, 0x82, - 0x13, 0xC2, 0x21, 0xA8, 0xA5, 0xB0, 0x84, 0x6D, 0x54, 0xC1, 0x25, 0x2D, - 0xA5, 0x2D, 0xA5, 0x05, 0x53, 0x08, 0x4C, 0x53, 0xB9, 0x44, 0x82, 0x8A, - 0x16, 0x94, 0xC4, 0xA7, 0xD5, 0x30, 0xD4, 0xA4, 0x85, 0xBB, 0xA9, 0xB4, - 0x26, 0xD8, 0xA4, 0x02, 0x49, 0x01, 0x96, 0x89, 0xC0, 0x94, 0xC1, 0x15, - 0xA0, 0x42, 0x09, 0x46, 0x5A, 0x9A, 0x11, 0x88, 0x09, 0xA0, 0x25, 0x6A, - 0xA4, 0x50, 0x9C, 0x22, 0x16, 0xA4, 0x18, 0x95, 0xAA, 0x98, 0x04, 0xFA, - 0x22, 0x6C, 0x01, 0x2D, 0xA0, 0xA5, 0x69, 0xA4, 0x70, 0x91, 0x03, 0xB2, - 0x9E, 0xC0, 0x13, 0x7B, 0x52, 0xB5, 0x52, 0x38, 0x29, 0x00, 0x42, 0x9C, - 0x6A, 0x9C, 0x35, 0x2B, 0x55, 0x30, 0x84, 0xC4, 0x22, 0x90, 0x02, 0x83, - 0x92, 0x53, 0x08, 0x4D, 0x21, 0x3C, 0x4A, 0x6D, 0x85, 0x14, 0x2E, 0x0A, - 0x70, 0xE4, 0xC1, 0xA9, 0xE1, 0x05, 0x32, 0x04, 0x27, 0x0A, 0x09, 0xC2, - 0x49, 0x66, 0x9C, 0x28, 0xA9, 0x09, 0x41, 0x4C, 0x81, 0x52, 0x0A, 0x20, - 0x29, 0x80, 0x82, 0x59, 0x04, 0xE9, 0x00, 0x9D, 0x05, 0xCB, 0x24, 0xA4, - 0x92, 0x4A, 0x7F, 0xFF, 0xD2, 0xEE, 0xA1, 0x3C, 0x24, 0x9D, 0x4E, 0xD7, - 0x63, 0x09, 0xE1, 0x4A, 0x12, 0xDA, 0x92, 0x96, 0x84, 0xA1, 0x4A, 0x02, - 0x50, 0x92, 0x96, 0x84, 0xA1, 0x4A, 0x12, 0x49, 0x4B, 0x42, 0x50, 0xA4, - 0x99, 0x25, 0x2D, 0x09, 0x42, 0x74, 0x92, 0x4A, 0xD0, 0x9A, 0x13, 0xA6, - 0x29, 0x21, 0x64, 0xC4, 0x27, 0x4C, 0x92, 0x96, 0x84, 0xA1, 0x3C, 0x24, - 0x92, 0x96, 0x84, 0xA1, 0x3A, 0x49, 0x29, 0x89, 0x0A, 0x24, 0x29, 0xA4, - 0x8A, 0x11, 0x16, 0xA8, 0x46, 0xA8, 0xC4, 0x26, 0x2D, 0x4A, 0xD1, 0x4C, - 0x02, 0x4A, 0x61, 0xA9, 0x10, 0x8D, 0xAA, 0x98, 0x18, 0x43, 0x25, 0x14, - 0xB1, 0x0C, 0xB7, 0x54, 0x90, 0x56, 0x4C, 0xE5, 0x3D, 0xAA, 0x2E, 0x6A, - 0x2A, 0xA4, 0x25, 0xA2, 0x53, 0x86, 0x4A, 0x91, 0x69, 0x4E, 0xD0, 0x51, - 0xB4, 0x52, 0x33, 0x5A, 0x6D, 0xA0, 0x23, 0x98, 0x50, 0x2D, 0x4A, 0xD5, - 0x4C, 0x04, 0x15, 0x20, 0xC0, 0x9F, 0x6A, 0x90, 0x1A, 0x21, 0x6A, 0xA6, - 0x21, 0x9D, 0xD4, 0xA0, 0x24, 0x02, 0x78, 0x29, 0x25, 0x6D, 0x52, 0x82, - 0xA7, 0xAA, 0x89, 0x3E, 0x29, 0x29, 0x40, 0x88, 0xD5, 0x48, 0x42, 0x82, - 0x40, 0x84, 0x94, 0xCD, 0xDC, 0x28, 0x14, 0xF3, 0x29, 0x8A, 0x4A, 0x5C, - 0x04, 0xC5, 0xA5, 0x2D, 0xC9, 0xC3, 0xA5, 0x25, 0x2C, 0x34, 0x52, 0x05, - 0x44, 0xA6, 0x0E, 0x01, 0x25, 0x33, 0x21, 0x47, 0x68, 0x4E, 0x5C, 0x90, - 0x72, 0x4A, 0x5B, 0x60, 0x4C, 0x5A, 0xA6, 0x0A, 0x72, 0x92, 0x90, 0xED, - 0x48, 0xB6, 0x78, 0x46, 0x00, 0x14, 0xB6, 0x78, 0x25, 0x6A, 0xA4, 0x3B, - 0x4A, 0x70, 0xD4, 0x6F, 0x4C, 0xA7, 0x15, 0xC2, 0x56, 0xAA, 0x42, 0x02, - 0x90, 0x08, 0x9B, 0x12, 0xD8, 0x85, 0xAA, 0x96, 0x01, 0x4C, 0x26, 0x01, - 0x4F, 0x6A, 0x45, 0x21, 0x70, 0x9E, 0x12, 0x01, 0x3A, 0x09, 0x5A, 0x12, - 0x4E, 0x92, 0x4A, 0x7F, 0xFF, 0xD3, 0xEF, 0x13, 0x84, 0xF0, 0x94, 0x29, - 0xDA, 0xEB, 0xA4, 0x92, 0x78, 0x41, 0x2C, 0x75, 0x4E, 0x14, 0xA1, 0x28, - 0x49, 0x4B, 0x42, 0x50, 0xA5, 0x09, 0x42, 0x4A, 0x62, 0x92, 0x94, 0x25, - 0x09, 0x29, 0x8C, 0x25, 0x0A, 0x50, 0x9A, 0x12, 0x53, 0x14, 0xD0, 0xA7, - 0x09, 0xA1, 0x25, 0x31, 0x84, 0xA1, 0x3C, 0x24, 0x8A, 0x98, 0x90, 0x99, - 0x48, 0xA6, 0x21, 0x24, 0x31, 0x49, 0x3A, 0x64, 0x94, 0xA8, 0x4A, 0x12, - 0x49, 0x25, 0x2D, 0x09, 0xA1, 0x3A, 0x49, 0x29, 0x68, 0x29, 0x27, 0x49, - 0x25, 0x31, 0x2A, 0x24, 0x05, 0x22, 0x98, 0xA2, 0x86, 0x30, 0x14, 0x0A, - 0x99, 0x09, 0xA1, 0x24, 0x23, 0xD5, 0x38, 0x4E, 0x5A, 0x98, 0x84, 0x50, - 0xA4, 0xE1, 0xA1, 0x47, 0x55, 0x20, 0xE4, 0x94, 0xAD, 0x81, 0x3E, 0xC4, - 0xDB, 0x94, 0x83, 0xA7, 0x94, 0x92, 0xAD, 0xA0, 0x25, 0xB4, 0x29, 0x88, - 0x29, 0x10, 0x85, 0xAA, 0x98, 0x91, 0xA2, 0x19, 0x6E, 0xA8, 0x84, 0x21, - 0x94, 0x42, 0x8B, 0x12, 0x9A, 0x13, 0x94, 0xD0, 0x8A, 0x15, 0xAA, 0x7D, - 0x53, 0x80, 0x9D, 0x05, 0x31, 0xDA, 0x98, 0x68, 0xA6, 0x94, 0x24, 0xA6, - 0x04, 0xCA, 0x50, 0xA7, 0x09, 0x42, 0x4A, 0xA6, 0x29, 0xE0, 0x29, 0x6D, - 0x4A, 0x12, 0x55, 0x28, 0x37, 0xC1, 0x39, 0x6A, 0x76, 0x88, 0x52, 0x29, - 0x26, 0x98, 0xB5, 0xA8, 0xCD, 0x68, 0x50, 0x10, 0x14, 0xA5, 0x02, 0x95, - 0xC8, 0x01, 0x31, 0x12, 0x90, 0xD5, 0x3A, 0x0A, 0x5A, 0x13, 0x16, 0xA9, - 0x29, 0x00, 0x8A, 0x98, 0x06, 0xA9, 0x84, 0xB6, 0xA7, 0x01, 0x04, 0xAC, - 0x92, 0x94, 0x25, 0x09, 0x29, 0x8A, 0x4A, 0x50, 0x92, 0x4A, 0x7F, 0xFF, - 0xD4, 0xF4, 0x08, 0x4A, 0x13, 0xA4, 0xA6, 0x60, 0x52, 0x74, 0xC9, 0xD2, - 0x52, 0x93, 0xA4, 0x92, 0x49, 0x52, 0x49, 0x27, 0x49, 0x4B, 0x25, 0x09, - 0xD2, 0x49, 0x4B, 0x24, 0x9E, 0x12, 0x84, 0x94, 0xC5, 0x32, 0x92, 0x64, - 0x94, 0xC5, 0x24, 0xF0, 0x98, 0xA4, 0x85, 0x8A, 0x64, 0xE5, 0x32, 0x4A, - 0x58, 0xA6, 0x4E, 0x98, 0xA2, 0x85, 0x92, 0x4E, 0x92, 0x4A, 0x5A, 0x12, - 0x4E, 0x99, 0x25, 0x29, 0x28, 0x49, 0x24, 0x94, 0xB1, 0x0A, 0x24, 0x29, - 0x14, 0xC9, 0x29, 0x89, 0x09, 0x93, 0x94, 0xC5, 0x14, 0x2C, 0x42, 0x62, - 0x13, 0xA4, 0x92, 0x98, 0x42, 0x50, 0x14, 0x8A, 0x8A, 0x28, 0x5A, 0x13, - 0x84, 0x93, 0x84, 0x94, 0xCC, 0x29, 0x21, 0x82, 0x53, 0xCA, 0x09, 0x5C, - 0x85, 0x02, 0x14, 0xE5, 0x44, 0xA4, 0xA6, 0x10, 0x94, 0x29, 0x27, 0x80, - 0x8A, 0x18, 0x42, 0x74, 0xF0, 0x94, 0x24, 0xA5, 0xA1, 0x3A, 0x74, 0x90, - 0x52, 0xD0, 0x9C, 0x04, 0xE9, 0x42, 0x49, 0x54, 0x25, 0x09, 0xE1, 0x3C, - 0x24, 0xA6, 0x30, 0x9C, 0x05, 0x28, 0x09, 0xE1, 0x25, 0x2D, 0x09, 0xE1, - 0x3C, 0x27, 0x84, 0x12, 0xC6, 0x13, 0xC4, 0x27, 0x80, 0x92, 0x4A, 0x50, - 0x52, 0x10, 0x99, 0x3A, 0x4A, 0x5D, 0x3A, 0x41, 0x3A, 0x09, 0x54, 0x25, - 0x09, 0x27, 0x49, 0x4B, 0x42, 0x49, 0xE1, 0x24, 0x94, 0xFF, 0x00, 0xFF, - 0xD5, 0xF4, 0x14, 0x93, 0x95, 0x12, 0xA6, 0x60, 0x5D, 0x3A, 0x8C, 0xA7, - 0x49, 0x4C, 0x93, 0xA8, 0x85, 0x20, 0x92, 0x54, 0x9E, 0x13, 0xC2, 0x74, - 0x14, 0xB4, 0x25, 0x09, 0xE1, 0x24, 0x94, 0xC5, 0x22, 0x9D, 0x24, 0x94, - 0xC5, 0x34, 0x29, 0x42, 0x68, 0x45, 0x4C, 0x61, 0x31, 0x52, 0x29, 0x8A, - 0x4A, 0x62, 0x98, 0xA9, 0x14, 0xC5, 0x24, 0x30, 0x4C, 0xA4, 0x54, 0x4A, - 0x28, 0x52, 0x49, 0x26, 0x49, 0x4B, 0xA6, 0x49, 0x24, 0x94, 0xA4, 0x92, - 0x4D, 0x29, 0x29, 0x4A, 0x25, 0x39, 0x4C, 0x92, 0x16, 0x29, 0x92, 0x49, - 0x15, 0x2C, 0x92, 0x49, 0x24, 0xA5, 0x93, 0x42, 0x92, 0x64, 0x94, 0xB2, - 0x49, 0x14, 0x92, 0x42, 0xE9, 0x26, 0x4E, 0x92, 0x94, 0x92, 0x49, 0x24, - 0x95, 0x92, 0x49, 0x24, 0x90, 0xA4, 0xE9, 0x92, 0x45, 0x4B, 0xA7, 0x4C, - 0x92, 0x0A, 0x5D, 0x3A, 0x64, 0xE9, 0x25, 0x70, 0x9D, 0x32, 0x74, 0x94, - 0xB8, 0x53, 0xD1, 0x0C, 0x29, 0x02, 0x82, 0x59, 0x82, 0x12, 0x30, 0xA0, - 0x13, 0xA4, 0xA5, 0xD2, 0x49, 0x24, 0x94, 0xA0, 0x9D, 0x20, 0xA4, 0x92, - 0x94, 0x13, 0x84, 0xA1, 0x3C, 0x20, 0x95, 0x24, 0x94, 0x27, 0x49, 0x4B, - 0x24, 0x9D, 0x24, 0x92, 0xFF, 0x00, 0xFF, 0xD6, 0xF4, 0x24, 0xC5, 0x7C, - 0xD2, 0x92, 0x99, 0x81, 0xFA, 0x55, 0x20, 0xBE, 0x6A, 0x49, 0x25, 0x3F, - 0x4B, 0x85, 0x31, 0x0B, 0xE6, 0x54, 0x92, 0x4B, 0xF4, 0xE0, 0x4E, 0xBE, - 0x62, 0x49, 0x04, 0xBF, 0x4E, 0xA4, 0xBE, 0x62, 0x49, 0x25, 0x3F, 0x4E, - 0x24, 0xBE, 0x63, 0x49, 0x24, 0x3F, 0x4D, 0xA6, 0x2B, 0xE6, 0x54, 0x92, - 0x53, 0xF4, 0xC9, 0x4C, 0x57, 0xCC, 0xE9, 0x22, 0xA7, 0xE9, 0x62, 0x98, - 0xAF, 0x9A, 0x92, 0x49, 0x0F, 0xD2, 0x65, 0x31, 0x5F, 0x36, 0xA4, 0x8A, - 0x1F, 0xA4, 0x12, 0x5F, 0x37, 0xA4, 0x92, 0x9F, 0xA4, 0x0A, 0x45, 0x7C, - 0xDE, 0x92, 0x4A, 0x7E, 0x8F, 0x4C, 0xBE, 0x71, 0x49, 0x24, 0x3F, 0x46, - 0x94, 0xC5, 0x7C, 0xE6, 0x92, 0x2A, 0x7E, 0x8B, 0x49, 0x7C, 0xE8, 0x92, - 0x4A, 0x7E, 0x8A, 0x4E, 0xBE, 0x74, 0x49, 0x25, 0x3F, 0x45, 0x26, 0x5F, - 0x3B, 0x24, 0x92, 0x1F, 0xA2, 0x53, 0x2F, 0x9D, 0xD2, 0x49, 0x4F, 0xD1, - 0x09, 0xD7, 0xCE, 0xC9, 0x22, 0xA7, 0xE8, 0x94, 0x97, 0xCE, 0xC9, 0x20, - 0x97, 0xE8, 0x84, 0x97, 0xCE, 0xE9, 0x24, 0x87, 0xE8, 0x84, 0x97, 0xCE, - 0xE9, 0x22, 0xA7, 0xE8, 0x94, 0xEB, 0xE7, 0x54, 0x90, 0x53, 0xF4, 0x52, - 0x75, 0xF3, 0xA2, 0x49, 0x25, 0xFA, 0x31, 0x3A, 0xF9, 0xC9, 0x24, 0x94, - 0xFD, 0x1C, 0x90, 0x5F, 0x38, 0xA4, 0x92, 0x9F, 0xA3, 0xC2, 0x92, 0xF9, - 0xB9, 0x24, 0x12, 0xFD, 0x22, 0x9D, 0x7C, 0xDA, 0x92, 0x4A, 0x7E, 0x93, - 0x0A, 0x41, 0x7C, 0xD4, 0x92, 0x09, 0x7E, 0x96, 0x4E, 0xBE, 0x68, 0x49, - 0x25, 0x3F, 0x4B, 0xA4, 0xBE, 0x68, 0x49, 0x24, 0xBF, 0x4B, 0xA4, 0xBE, - 0x68, 0x49, 0x25, 0x3F, 0xFF, 0xD9, -}; - -#define IMAGE_LENGTH 0x000049F2 - -char * get_clouds(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(IMAGE_LENGTH); - memcpy(ptr, Clouds, IMAGE_LENGTH); - *Len = IMAGE_LENGTH; - - return ptr; -} - -char * get_ais_blue(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(aisblue_png_len); - memcpy(ptr, aisblue_png, aisblue_png_len); - *Len = IMAGE_LENGTH; - - return ptr; -} - -char * get_ais_navaid(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(aisnavaid_png_len); - memcpy(ptr, aisnavaid_png, aisnavaid_png_len); - *Len = aisnavaid_png_len; - - return ptr; -} - -char * get_ais_green(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(aisgreen_png_len); - memcpy(ptr, aisgreen_png, aisgreen_png_len); - *Len = aisgreen_png_len; - - return ptr; -} - -char * get_ais_red(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(aisred_png_len); - memcpy(ptr, aisred_png, aisred_png_len); - *Len = aisred_png_len; - - return ptr; -} - -char * get_ais_white(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(aiswhite_png_len); - memcpy(ptr, aiswhite_png, aiswhite_png_len); - *Len = aiswhite_png_len; - - return ptr; -} - -char * get_ais_trans(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(aistrans_png_len); - memcpy(ptr, aistrans_png, aistrans_png_len); - *Len = aistrans_png_len; - - return ptr; -} - - -char * get_greenplane(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(greenplane_png_len); - memcpy(ptr, greenplane_png, greenplane_png_len); - *Len = greenplane_png_len; - - return ptr; -} - -char * get_yellowplane(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(yellowplane_png_len); - memcpy(ptr, yellowplane_png, yellowplane_png_len); - *Len = yellowplane_png_len; - - return ptr; -} - -char * get_helicopter(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(helicopter_png_len); - memcpy(ptr, helicopter_png, helicopter_png_len); - *Len = helicopter_png_len; - - return ptr; -} - - -char * get_plane(int * Len) -{ - unsigned char * ptr; - - ptr = malloc(plane_png_len); - memcpy(ptr, plane_png, plane_png_len); - *Len = plane_png_len; - - return ptr; -} - - - - -char * get_aprs() -{ - char Msg[] = - -"\n" -"\n" -"\n" -"\n" -"\n" -"\n" -"G8BPQ APRS Display\n" -"\n" - -"\n" -"\n" - - -//"\n" -//"\n" -"\n" -"\n" -"\n" -"\n" -" \n" -"\n" -"\n" -"\n" -"\n" -"\n" -"
\n" -"\n" -""; - - - return _strdup(Msg);; -} - - -char * get_rotatedMarker() -{ - char Msg[] = - -"(function() {\n" -" // save these original methods before they are overwritten\n" -" var proto_initIcon = L.Marker.prototype._initIcon;\n" -" var proto_setPos = L.Marker.prototype._setPos;\n" -"\n" -" var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');\n" -"\n" -" L.Marker.addInitHook(function () {\n" -" var iconOptions = this.options.icon && this.options.icon.options;\n" -" var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;\n" -" if (iconAnchor) {\n" -" iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');\n" -" }\n" -" this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;\n" -" this.options.rotationAngle = this.options.rotationAngle || 0;\n" -"\n" -" // Ensure marker keeps rotated during dragging\n" -" this.on('drag', function(e) { e.target._applyRotation(); });\n" -" });\n" -"\n" -" L.Marker.include({\n" -" _initIcon: function() {\n" -" proto_initIcon.call(this);\n" -" },\n" -"\n" -" _setPos: function (pos) {\n" -" proto_setPos.call(this, pos);\n" -" this._applyRotation();\n" -" },\n" -"\n" -" _applyRotation: function () {\n" -" if(this.options.rotationAngle) {\n" -" this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;\n" -"\n" -" if(oldIE) {\n" -" // for IE 9, use the 2D rotation\n" -" this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';\n" -" } else {\n" -" // for modern browsers, prefer the 3D accelerated version\n" -" this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';\n" -" }\n" -" }\n" -" },\n" -"\n" -" setRotationAngle: function(angle) {\n" -" this.options.rotationAngle = angle;\n" -" this.update();\n" -" return this;\n" -" },\n" -"\n" -" setRotationOrigin: function(origin) {\n" -" this.options.rotationOrigin = origin;\n" -" this.update();\n" -" return this;\n" -" }\n" -" });\n" -"})();"; - - - return _strdup(Msg);; -} - - - -char * GetStandardPage(char * FN, int * Len) -{ - if (_stricmp(FN, "aprs.html") == 0) - return get_aprs(); - - if (_stricmp(FN, "leaflet.rotatedMarker.js") == 0) - return get_rotatedMarker(); - - if (_stricmp(FN, "info_call.html") == 0) - return get_info_call(); - - if (_stricmp(FN, "infomobile_call.html") == 0) - return get_infomobile_call(); - - if (_stricmp(FN, "infoobj_call.html") == 0) - return get_infoobj_call(); - - if (_stricmp(FN, "infowx_call.html") == 0) - return get_infowx_call(); - - if (_stricmp(FN, "all.html") == 0) - return get_all(); - - if (_stricmp(FN, "allrf.html") == 0) - return get_allrf(); - - if (_stricmp(FN, "wxall.html") == 0) - return get_wxall(); - - if (_stricmp(FN, "wxrf.html") == 0) - return get_wxrf(); - - if (_stricmp(FN, "obj.html") == 0) - return get_obj(); - - if (_stricmp(FN, "objrf.html") == 0) - return get_objrf(); - - if (_stricmp(FN, "mobilesall.html") == 0) - return get_mobileall(); - - if (_stricmp(FN, "mobilesrf.html") == 0) - return get_mobilesrf(); - - if (_stricmp(FN, "info.html") == 0) - return get_info(); - - if (_stricmp(FN, "noinfo.html") == 0) - return get_noinfo(); - - if (_stricmp(FN, "favicion.ico") == 0) - return get_favicon(Len); - - if (_stricmp(FN, "Images/clouds.jpg") == 0) - return get_clouds(Len); - - if (_stricmp(FN, "aisnavaid.png") == 0) - return get_ais_navaid(Len); - - if (_stricmp(FN, "aisgreen.png") == 0) - return get_ais_green(Len); - - if (_stricmp(FN, "aisblue.png") == 0) - return get_ais_blue(Len); - - if (_stricmp(FN, "aisred.png") == 0) - return get_ais_red(Len); - - if (_stricmp(FN, "aistrans.png") == 0) - return get_ais_trans(Len); - - if (_stricmp(FN, "aiswhite.png") == 0) - return get_ais_white(Len); - - if (_stricmp(FN, "greenplane.png") == 0) - return get_greenplane(Len); - - if (_stricmp(FN, "yellowplane.png") == 0) - return get_yellowplane(Len); - - if (_stricmp(FN, "plane.png") == 0) - return get_plane(Len); - - if (_stricmp(FN, "helicopter.png") == 0) - return get_helicopter(Len); - - return 0; -} - diff --git a/BBSHTMLConfig.c b/BBSHTMLConfig.c index f965f27..01b05bd 100644 --- a/BBSHTMLConfig.c +++ b/BBSHTMLConfig.c @@ -398,7 +398,7 @@ int SendHeader(char * Reply, char * Key) } -void ConvertTitletoUTF8(char * Title, char * UTF8Title) +void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len) { if (WebIsUTF8(Title, (int)strlen(Title)) == FALSE) { @@ -414,15 +414,26 @@ void ConvertTitletoUTF8(char * Title, char * UTF8Title) wlen = MultiByteToWideChar(CP_ACP, 0, Title, len, BufferW, origlen * 2); len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, UTF8Title, origlen * 2, NULL, NULL); #else - int left = 2 * strlen(Title); - int len = origlen; - iconv_t * icu = NULL; - - if (icu == NULL) - icu = iconv_open("UTF-8", "CP1252"); + size_t left = Len - 1; + size_t len = origlen; + + iconv_t * icu = WebMail->iconv_toUTF8; + + if (WebMail->iconv_toUTF8 == NULL) + icu = WebMail->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + + if (icu == (iconv_t)-1) + { + strcpy(UTF8Title, Title); + WebMail->iconv_toUTF8 = NULL; + return; + } + + char * orig = UTF8Title; iconv(icu, NULL, NULL, NULL, NULL); // Reset State Machine iconv(icu, &Title, &len, (char ** __restrict__)&UTF8Title, &left); + #endif } else @@ -1681,6 +1692,85 @@ VOID ProcessConfUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char HoldAt = GetMultiStringInput(input, "Hat="); HoldBID = GetMultiStringInput(input, "HBID="); + // Look for fbb style filters + + input = strstr(input, "&Action="); + + // delete old list + + while(Filters && Filters->Next) + { + FBBFilter * next = Filters->Next; + free(Filters); + Filters = next; + } + + free(Filters); + Filters = NULL; + + while (input) + { + // extract and validate before saving + + FBBFilter Filter; + FBBFilter * PFilter; + + memset(&Filter, 0, sizeof(FBBFilter)); + + Filter.Action = toupper(input[8]); + + input = strstr(input, "&Type="); + + if (Filter.Action == 'H' || Filter.Action == 'R') + { + Filter.Type = toupper(input[6]); + input = strstr(input, "&From="); + memcpy(Filter.From, &input[6], 10); + input = strstr(input, "&TO="); + strlop(Filter.From, '&'); + _strupr(Filter.From); + memcpy(Filter.TO, &input[4], 10); + input = strstr(input, "&AT="); + strlop(Filter.TO, '&'); + _strupr(Filter.TO); + memcpy(Filter.AT, &input[4], 10); + input = strstr(input, "&BID="); + strlop(Filter.AT, '&'); + _strupr(Filter.AT); + memcpy(Filter.BID, &input[5], 10); + input = strstr(input, "&MaxLen="); + strlop(Filter.BID, '&'); + _strupr(Filter.BID); + Filter.MaxLen = atoi(&input[8]); + + if (Filter.Type == '&') Filter.Type = '*'; + if (Filter.From[0] == 0) strcpy(Filter.From, "*"); + if (Filter.TO[0] == 0) strcpy(Filter.TO, "*"); + if (Filter.AT[0] == 0) strcpy(Filter.AT, "*"); + if (Filter.BID[0] == 0) strcpy(Filter.BID, "*"); + + // add to list + + PFilter = zalloc(sizeof(FBBFilter)); + + memcpy(PFilter, &Filter, sizeof(FBBFilter)); + + if (Filters == 0) + Filters = PFilter; + else + { + FBBFilter * p = Filters; + + while (p->Next) + p = p->Next; + + p->Next = PFilter; + } + } + + input = strstr(input, "&Action="); + } + SaveConfig(ConfigName); GetConfig(ConfigName); } @@ -2437,7 +2527,7 @@ VOID SendFwdDetails(struct UserInfo * User, char * Reply, int * ReplyLen, char * VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key) { - int Len; + int Len, i; char HF[2048] = ""; char HT[2048] = ""; @@ -2449,6 +2539,12 @@ VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key) char RB[2048] = ""; char WPTO[10000] = ""; + char FBBFilters[100000] = ""; + + + char * ptr = FBBFilters; + FBBFilter * Filter = Filters; + SetMultiStringValue(RejFrom, RF); SetMultiStringValue(RejTo, RT); SetMultiStringValue(RejAt, RA); @@ -2459,7 +2555,44 @@ VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key) SetMultiStringValue(HoldBID, HB); SetMultiStringValue(SendWPAddrs, WPTO); + // set up FB style fiters + ptr += sprintf(ptr, + ""); + + while(Filter) + { + ptr += sprintf(ptr, "" + "" + "" + "" + "" + "" + "" + "", + Filter->Action, Filter->Type, Filter->From, Filter->TO, Filter->AT, Filter->BID, Filter->MaxLen); + + Filter = Filter->Next; + } + + // Add a few blank entries for input + + for (i = 0; i < 5; i++) + { + ptr += sprintf(ptr, "" + "" + "" + "" + "" + "" + "" + "", ' ', ' ', "", "", "", "", 0); + } + + ptr += sprintf(ptr, "
ActionTypeFromTo@BBSBidMax Size
"); + + Debugprintf("%d", strlen(FBBFilters)); + Len = sprintf(Reply, ConfigTemplate, BBSName, Key, Key, Key, Key, Key, Key, Key, Key, Key, BBSName, SYSOPCall, HRoute, @@ -2490,7 +2623,7 @@ VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key) (SendWPType == 0) ? CHKD : UNC, (SendWPType == 1) ? CHKD : UNC, WPTO, - RF, RT, RA, RB, HF, HT, HA, HB); + RF, RT, RA, RB, HF, HT, HA, HB, FBBFilters); *ReplyLen = Len; } diff --git a/BBSUtilities.c b/BBSUtilities.c index 413a121..1d3c1f2 100644 --- a/BBSUtilities.c +++ b/BBSUtilities.c @@ -45,6 +45,7 @@ BOOL OpenMon; int reportNewMesageEvents = 0; +FBBFilter * Filters = NULL; extern struct ConsoleInfo BBSConsole; @@ -2078,10 +2079,37 @@ int CountConnectionsOnPort(int CheckPort) return Count; } +/* +REJECT.SYS (\FBB\SYSTEM). -BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type) + This file is in SYSTEM-directory. With this file it is possible to reject or +hold certain types or sizes of messages. + +The first letter of each valid line specifies the action : + +R = Reject : The message will not be received. +H = Hold : The message will be received but held until the sysop reviews. +L = Local Hold : Only messages created on this BBS will be held. + + # File for rejecting messages. They are rejected with N-BID: + # + # Type, from, @BBS, to, BID, maximum size: + # + # * and ? can be used as wildcards (as in MS-DOS) + # + R B TOTO ALL TATA * 0 + R B * * VENTE * 0 + R B * VENTE * * 0 + H * P1RAT * * * 0 + L B * * * * 0 + */ + + +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type, int Len) { char ** Calls; + FBBFilter * p = Filters; + char ToCopy[256]; if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) return TRUE; @@ -2145,6 +2173,43 @@ BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type Calls++; } } + + // check fbb reject.sys type filters + + strcpy(ToCopy, To); + _strupr(ToCopy); + + while (p) + { + if (p->Action != 'R') + goto Continue; + + if (p->Type != Type && p->Type != '*') + goto Continue; + + if (wildcardcompare(From, p->From) == 0) + goto Continue; + + if (wildcardcompare(ToCopy, p->TO) == 0) + goto Continue; + + if (ATBBS) + if (wildcardcompare(ATBBS, p->AT) == 0) + goto Continue; + + if (BID) + if (wildcardcompare(BID, p->BID) == 0) + goto Continue; + + if (p->MaxLen && Len < p->MaxLen) + goto Continue; + + return TRUE; // Hold + +Continue: + p = p->Next; + } + return FALSE; // Ok to accept } @@ -2175,9 +2240,12 @@ BOOL CheckValidCall(char * From) return FALSE; } -BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) +BOOL wildcardcompare(char * Target, char * Match); + +BOOL CheckHoldFilters(struct MsgInfo * Msg, char * From, char * To, char * ATBBS, char * BID) { char ** Calls; + FBBFilter * p = Filters; if (HoldFrom && From) { @@ -2238,6 +2306,38 @@ BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) Calls++; } } + + // check fbb reject.sys type filters + + while (p) + { + if (p->Action != 'H') + goto Continue; + + if (p->Type != Msg->type && p->Type != '*') + goto Continue; + + if (wildcardcompare(Msg->from, p->From) == 0) + goto Continue; + + if (wildcardcompare(Msg->to, p->TO) == 0) + goto Continue; + + if (wildcardcompare(Msg->via, p->AT) == 0) + goto Continue; + + if (wildcardcompare(Msg->bid, p->BID) == 0) + goto Continue; + + if (p->MaxLen && Msg->length < p->MaxLen) + goto Continue; + + return TRUE; // Hold + +Continue: + p = p->Next; + } + return FALSE; // Ok to accept } @@ -5361,7 +5461,7 @@ BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, cha } else { - if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType)) + if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType, 0)) { if ((conn->BBSFlags & BBS)) { @@ -6169,7 +6269,7 @@ nextline: HoldReason = "Bad word in title or body"; } - if (CheckHoldFilters(Msg->from, Msg->to, Msg->via, Msg->bid)) + if (CheckHoldFilters(Msg, Msg->from, Msg->to, Msg->via, Msg->bid)) { Msg->status = 'H'; HoldReason = "Matched Hold Filters"; @@ -9441,6 +9541,9 @@ VOID SaveConfig(char * ConfigName) char Size[80]; struct BBSForwardingInfo DummyForwardingInfo; char Line[1024]; + char FBBString[8192]= ""; + FBBFilter * p = Filters; + char * ptr = FBBString; if (configSaved == 0) { @@ -9566,6 +9669,18 @@ VOID SaveConfig(char * ConfigName) SaveMultiStringValue(group, "HoldAt", HoldAt); SaveMultiStringValue(group, "HoldBID", HoldBID); + // Save FBB Filters + + while (p) + { + ptr += sprintf(ptr, "%c|%c|%s|%s|%s|%s|%d|", + p->Action, p->Type, p->From, p->TO, p->AT, p->BID, p->MaxLen); + + p = p->Next; + } + + SaveStringValue(group, "FBBFilters", FBBString); + SaveIntValue(group, "SendWP", SendWP); SaveIntValue(group, "SendWPType", SendWPType); SaveIntValue(group, "FilterWPBulls", FilterWPBulls); @@ -9964,7 +10079,8 @@ BOOL GetConfig(char * ConfigName) char Size[80]; config_setting_t *setting; const char * ptr; - + char FBBString[8192]= ""; + FBBFilter f; config_init(&cfg); /* Read the file. If there is an error, report it and exit. */ @@ -10161,6 +10277,89 @@ BOOL GetConfig(char * ConfigName) HoldAt = GetMultiStringValue(group, "HoldAt"); HoldBID = GetMultiStringValue(group, "HoldBID"); + // Get FBB Filters + + GetStringValue(group, "FBBFilters", FBBString); + + ptr = FBBString; + + // delete old list + + while(Filters && Filters->Next) + { + FBBFilter * next = Filters->Next; + free(Filters); + Filters = next; + } + + free(Filters); + Filters = NULL; + + while (ptr && ptr[0]) + { + FBBFilter * PFilter; + + f.Action = ptr[0]; + f.Type = ptr[2]; + ptr = &ptr[4]; + + memcpy(f.From, ptr, 10); + strlop(f.From, '|'); + ptr = strlop(ptr, '|'); + + memcpy(f.TO, ptr, 10); + strlop(f.TO, '|'); + ptr = strlop(ptr, '|'); + + memcpy(f.AT, ptr, 10); + strlop(f.AT, '|'); + ptr = strlop(ptr, '|'); + + memcpy(f.BID, ptr, 10); + strlop(f.BID, '|'); + ptr = strlop(ptr, '|'); + + f.MaxLen = atoi(ptr); + + // add to list + + f.Next = 0; + + PFilter = zalloc(sizeof(FBBFilter)); + + memcpy(PFilter, &f, sizeof(FBBFilter)); + + if (Filters == 0) + Filters = PFilter; + else + { + FBBFilter * p = Filters; + + while (p->Next) + p = p->Next; + + p->Next = PFilter; + } + + ptr = strlop(ptr, '|'); + } + + + + + +//f.Action, f.Type, f.From, f.TO, f.AT, f.BID, &f.MaxLen); + +/* while (p) + { + ptr += sprintf(ptr, "%c|%c|%s|%s|%s|%s|%d|", + p->Action, p->Type, p->From, p->TO, p->AT, p->BID, p->MaxLen); + + p = p->Next; + } + +*/ + // Send WP Params SendWP = GetIntValue(group, "SendWP"); diff --git a/BBSUtilities.c.bak b/BBSUtilities.c.bak deleted file mode 100644 index c5c7e48..0000000 --- a/BBSUtilities.c.bak +++ /dev/null @@ -1,14926 +0,0 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Mail and Chat Server for BPQ32 Packet Switch -// -// Utility Routines - -#include "bpqmail.h" -#ifdef WIN32 -#include "Winspool.h" -#else -#include -#endif - - -BOOL Bells; -BOOL FlashOnBell; // Flash instead of Beep -BOOL StripLF; - -BOOL WarnWrap; -BOOL FlashOnConnect; -BOOL WrapInput; -BOOL CloseWindowOnBye; - -RECT ConsoleRect; - -BOOL OpenConsole; -BOOL OpenMon; - -int reportNewMesageEvents = 0; - - -extern struct ConsoleInfo BBSConsole; - -extern char LOC[7]; - -//#define BBSIDLETIME 120 -//#define USERIDLETIME 300 - - -#define BBSIDLETIME 900 -#define USERIDLETIME 900 - -#ifdef LINBPQ -extern BPQVECSTRUC ** BPQHOSTVECPTR; -UCHAR * GetLogDirectory(); -DllExport int APIENTRY SessionStateNoAck(int stream, int * state); -int RefreshWebMailIndex(); -#else -__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; -typedef char * (WINAPI FAR *FARPROCZ)(); -typedef int (WINAPI FAR *FARPROCX)(); -FARPROCZ pGetLOC; -FARPROCX pRefreshWebMailIndex; - -#endif - -Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); -VOID APIENTRY md5 (char *arg, unsigned char * checksum); -int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); -void GetSemaphore(struct SEM * Semaphore, int ID); -void FreeSemaphore(struct SEM * Semaphore); -int EncryptPass(char * Pass, char * Encrypt); -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); -void DeletetoRecycle(char * FN); -VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID TidyPrompts(); -char * ReadMessageFileEx(struct MsgInfo * MsgRec); -char * APIENTRY GetBPQDirectory(); -BOOL SendARQMail(CIRCUIT * conn); -int APIENTRY ChangeSessionIdletime(int Stream, int idletime); -int APIENTRY GetApplNum(int Stream); -VOID FormatTime(char * Time, time_t cTime); -BOOL CheckifPacket(char * Via); -char * APIENTRY GetVersionString(); -void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); -void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); -int GetCMSHash(char * Challenge, char * Password); -BOOL SendAMPRSMTP(CIRCUIT * conn); -VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); -VOID MCastTimer(); -VOID MCastConTimer(ConnectionInfo * conn); -int FindFreeBBSNumber(); -VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -BOOL ProcessYAPPMessage(CIRCUIT * conn); -void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); -void YAPPSendData(ConnectionInfo * conn); -VOID CheckBBSNumber(int i); -struct UserInfo * FindAMPR(); -VOID SaveInt64Value(config_setting_t * group, char * name, long long value); -VOID SaveIntValue(config_setting_t * group, char * name, int value); -VOID SaveStringValue(config_setting_t * group, char * name, char * value); -char *stristr (char *ch1, char *ch2); -BOOL CheckforMessagetoServer(struct MsgInfo * Msg); -void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); -void ListCategories(ConnectionInfo * conn); -void RebuildNNTPList(); -long long GetInt64Value(config_setting_t * group, char * name); -void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); -int ReformatSyncMessage(CIRCUIT * conn); -char * initMultipartUnpack(char ** Input); -char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg); -int decode_quoted_printable(char *ptr, int len); -void decodeblock( unsigned char in[4], unsigned char out[3]); -int encode_quoted_printable(char *s, char * out, int Len); -int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); -int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); - -config_t cfg; -config_setting_t * group; - -extern ULONG BBSApplMask; - -//static int SEMCLASHES = 0; - -char SecureMsg[80] = ""; // CMS Secure Signon Response - -int NumberofStreams; - -extern char VersionStringWithBuild[50]; - -#define MaxSockets 64 - -extern struct SEM OutputSEM; - -extern ConnectionInfo Connections[MaxSockets+1]; - -extern struct UserInfo ** UserRecPtr; -extern int NumberofUsers; - -extern struct UserInfo * BBSChain; // Chain of users that are BBSes - -extern struct MsgInfo ** MsgHddrPtr; -extern int NumberofMessages; - -extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search - -extern char UserDatabaseName[MAX_PATH]; -extern char UserDatabasePath[MAX_PATH]; - -extern char MsgDatabasePath[MAX_PATH]; -extern char MsgDatabaseName[MAX_PATH]; - -extern char BIDDatabasePath[MAX_PATH]; -extern char BIDDatabaseName[MAX_PATH]; - -extern char WPDatabasePath[MAX_PATH]; -extern char WPDatabaseName[MAX_PATH]; - -extern char BadWordsPath[MAX_PATH]; -extern char BadWordsName[MAX_PATH]; - -extern char BaseDir[MAX_PATH]; -extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% -extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat - - -extern char MailDir[MAX_PATH]; - -extern BIDRec ** BIDRecPtr; -extern int NumberofBIDs; - -extern BIDRec ** TempBIDRecPtr; -extern int NumberofTempBIDs; - -extern WPRec ** WPRecPtr; -extern int NumberofWPrecs; - -extern char ** BadWords; -extern int NumberofBadWords; -extern char * BadFile; - -extern int LatestMsg; -extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg -extern int HighestBBSNumber; - -extern int MaxMsgno; -extern int BidLifetime; -extern int MaxAge; -extern int MaintInterval; -extern int MaintTime; - -extern int ProgramErrors; - -extern BOOL MonBBS; -extern BOOL MonCHAT; -extern BOOL MonTCP; - -BOOL SendNewUserMessage = TRUE; -BOOL AllowAnon = FALSE; -BOOL UserCantKillT = FALSE; - -typedef int (WINAPI FAR *FARPROCX)(); -FARPROCX pRunEventProgram; - -int RunEventProgram(char * Program, char * Param); - - -extern BOOL EventsEnabled; - -#define BPQHOSTSTREAMS 64 - -// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 - -extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; - -#ifdef LINBPQ -extern BPQVECSTRUC ** BPQHOSTVECPTR; -extern char WL2KModes [54][18]; -#else -__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; - - -char WL2KModes [54][18] = { - "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", - "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 - "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 - "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 - "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 - "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; -#endif - - - - - -FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; - -time_t LastLogTime[4] = {0, 0, 0, 0}; - -char FilesNames[4][100] = {"", "", "", ""}; - -char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; - - -BOOL OpenLogfile(int Flags) -{ - UCHAR FN[MAX_PATH]; - time_t LT; - struct tm * tm; - - LT = time(NULL); - tm = gmtime(<); - - sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); - - LogHandle[Flags] = fopen(FN, "ab"); - -#ifndef WIN32 - - if (strcmp(FN, &FilesNames[Flags][0])) - { - UCHAR SYMLINK[MAX_PATH]; - - sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); - unlink(SYMLINK); - strcpy(&FilesNames[Flags][0], FN); - symlink(FN, SYMLINK); - } - -#endif - - return (LogHandle[Flags] != NULL); -} - -typedef int (WINAPI FAR *FARPROCX)(); - -extern FARPROCX pDllBPQTRACE; - -struct SEM LogSEM = {0, 0}; - -void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) -{ - char CRLF[2] = {0x0d,0x0a}; - struct tm * tm; - char Stamp[20]; - time_t LT; -// struct _EXCEPTION_POINTERS exinfo; - - // Write to Node BPQTRACE system - - if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) - { - MESSAGE Monframe; - memset(&Monframe, 0, sizeof(Monframe)); - - Monframe.PORT = 64; - Monframe.LENGTH = 12 + MsgLen; - Monframe.DEST[0] = 1; // Plain Text Monitor - - memcpy(&Monframe.DEST[1], Msg, MsgLen); - Monframe.DEST[1 + MsgLen] = 0; - - time(&Monframe.Timestamp); -#ifdef LINBPQ - GetSemaphore(&Semaphore, 88); - BPQTRACE(&Monframe, FALSE); - FreeSemaphore(&Semaphore); -#else - if (pDllBPQTRACE) - pDllBPQTRACE(&Monframe, FALSE); -#endif - } -#ifndef LINBPQ - __try - { -#endif - - - -#ifndef LINBPQ - - if (hMonitor) - { - if (Flags == LOG_TCP && MonTCP) - { - WritetoMonitorWindow((char *)&Flag, 1); - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_CHAT && MonCHAT) - { - WritetoMonitorWindow((char *)&Flag, 1); - - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - WritetoMonitorWindow(call, 10); - } - else - WritetoMonitorWindow(" ", 10); - - WritetoMonitorWindow(Msg, MsgLen); - if (Msg[MsgLen-1] != '\r') - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_BBS && MonBBS) - { - WritetoMonitorWindow((char *)&Flag, 1); - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - WritetoMonitorWindow(call, 10); - } - else - WritetoMonitorWindow(" ", 10); - - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_DEBUG_X) - { - WritetoMonitorWindow((char *)&Flag, 1); - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - } -#endif - - if (Flags == LOG_TCP && !LogTCP) - return; - if (Flags == LOG_BBS && !LogBBS) - return; - if (Flags == LOG_CHAT && !LogCHAT) - return; - - GetSemaphore(&LogSEM, 0); - - if (LogHandle[Flags] == NULL) - OpenLogfile(Flags); - - if (LogHandle[Flags] == NULL) - { - FreeSemaphore(&LogSEM); - return; - } - LT = time(NULL); - tm = gmtime(<); - - sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); - - fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); - - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - fwrite(call, 1, 10, LogHandle[Flags]); - } - else - fwrite(" ", 1, 10, LogHandle[Flags]); - - fwrite(Msg, 1, MsgLen, LogHandle[Flags]); - - if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') - fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); - else - fwrite(CRLF, 1, 2, LogHandle[Flags]); - - // Don't close/reopen logs every time - -// if ((LT - LastLogTime[Flags]) > 60) - { - LastLogTime[Flags] = LT; - fclose(LogHandle[Flags]); - LogHandle[Flags] = NULL; - } - FreeSemaphore(&LogSEM); - -#ifndef LINBPQ - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } -#endif -} - -int CriticalErrorHandler(char * error) -{ - Debugprintf("Critical Error %s", error); - ProgramErrors = 25; - CheckProgramErrors(); // Force close - return 0; -} - -BOOL CheckForTooManyErrors(ConnectionInfo * conn) -{ - conn->ErrorCount++; - - if (conn->ErrorCount > 4) - { - BBSputs(conn, "Too many errors - closing\r"); - conn->CloseAfterFlush = 20; - return TRUE; - } - return FALSE; -} - - - - - -VOID __cdecl Debugprintf(const char * format, ...) -{ - char Mess[16384]; - va_list(arglist); - int Len; - - va_start(arglist, format); - Len = vsprintf(Mess, format, arglist); -#ifndef LINBPQ - WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); -#endif - // #ifdef _DEBUG - strcat(Mess, "\r\n"); - OutputDebugString(Mess); - -// #endif - return; -} - -VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) -{ - char Mess[1000]; - va_list(arglist);int Len; - - va_start(arglist, format); - Len = vsprintf(Mess, format, arglist); - WriteLogLine(conn, InOut, Mess, Len, LogMode); - - return; -} - -struct MsgInfo * GetMsgFromNumber(int msgno) -{ - if (msgno < 1 || msgno > 999999) - return NULL; - - return MsgnotoMsg[msgno]; -} - -struct UserInfo * AllocateUserRecord(char * Call) -{ - struct UserInfo * User = zalloc(sizeof (struct UserInfo)); - - strcpy(User->Call, Call); - User->Length = sizeof (struct UserInfo); - - GetSemaphore(&AllocSemaphore, 0); - - UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); - UserRecPtr[NumberofUsers]= User; - - FreeSemaphore(&AllocSemaphore); - - return User; -} - -struct MsgInfo * AllocateMsgRecord() -{ - struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); - - GetSemaphore(&AllocSemaphore, 0); - - MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); - MsgHddrPtr[NumberofMessages] = Msg; - - FreeSemaphore(&AllocSemaphore); - - return Msg; -} - -BIDRec * AllocateBIDRecord() -{ - BIDRec * BID = zalloc(sizeof (BIDRec)); - - GetSemaphore(&AllocSemaphore, 0); - - BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); - BIDRecPtr[NumberofBIDs] = BID; - - FreeSemaphore(&AllocSemaphore); - - return BID; -} - -BIDRec * AllocateTempBIDRecord() -{ - BIDRec * BID = zalloc(sizeof (BIDRec)); - - GetSemaphore(&AllocSemaphore, 0); - - TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); - TempBIDRecPtr[NumberofTempBIDs] = BID; - - FreeSemaphore(&AllocSemaphore); - - return BID; -} - -struct UserInfo * LookupCall(char * Call) -{ - struct UserInfo * ptr = NULL; - int i; - - for (i=1; i <= NumberofUsers; i++) - { - ptr = UserRecPtr[i]; - - if (_stricmp(ptr->Call, Call) == 0) return ptr; - - } - - return NULL; -} - -int GetNetInt(char * Line) -{ - char temp[1024]; - char * ptr = strlop(Line, ','); - int n = atoi(Line); - if (ptr == NULL) - Line[0] = 0; - else - { - strcpy(temp, ptr); - strcpy(Line, temp); - } - return n; -} - -VOID GetUserDatabase() -{ - struct UserInfo UserRec; - - FILE * Handle; - size_t ReadLen; - struct UserInfo * user; - time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep - int i; - - // See if user config is in main config - - group = config_lookup (&cfg, "BBSUsers"); - - if (group) - { - // We have User config in the main config file. so use that - - int index = 0; - char * stats; - struct MsgStats * Stats; - char * ptr, * ptr2; - - config_setting_t * entry = config_setting_get_elem (group, index++); - - // Initialise a new File - - UserRecPtr = malloc(sizeof(void *)); - UserRecPtr[0] = malloc(sizeof (struct UserInfo)); - memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); - UserRecPtr[0]->Length = sizeof (struct UserInfo); - - NumberofUsers = 0; - - while (entry) - { - char call[16]; - - // entry->name is call, will have * in front if a call stating woth number - - if (entry->name[0] == '*') - strcpy(call, &entry->name[1]); - else - strcpy(call, entry->name); - - user = AllocateUserRecord(call); - - ptr = entry->value.sval; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->Name, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->Address, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->HomeBBS, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->QRA, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->pass, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->ZIP, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->CMSPass, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->lastmsg = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->flags = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->PageLen = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->BBSNumber = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->RMSSSIDBits = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->WebSeqNo = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->TimeLastConnected = atol(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) Stats = &user->Total; - stats = ptr; - - if (Stats == NULL) - { - NumberofUsers--; - free(user); - entry = config_setting_get_elem (group, index++); - continue; - } - - Stats->ConnectsIn = GetNetInt(stats); - Stats->ConnectsOut = GetNetInt(stats); - Stats->MsgsReceived[0] = GetNetInt(stats); - Stats->MsgsReceived[1] = GetNetInt(stats); - Stats->MsgsReceived[2] = GetNetInt(stats); - Stats->MsgsReceived[3] = GetNetInt(stats); - Stats->MsgsSent[0] = GetNetInt(stats); - Stats->MsgsSent[1] = GetNetInt(stats); - Stats->MsgsSent[2] = GetNetInt(stats); - Stats->MsgsSent[3] = GetNetInt(stats); - Stats->MsgsRejectedIn[0] = GetNetInt(stats); - Stats->MsgsRejectedIn[1] = GetNetInt(stats); - Stats->MsgsRejectedIn[2] = GetNetInt(stats); - Stats->MsgsRejectedIn[3] = GetNetInt(stats); - Stats->MsgsRejectedOut[0] = GetNetInt(stats); - Stats->MsgsRejectedOut[1] = GetNetInt(stats); - Stats->MsgsRejectedOut[2] = GetNetInt(stats); - Stats->MsgsRejectedOut[3] = GetNetInt(stats); - Stats->BytesForwardedIn[0] = GetNetInt(stats); - Stats->BytesForwardedIn[1] = GetNetInt(stats); - Stats->BytesForwardedIn[2] = GetNetInt(stats); - Stats->BytesForwardedIn[3] = GetNetInt(stats); - Stats->BytesForwardedOut[0] = GetNetInt(stats); - Stats->BytesForwardedOut[1] = GetNetInt(stats); - Stats->BytesForwardedOut[2] = GetNetInt(stats); - Stats->BytesForwardedOut[3] = GetNetInt(stats); - - Stats = &user->Last; - stats = ptr2; - - if (Stats == NULL) - { - NumberofUsers--; - free(user); - entry = config_setting_get_elem (group, index++); - continue; - } - - Stats->ConnectsIn = GetNetInt(stats); - Stats->ConnectsOut = GetNetInt(stats); - Stats->MsgsReceived[0] = GetNetInt(stats); - Stats->MsgsReceived[1] = GetNetInt(stats); - Stats->MsgsReceived[2] = GetNetInt(stats); - Stats->MsgsReceived[3] = GetNetInt(stats); - Stats->MsgsSent[0] = GetNetInt(stats); - Stats->MsgsSent[1] = GetNetInt(stats); - Stats->MsgsSent[2] = GetNetInt(stats); - Stats->MsgsSent[3] = GetNetInt(stats); - Stats->MsgsRejectedIn[0] = GetNetInt(stats); - Stats->MsgsRejectedIn[1] = GetNetInt(stats); - Stats->MsgsRejectedIn[2] = GetNetInt(stats); - Stats->MsgsRejectedIn[3] = GetNetInt(stats); - Stats->MsgsRejectedOut[0] = GetNetInt(stats); - Stats->MsgsRejectedOut[1] = GetNetInt(stats); - Stats->MsgsRejectedOut[2] = GetNetInt(stats); - Stats->MsgsRejectedOut[3] = GetNetInt(stats); - Stats->BytesForwardedIn[0] = GetNetInt(stats); - Stats->BytesForwardedIn[1] = GetNetInt(stats); - Stats->BytesForwardedIn[2] = GetNetInt(stats); - Stats->BytesForwardedIn[3] = GetNetInt(stats); - Stats->BytesForwardedOut[0] = GetNetInt(stats); - Stats->BytesForwardedOut[1] = GetNetInt(stats); - Stats->BytesForwardedOut[2] = GetNetInt(stats); - Stats->BytesForwardedOut[3] = GetNetInt(stats); - - - if ((user->flags & F_BBS) == 0) // Not BBS - Check Age - { - if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected - { - if (user->TimeLastConnected < UserLimit) - { - // Too Old - ignore - - NumberofUsers--; - free(user); - entry = config_setting_get_elem (group, index++); - continue; - } - } - } - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - if (user->lastmsg < 0 || user->lastmsg > LatestMsg) - user->lastmsg = LatestMsg; - - - entry = config_setting_get_elem (group, index++); - } - } - else - { - Handle = fopen(UserDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - UserRecPtr=malloc(sizeof(void *)); - UserRecPtr[0]= malloc(sizeof (struct UserInfo)); - memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); - UserRecPtr[0]->Length = sizeof (struct UserInfo); - - NumberofUsers = 0; - - return; - } - - - // Get First Record - - ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&UserRec, 0, sizeof (struct UserInfo)); - UserRec.Length = sizeof (struct UserInfo); - } - else - { - // See if format has changed - - if (UserRec.Length == 0) - { - // Old format without a Length field - - struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; - int Users = OldRec->ConnectsIn; // User Count in control record - char Backup1[MAX_PATH]; - - // Create a backup in case reversion is needed and Reposition to first User record - - fclose(Handle); - - strcpy(Backup1, UserDatabasePath); - strcat(Backup1, ".oldformat"); - - CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak - - Handle = fopen(UserDatabasePath, "rb"); - - ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record - - // Set up control record - - UserRecPtr=malloc(sizeof(void *)); - UserRecPtr[0]= malloc(sizeof (struct UserInfo)); - memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); - UserRecPtr[0]->Length = sizeof (UserRec); - - NumberofUsers = 0; - -OldNext: - - ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); - - if (ReadLen > 0) - { - if (OldRec->Call[0] < '0') - goto OldNext; // Blank record - - user = AllocateUserRecord(OldRec->Call); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - // Copy info from Old record - - user->lastmsg = OldRec->lastmsg; - user->Total.ConnectsIn = OldRec->ConnectsIn; - user->TimeLastConnected = OldRec->TimeLastConnected; - user->flags = OldRec->flags; - user->PageLen = OldRec->PageLen; - user->BBSNumber = OldRec->BBSNumber; - memcpy(user->Name, OldRec->Name, 18); - memcpy(user->Address, OldRec->Address, 61); - user->Total.MsgsReceived[0] = OldRec->MsgsReceived; - user->Total.MsgsSent[0] = OldRec->MsgsSent; - user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject - user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end - user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; - user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; - user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out - user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS - memcpy(user->HomeBBS, OldRec->HomeBBS, 41); - memcpy(user->QRA, OldRec->QRA, 7); - memcpy(user->pass, OldRec->pass, 13); - memcpy(user->ZIP, OldRec->ZIP, 9); - - // Read any forwarding info, even if not a BBS. - // This allows a BBS to be temporarily set as a - // normal user without loosing forwarding info - - SetupForwardingStruct(user); - - if (user->flags & F_BBS) - { - // Defined as BBS - allocate and initialise forwarding structure - - // Add to BBS Chain; - - user->BBSNext = BBSChain; - BBSChain = user; - - // Save Highest BBS Number - - if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; - } - goto OldNext; - } - - SortBBSChain(); - fclose(Handle); - - return; - } - } - - // Set up control record - - UserRecPtr=malloc(sizeof(void *)); - UserRecPtr[0]= malloc(sizeof (struct UserInfo)); - memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); - UserRecPtr[0]->Length = sizeof (UserRec); - - NumberofUsers = 0; - -Next: - - ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); - - if (ReadLen > 0) - { - if (UserRec.Call[0] < '0') - goto Next; // Blank record - - if (UserRec.TimeLastConnected == 0) - UserRec.TimeLastConnected = UserRec.xTimeLastConnected; - - if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age - if (UserLifetime) // if limit set - if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected - if (UserRec.TimeLastConnected < UserLimit) - goto Next; // Too Old - ignore - - user = AllocateUserRecord(UserRec.Call); - memcpy(user, &UserRec, sizeof (UserRec)); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - user->ForwardingInfo = NULL; // In case left behind on crash - user->BBSNext = NULL; - user->POP3Locked = FALSE; - - if (user->lastmsg < 0 || user->lastmsg > LatestMsg) - user->lastmsg = LatestMsg; - - goto Next; - } - fclose(Handle); - } - - // Setting up BBS struct has been moved until all user record - // have been read so we can fix corrupt BBSNUmber - - for (i=1; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - // Read any forwarding info, even if not a BBS. - // This allows a BBS to be temporarily set as a - // normal user without loosing forwarding info - - SetupForwardingStruct(user); - - if (user->flags & F_BBS) - { - // Add to BBS Chain; - - if (user->BBSNumber == NBBBS) // Fix corrupt records - { - user->BBSNumber = FindFreeBBSNumber(); - if (user->BBSNumber == 0) - user->BBSNumber = NBBBS; // cant really do much else - } - - user->BBSNext = BBSChain; - BBSChain = user; - -// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); - - // Save Highest BBS Number - - if (user->BBSNumber > HighestBBSNumber) - HighestBBSNumber = user->BBSNumber; - } - } - - // Check for dulicate BBS numbers - - for (i=1; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - if (user->flags & F_BBS) - { - if (user->BBSNumber == 0) - user->BBSNumber = FindFreeBBSNumber(); - - CheckBBSNumber(user->BBSNumber); - } - } - - SortBBSChain(); -} - -VOID CopyUserDatabase() -{ - return; // User config now in main config file -/* - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - - // Keep 4 Generations - - strcpy(Backup2, UserDatabasePath); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, UserDatabasePath); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, UserDatabasePath); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, UserDatabasePath); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); //Move .bak to .bak.1 - - CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak -*/ -} - -VOID CopyConfigFile(char * ConfigName) -{ - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - - // Keep 4 Generations - - strcpy(Backup2, ConfigName); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, ConfigName); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, ConfigName); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, ConfigName); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); // Move .bak to .bak.1 - - CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak -} - - - -VOID SaveUserDatabase() -{ - SaveConfig(ConfigName); // User config is now in main config file - GetConfig(ConfigName); - -/* - FILE * Handle; - size_t WriteLen; - int i; - - Handle = fopen(UserDatabasePath, "wb"); - - UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; - - for (i=0; i <= NumberofUsers; i++) - { - WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); - } - - fclose(Handle); -*/ - return; -} - -VOID GetMessageDatabase() -{ - struct MsgInfo MsgRec; - FILE * Handle; - size_t ReadLen; - struct MsgInfo * Msg; - char * MsgBytes; - int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating - BOOL Reformatting = FALSE; - char HEX[3] = ""; - int n; - - // See if Message Database is in main config - - group = config_lookup (&cfg, "MSGS"); - -// group = 0; - - if (group) - { - // We have User config in the main config file. so use that - - int index = 0; - char * ptr, * ptr2; - config_setting_t * entry = config_setting_get_elem (group, index++); - - // Initialise a new File - - MsgHddrPtr=malloc(sizeof(void *)); - MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); - NumberofMessages = 0; - MsgHddrPtr[0]->status = 2; - - if (entry) - { - // First Record has current message number - - ptr = entry->value.sval; - ptr2 = strlop(ptr, '|'); - ptr2 = strlop(ptr2, '|'); - if (ptr2) - LatestMsg = atoi(ptr2); - } - - entry = config_setting_get_elem (group, index++); - - while (entry) - { - // entry->name is MsgNo with 'R' in front - - ptr = entry->value.sval; - ptr2 = strlop(ptr, '|'); - - memset(&MsgRec, 0, sizeof(struct MsgInfo)); - - MsgRec.number = atoi(&entry->name[1]); - MsgRec.type = ptr[0]; - - ptr = ptr2; - - if (ptr == NULL) - { - entry = config_setting_get_elem (group, index++); - continue; - } - - ptr2 = strlop(ptr, '|'); - MsgRec.status = ptr[0]; - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.length = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.datereceived = atol(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.bbsfrom, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.via, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.from, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.to, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.bid, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.B2Flags = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.datecreated = atol(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.datechanged = atol(ptr); - - ptr = ptr2; - if (ptr) ptr2 = strlop(ptr, '|'); - - if (ptr == NULL) - { - entry = config_setting_get_elem (group, index++); - continue; - } - - if (ptr[0]) - { - char String[50] = "00000000000000000000"; - String[20] = 0; - memcpy(String, ptr, strlen(ptr)); - for (n = 0; n < NBMASK; n++) - { - memcpy(HEX, &String[n * 2], 2); - MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); - } - } - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - - if (ptr == NULL) - { - entry = config_setting_get_elem (group, index++); - continue; - } - - if (ptr[0]) - { - char String[50] = "00000000000000000000"; - String[20] = 0; - memcpy(String, ptr, strlen(ptr)); - for (n = 0; n < NBMASK; n++) - { - memcpy(HEX, &String[n * 2], 2); - MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); - } - } - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.emailfrom, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.UTF8 = atoi(ptr); - - ptr = ptr2; - - if (ptr) - { - strcpy(MsgRec.title, ptr); - - MsgBytes = ReadMessageFileEx(&MsgRec); - - if (MsgBytes) - { - free(MsgBytes); - Msg = AllocateMsgRecord(); - memcpy(Msg, &MsgRec, sizeof (MsgRec)); - - MsgnotoMsg[Msg->number] = Msg; - - // Fix Corrupted NTS Messages - - if (Msg->type == 'N') - Msg->type = 'T'; - - // Look for corrupt FROM address (ending in @) - - strlop(Msg->from, '@'); - - BuildNNTPList(Msg); // Build NNTP Groups list - - // If any forward bits are set, increment count on corresponding BBS record. - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - } - } - } - entry = config_setting_get_elem (group, index++); - } - - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - - return; - } - - Handle = fopen(MsgDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - MsgHddrPtr=malloc(sizeof(void *)); - MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); - NumberofMessages = 0; - MsgHddrPtr[0]->status = 2; - - return; - } - - // Get First Record - - ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&MsgRec, 0, sizeof (MsgRec)); - MsgRec.status = 2; - } - - // Set up control record - - MsgHddrPtr=malloc(sizeof(void *)); - MsgHddrPtr[0]= malloc(sizeof (MsgRec)); - memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); - - LatestMsg=MsgHddrPtr[0]->length; - - NumberofMessages = 0; - - if (MsgRec.status == 1) // Used as file format version - // 0 = original, 1 = Extra email from addr, 2 = More BBS's. - { - char Backup1[MAX_PATH]; - - // Create a backup in case reversion is needed and Reposition to first User record - - fclose(Handle); - - strcpy(Backup1, MsgDatabasePath); - strcat(Backup1, ".oldformat"); - - CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat - - Handle = fopen(MsgDatabasePath, "rb"); - - FileRecsize = sizeof(struct OldMsgInfo); - - ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); - - MsgHddrPtr[0]->status = 2; - } - -Next: - - ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); - - if (ReadLen > 0) - { - // Validate Header - - if (FileRecsize == sizeof(struct MsgInfo)) - { - if (MsgRec.type == 0 || MsgRec.number == 0) - goto Next; - - MsgBytes = ReadMessageFileEx(&MsgRec); - - if (MsgBytes) - { - // MsgRec.length = strlen(MsgBytes); - free(MsgBytes); - } - else - goto Next; - - Msg = AllocateMsgRecord(); - - memcpy(Msg, &MsgRec, +sizeof (MsgRec)); - } - else - { - // Resizing - record from file is an OldRecInfo - - struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; - - if (OldMessage->type == 0) - goto Next; - - if (OldMessage->number > 99999 || OldMessage->number < 1) - goto Next; - - Msg = AllocateMsgRecord(); - - - Msg->B2Flags = OldMessage->B2Flags; - memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); - memcpy(Msg->bid, OldMessage->bid, 13); - Msg->datechanged = OldMessage->datechanged; - Msg->datecreated = OldMessage->datecreated; - Msg->datereceived = OldMessage->datereceived; - memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); - memcpy(Msg->fbbs , OldMessage->fbbs, 10); - memcpy(Msg->forw , OldMessage->forw, 10); - memcpy(Msg->from, OldMessage->from, 7); - Msg->length = OldMessage->length; - Msg->nntpnum = OldMessage->nntpnum; - Msg->number = OldMessage->number; - Msg->status = OldMessage->status; - memcpy(Msg->title, OldMessage->title, 61); - memcpy(Msg->to, OldMessage->to, 7); - Msg->type = OldMessage->type; - memcpy(Msg->via, OldMessage->via, 41); - } - - MsgnotoMsg[Msg->number] = Msg; - - // Fix Corrupted NTS Messages - - if (Msg->type == 'N') - Msg->type = 'T'; - - // Look for corrupt FROM address (ending in @) - - strlop(Msg->from, '@'); - - // Move Dates if first run with new format - - if (Msg->datecreated == 0) - Msg->datecreated = Msg->xdatecreated; - - if (Msg->datereceived == 0) - Msg->datereceived = Msg->xdatereceived; - - if (Msg->datechanged == 0) - Msg->datechanged = Msg->xdatechanged; - - BuildNNTPList(Msg); // Build NNTP Groups list - - Msg->Locked = 0; // In case left locked - Msg->Defered = 0; // In case left set. - - // If any forward bits are set, increment count on corresponding BBS record. - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - } - - goto Next; - } - - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - - fclose(Handle); -} - -VOID CopyMessageDatabase() -{ - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - -// return; - - // Keep 4 Generations - - strcpy(Backup2, MsgDatabasePath); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, MsgDatabasePath); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, MsgDatabasePath); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, MsgDatabasePath); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); //Move .bak to .bak.1 - - strcpy(Backup2, MsgDatabasePath); - strcat(Backup2, ".bak"); - - CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak - -} - -VOID SaveMessageDatabase() -{ - FILE * Handle; - size_t WriteLen; - int i; - char Key[16]; - struct MsgInfo *Msg; -// char CfgName[MAX_PATH]; - char HEXString1[64]; - char HEXString2[64]; - int n; -// char * CfgBuffer; - char Cfg[1024]; -// int CfgLen = 0; -// FILE * hFile; - -// SaveConfig(ConfigName); // Message Headers now in main config -// return; - -#ifdef LINBPQ - RefreshWebMailIndex(); -#else - if (pRefreshWebMailIndex) - pRefreshWebMailIndex(); -#endif - - Handle = fopen(MsgDatabasePath, "wb"); - - if (Handle == NULL) - { - CriticalErrorHandler("Failed to open message database"); - return; - } - - MsgHddrPtr[0]->status = 2; - MsgHddrPtr[0]->number = NumberofMessages; - MsgHddrPtr[0]->length = LatestMsg; - - for (i=0; i <= NumberofMessages; i++) - { - WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); - - if (WriteLen != sizeof(struct MsgInfo)) - { - CriticalErrorHandler("Failed to write message database record"); - return; - } - } - - if (fclose(Handle) != 0) - CriticalErrorHandler("Failed to close message database"); - - for (i = 1; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); - - n = 39; - while (n >=0 && HEXString1[n] == '0') - HEXString1[n--] = 0; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); - - n = 39; - while (n >= 0 && HEXString2[n] == '0') - HEXString2[n--] = 0; - - sprintf(Key, "R%d:\r\n", i); - - n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, - Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], - &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, - &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); - } - - return; -} - -VOID GetBIDDatabase() -{ - BIDRec BIDRec; - FILE * Handle; - size_t ReadLen; - BIDRecP BID; - int index = 0; - char * ptr, * ptr2; - - // If BID info is in main config file, use it - - group = config_lookup (&cfg, "BIDS"); - - if (group) - { - config_setting_t * entry = config_setting_get_elem (group, index++); - - BIDRecPtr=malloc(sizeof(void *)); - BIDRecPtr[0]= malloc(sizeof (BIDRec)); - memset(BIDRecPtr[0], 0, sizeof (BIDRec)); - NumberofBIDs = 0; - - while (entry) - { - // entry->name is Bid with 'R' in front - - ptr = entry->value.sval; - ptr2 = strlop(ptr, '|'); - - if (ptr && ptr2) - { - BID = AllocateBIDRecord(); - strcpy(BID->BID, &entry->name[1]); - BID->mode = atoi(ptr); - BID->u.timestamp = atoi(ptr2); - - if (BID->u.timestamp == 0) - BID->u.timestamp = LOWORD(time(NULL)/86400); - - } - entry = config_setting_get_elem (group, index++); - } - return; - } - - Handle = fopen(BIDDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - BIDRecPtr=malloc(sizeof(void *)); - BIDRecPtr[0]= malloc(sizeof (BIDRec)); - memset(BIDRecPtr[0], 0, sizeof (BIDRec)); - NumberofBIDs = 0; - - return; - } - - - // Get First Record - - ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&BIDRec, 0, sizeof (BIDRec)); - } - - // Set up control record - - BIDRecPtr = malloc(sizeof(void *)); - BIDRecPtr[0] = malloc(sizeof (BIDRec)); - memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); - - NumberofBIDs = 0; - -Next: - - ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); - - if (ReadLen > 0) - { - BID = AllocateBIDRecord(); - memcpy(BID, &BIDRec, sizeof (BIDRec)); - - if (BID->u.timestamp == 0) - BID->u.timestamp = LOWORD(time(NULL)/86400); - - goto Next; - } - - fclose(Handle); -} - -VOID CopyBIDDatabase() -{ - char Backup[MAX_PATH]; - -// return; - - - strcpy(Backup, BIDDatabasePath); - strcat(Backup, ".bak"); - - CopyFile(BIDDatabasePath, Backup, FALSE); -} - -VOID SaveBIDDatabase() -{ - FILE * Handle; - size_t WriteLen; - int i; - -// return; // Bids are now in main config and are saved when message is saved - - Handle = fopen(BIDDatabasePath, "wb"); - - BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size - - for (i=0; i <= NumberofBIDs; i++) - { - WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); - } - - fclose(Handle); - - return; -} - -BIDRec * LookupBID(char * BID) -{ - BIDRec * ptr = NULL; - int i; - - for (i=1; i <= NumberofBIDs; i++) - { - ptr = BIDRecPtr[i]; - - if (_stricmp(ptr->BID, BID) == 0) - return ptr; - } - - return NULL; -} - -BIDRec * LookupTempBID(char * BID) -{ - BIDRec * ptr = NULL; - int i; - - for (i=1; i <= NumberofTempBIDs; i++) - { - ptr = TempBIDRecPtr[i]; - - if (_stricmp(ptr->BID, BID) == 0) return ptr; - } - - return NULL; -} - -VOID RemoveTempBIDS(CIRCUIT * conn) -{ - // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed - - if (NumberofTempBIDs == 0) - return; - else - { - BIDRec * ptr = NULL; - BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); - int i = 0, n; - - GetSemaphore(&AllocSemaphore, 0); - - for (n = 1; n <= NumberofTempBIDs; n++) - { - ptr = TempBIDRecPtr[n]; - - if (ptr) - { - if (ptr->u.conn == conn) - // Remove this entry - free(ptr); - else - NewTempBIDRecPtr[++i] = ptr; - } - } - - NumberofTempBIDs = i; - - free(TempBIDRecPtr); - - TempBIDRecPtr = NewTempBIDRecPtr; - FreeSemaphore(&AllocSemaphore); - } - -} - -VOID GetBadWordFile() -{ - FILE * Handle; - DWORD FileSize; - char * ptr1, * ptr2; - struct stat STAT; - - if (stat(BadWordsPath, &STAT) == -1) - return; - - FileSize = STAT.st_size; - - Handle = fopen(BadWordsPath, "rb"); - - if (Handle == NULL) - return; - - // Release old info in case a re-read - - if (BadWords) free(BadWords); - if (BadFile) free(BadFile); - - BadWords = NULL; - BadFile = NULL; - NumberofBadWords = 0; - - BadFile = malloc(FileSize+1); - - fread(BadFile, 1, FileSize, Handle); - - fclose(Handle); - - BadFile[FileSize]=0; - - _strlwr(BadFile); // Compares are case-insensitive - - ptr1 = BadFile; - - while (ptr1) - { - if (*ptr1 == '\n') ptr1++; - - ptr2 = strtok_s(NULL, "\r\n", &ptr1); - if (ptr2) - { - if (*ptr2 != '#') - { - BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); - BadWords[NumberofBadWords] = ptr2; - } - } - else - break; - } -} - -BOOL CheckBadWord(char * Word, char * Msg) -{ - char * ptr1 = Msg, * ptr2; - size_t len = strlen(Word); - - while (*ptr1) // Stop at end - { - ptr2 = strstr(ptr1, Word); - - if (ptr2 == NULL) - return FALSE; // OK - - // Only bad if it ia not part of a longer word - - if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before - if (!(isalpha(*(ptr2 + len)))) // No alpha after - return TRUE; // Bad word - - // Keep searching - - ptr1 = ptr2 + len; - } - - return FALSE; // OK -} - -BOOL CheckBadWords(char * Msg) -{ - char * dupMsg = _strlwr(_strdup(Msg)); - int i; - - for (i = 1; i <= NumberofBadWords; i++) - { - if (CheckBadWord(BadWords[i], dupMsg)) - { - free(dupMsg); - return TRUE; // Bad - } - } - - free(dupMsg); - return FALSE; // OK - -} - -VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) -{ - if (user->flags & F_Expert) - ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); - else if (conn->NewUser) - ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); - else - ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); - - if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) - BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); - -// if (user->flags & F_Temp_B2_BBS) -// nodeprintf(conn, "%s CMS >\r", BBSName); -// else - SendPrompt(conn, user); -} - -VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) -{ - if (user->Temp->ListSuspended) - return; // Dont send prompt if pausing a listing - - if (user->flags & F_Expert) - ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); - else if (conn->NewUser) - ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); - else - ExpandAndSendMessage(conn, Prompt, LOG_BBS); - -// if (user->flags & F_Expert) -// nodeprintf(conn, "%s\r", ExpertPrompt); -// else if (conn->NewUser) -// nodeprintf(conn, "%s\r", NewPrompt); -// else -// nodeprintf(conn, "%s\r", Prompt); -} - - - -VOID * _zalloc(size_t len) -{ - // ?? malloc and clear - - void * ptr; - - ptr=malloc(len); - memset(ptr, 0, len); - - return ptr; -} - -BOOL isAMPRMsg(char * Addr) -{ - // See if message is addressed to ampr.org and is either - // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) - - size_t toLen = strlen(Addr); - - if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) - { - // message is for ampr.org - - char toCall[48]; - char * via; - - strcpy(toCall, _strupr(Addr)); - - via = strlop(toCall, '@'); - - if (_stricmp(via, AMPRDomain) == 0) - { - // message is for us. - - return TRUE; - } - - if (SendAMPRDirect) - { - // We want to send ampr mail direct to host. Queue to BBS AMPR - - if (FindAMPR()) - { - // We have bbs AMPR - - return TRUE; - } - } - } - return FALSE; -} - -struct UserInfo * FindAMPR() -{ - struct UserInfo * bbs; - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - if (strcmp(bbs->Call, "AMPR") == 0) - return bbs; - } - - return NULL; -} - -struct UserInfo * FindRMS() -{ - struct UserInfo * bbs; - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - if (strcmp(bbs->Call, "RMS") == 0) - return bbs; - } - - return NULL; -} - -struct UserInfo * FindBBS(char * Name) -{ - struct UserInfo * bbs; - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - if (strcmp(bbs->Call, Name) == 0) - return bbs; - } - - return NULL; -} - -int CountConnectionsOnPort(int CheckPort) -{ - int n, Count = 0; - CIRCUIT * conn; - int port, sesstype, paclen, maxframe, l4window; - char callsign[11]; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (conn->Active) - { - GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); - if (port == CheckPort) - Count++; - } - } - - return Count; -} - - -BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type) -{ - char ** Calls; - - if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) - return TRUE; - - if (RejFrom && From) - { - Calls = RejFrom; - - while(Calls[0]) - { - if (_stricmp(Calls[0], From) == 0) - return TRUE; - - Calls++; - } - } - - if (RejTo && To) - { - Calls = RejTo; - - while(Calls[0]) - { - if (_stricmp(Calls[0], To) == 0) - return TRUE; - - Calls++; - } - } - - if (RejAt && ATBBS) - { - Calls = RejAt; - - while(Calls[0]) - { - if (_stricmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - - if (RejBID && BID) - { - Calls = RejBID; - - while(Calls[0]) - { - if (Calls[0][0] == '*') - { - if (stristr(BID, &Calls[0][1])) - return TRUE; - } - else - { - if (_stricmp(BID, Calls[0]) == 0) - return TRUE; - } - - Calls++; - } - } - return FALSE; // Ok to accept -} - -BOOL CheckValidCall(char * From) -{ - unsigned int i; - - if (DontCheckFromCall) - return TRUE; - - if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || - strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) - return TRUE; - - for (i = 1; i < strlen(From); i++) // skip first which may also be digit - { - if (isdigit(From[i])) - { - // Has a digit. Check Last is not digit - - if (isalpha(From[strlen(From) - 1])) - return TRUE; - } - } - - // No digit, return false - - return FALSE; -} - -BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) -{ - char ** Calls; - - if (HoldFrom && From) - { - Calls = HoldFrom; - - while(Calls[0]) - { - if (_stricmp(Calls[0], From) == 0) - return TRUE; - - Calls++; - } - } - - if (HoldTo && To) - { - Calls = HoldTo; - - while(Calls[0]) - { - if (_stricmp(Calls[0], To) == 0) - return TRUE; - - Calls++; - } - } - - if (HoldAt && ATBBS) - { - Calls = HoldAt; - - while(Calls[0]) - { - if (_stricmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - - if (HoldBID && BID) - { - Calls = HoldBID; - - while(Calls[0]) - { - if (Calls[0][0] == '*') - { - if (stristr(BID, &Calls[0][1])) - return TRUE; - } - else - { - if (_stricmp(BID, Calls[0]) == 0) - return TRUE; - } - - Calls++; - } - } - return FALSE; // Ok to accept -} - -BOOL CheckifLocalRMSUser(char * FullTo) -{ - struct UserInfo * user = LookupCall(FullTo); - - if (user) - if (user->flags & F_POLLRMS) - return TRUE; - - return FALSE; - -} - - - -int check_fwd_bit(char *mask, int bbsnumber) -{ - if (bbsnumber) - return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); - else - return 0; -} - - -void set_fwd_bit(char *mask, int bbsnumber) -{ - if (bbsnumber) - mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); -} - - -void clear_fwd_bit (char *mask, int bbsnumber) -{ - if (bbsnumber) - mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); -} - -VOID BBSputs(CIRCUIT * conn, char * buf) -{ - // Sends to user and logs - - WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); - - QueueMsg(conn, buf, (int)strlen(buf)); -} - -VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) -{ - char Mess[1000]; - int len; - va_list(arglist); - - - va_start(arglist, format); - len = vsprintf(Mess, format, arglist); - - QueueMsg(conn, Mess, len); - - WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); - - return; -} - -// nodeprintfEx add a LF if NEEFLF is set - -VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) -{ - char Mess[1000]; - int len; - va_list(arglist); - - - va_start(arglist, format); - len = vsprintf(Mess, format, arglist); - - QueueMsg(conn, Mess, len); - - WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); - - if (conn->BBSFlags & NEEDLF) - QueueMsg(conn, "\r", 1); - - return; -} - - -int compare( const void *arg1, const void *arg2 ); - -VOID SortBBSChain() -{ - struct UserInfo * user; - struct UserInfo * users[161]; - int i = 0, n; - - // Get array of addresses - - for (user = BBSChain; user; user = user->BBSNext) - { - users[i++] = user; - if (i > 160) break; - } - - qsort((void *)users, i, sizeof(void *), compare ); - - BBSChain = NULL; - - // Rechain (backwards, as entries ate put on front of chain) - - for (n = i-1; n >= 0; n--) - { - users[n]->BBSNext = BBSChain; - BBSChain = users[n]; - } -} - -int compare(const void *arg1, const void *arg2) -{ - // Compare Calls. Fortunately call is at start of stuct - - return _stricmp(*(char**)arg1 , *(char**)arg2); -} - -int CountMessagesTo(struct UserInfo * user, int * Unread) -{ - int i, Msgs = 0; - UCHAR * Call = user->Call; - - *Unread = 0; - - for (i = NumberofMessages; i > 0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) - { - Msgs++; - if (MsgHddrPtr[i]->status == 'N') - *Unread = *Unread + 1; - } - } - return(Msgs); -} - - - -// Custimised message handling routines. -/* - Variables - a subset of those used by FBB - - $C : Number of the next message. - $I : First name of the connected user. - $L : Number of the latest message. - $N : Number of active messages - $U : Callsign of the connected user. - $W : Inserts a carriage return. - $Z : Last message read by the user (L command). - %X : Number of messages for the user. - %x : Number of new messages for the user. -*/ - -VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) -{ - char NewMessage[10000]; - char * OldP = Msg; - char * NewP = NewMessage; - char * ptr, * pptr; - size_t len; - char Dollar[] = "$"; - char CR[] = "\r"; - char num[20]; - int Msgs = 0, Unread = 0; - - ptr = strchr(OldP, '$'); - - while (ptr) - { - len = ptr - OldP; // Chars before $ - memcpy(NewP, OldP, len); - NewP += len; - - switch (*++ptr) - { - case 'I': // First name of the connected user. - - pptr = conn->UserPointer->Name; - break; - - case 'L': // Number of the latest message. - - sprintf(num, "%d", LatestMsg); - pptr = num; - break; - - case 'N': // Number of active messages. - - sprintf(num, "%d", NumberofMessages); - pptr = num; - break; - - case 'U': // Callsign of the connected user. - - pptr = conn->UserPointer->Call; - break; - - case 'W': // Inserts a carriage return. - - pptr = CR; - break; - - case 'Z': // Last message read by the user (L command). - - sprintf(num, "%d", conn->UserPointer->lastmsg); - pptr = num; - break; - - case 'X': // Number of messages for the user. - - Msgs = CountMessagesTo(conn->UserPointer, &Unread); - sprintf(num, "%d", Msgs); - pptr = num; - break; - - case 'x': // Number of new messages for the user. - - Msgs = CountMessagesTo(conn->UserPointer, &Unread); - sprintf(num, "%d", Unread); - pptr = num; - break; - - case 'F': // Number of new messages to forward to this BBS. - - Msgs = CountMessagestoForward(conn->UserPointer); - sprintf(num, "%d", Msgs); - pptr = num; - break; - - default: - - pptr = Dollar; // Just Copy $ - } - - len = strlen(pptr); - memcpy(NewP, pptr, len); - NewP += len; - - OldP = ++ptr; - ptr = strchr(OldP, '$'); - } - - strcpy(NewP, OldP); - - len = RemoveLF(NewMessage, (int)strlen(NewMessage)); - - WriteLogLine(conn, '>', NewMessage, (int)len, LOG); - QueueMsg(conn, NewMessage, (int)len); -} - -BOOL isdigits(char * string) -{ - // Returns TRUE id sting is decimal digits - - size_t i, n = strlen(string); - - for (i = 0; i < n; i++) - { - if (isdigit(string[i]) == FALSE) return FALSE; - } - return TRUE; -} - -BOOL wildcardcompare(char * Target, char * Match) -{ - // Do a compare with string *string string* *string* - - // Strings should all be UC - - char Pattern[100]; - char * firststar; - - strcpy(Pattern, Match); - firststar = strchr(Pattern,'*'); - - if (firststar) - { - size_t Len = strlen(Pattern); - - if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end - { - Pattern[Len - 1] = 0; - return !(strstr(Target, &Pattern[1]) == NULL); - } - if (Pattern[0] == '*') // * at start - { - // Compare the last len - 1 chars of Target - - size_t Targlen = strlen(Target); - size_t Comparelen = Targlen - (Len - 1); - - if (Len == 1) // Just * - return TRUE; - - if (Comparelen < 0) // Too Short - return FALSE; - - return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); - } - - // Must be * at end - compare first Len-1 char - - return (memcmp(Target, Pattern, Len - 1) == 0); - } - - // No WildCards - straight strcmp - return (strcmp(Target, Pattern) == 0); -} - -#ifndef LINBPQ - -PrintMessage(HDC hDC, struct MsgInfo * Msg); - -PrintMessages(HWND hDlg, int Count, int * Indexes) -{ - int i, CurrentMsgIndex; - char MsgnoText[10]; - int Msgno; - struct MsgInfo * Msg; - int Len = MAX_PATH; - BOOL hResult; - PRINTDLG pdx = {0}; - HDC hDC; - -// CHOOSEFONT cf; - LOGFONT lf; - HFONT hFont; - - - // Initialize the PRINTDLG structure. - - pdx.lStructSize = sizeof(PRINTDLG); - pdx.hwndOwner = hWnd; - pdx.hDevMode = NULL; - pdx.hDevNames = NULL; - pdx.hDC = NULL; - pdx.Flags = PD_RETURNDC | PD_COLLATE; - pdx.nMinPage = 1; - pdx.nMaxPage = 1000; - pdx.nCopies = 1; - pdx.hInstance = 0; - pdx.lpPrintTemplateName = NULL; - - // Invoke the Print property sheet. - - hResult = PrintDlg(&pdx); - - memset(&lf, 0, sizeof(LOGFONT)); - - /* - - // Initialize members of the CHOOSEFONT structure. - - cf.lStructSize = sizeof(CHOOSEFONT); - cf.hwndOwner = (HWND)NULL; - cf.hDC = pdx.hDC; - cf.lpLogFont = &lf; - cf.iPointSize = 0; - cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; - cf.rgbColors = RGB(0,0,0); - cf.lCustData = 0L; - cf.lpfnHook = (LPCFHOOKPROC)NULL; - cf.lpTemplateName = (LPSTR)NULL; - cf.hInstance = (HINSTANCE) NULL; - cf.lpszStyle = (LPSTR)NULL; - cf.nFontType = PRINTER_FONTTYPE; - cf.nSizeMin = 0; - cf.nSizeMax = 0; - - // Display the CHOOSEFONT common-dialog box. - - ChooseFont(&cf); - - // Create a logical font based on the user's - // selection and return a handle identifying - // that font. -*/ - - lf.lfHeight = -56; - lf.lfWeight = 600; - lf.lfOutPrecision = 3; - lf.lfClipPrecision = 2; - lf.lfQuality = 1; - lf.lfPitchAndFamily = '1'; - strcpy (lf.lfFaceName, "Courier New"); - - hFont = CreateFontIndirect(&lf); - - if (hResult) - { - // User clicked the Print button, so use the DC and other information returned in the - // PRINTDLG structure to print the document. - - DOCINFO pdi; - - pdi.cbSize = sizeof(DOCINFO); - pdi.lpszDocName = "BBS Message Print"; - pdi.lpszOutput = NULL; - pdi.lpszDatatype = "RAW"; - pdi.fwType = 0; - - hDC = pdx.hDC; - - SelectObject(hDC, hFont); - - StartDoc(hDC, &pdi); - StartPage(hDC); - - for (i = 0; i < Count; i++) - { - SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); - - Msgno = atoi(MsgnoText); - - for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) - { - Msg = MsgHddrPtr[CurrentMsgIndex]; - - if (Msg->number == Msgno) - { - PrintMessage(hDC, Msg); - break; - } - } - } - - EndDoc(hDC); - } - - if (pdx.hDevMode != NULL) - GlobalFree(pdx.hDevMode); - if (pdx.hDevNames != NULL) - GlobalFree(pdx.hDevNames); - - if (pdx.hDC != NULL) - DeleteDC(pdx.hDC); - - return 0; -} - -PrintMessage(HDC hDC, struct MsgInfo * Msg) -{ - int Len = MAX_PATH; - char * MsgBytes; - char * Save; - int Msglen; - - StartPage(hDC); - - Save = MsgBytes = ReadMessageFile(Msg->number); - - Msglen = Msg->length; - - if (MsgBytes) - { - char Hddr[1000]; - char FullTo[100]; - int HRes, VRes; - char * ptr1, * ptr2; - int LineLen; - - RECT Rect; - - if (_stricmp(Msg->to, "RMS") == 0) - sprintf(FullTo, "RMS:%s", Msg->via); - else - if (Msg->to[0] == 0) - sprintf(FullTo, "smtp:%s", Msg->via); - else - strcpy(FullTo, Msg->to); - - - sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", - Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); - - - if (Msg->B2Flags & B2Msg) - { - // Remove B2 Headers (up to the File: Line) - - char * ptr; - ptr = strstr(MsgBytes, "Body:"); - if (ptr) - { - Msglen = atoi(ptr + 5); - ptr = strstr(ptr, "\r\n\r\n"); - } - if (ptr) - MsgBytes = ptr + 4; - } - - HRes = GetDeviceCaps(hDC, HORZRES) - 50; - VRes = GetDeviceCaps(hDC, VERTRES) - 50; - - Rect.top = 50; - Rect.left = 50; - Rect.right = HRes; - Rect.bottom = VRes; - - DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); - DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); - - // process message a line at a time. When page is full, output a page break - - ptr1 = MsgBytes; - ptr2 = ptr1; - - while (Msglen-- > 0) - { - if (*ptr1++ == '\r') - { - // Output this line - - // First check if it will fit - - Rect.top = Rect.bottom; - Rect.right = HRes; - Rect.bottom = VRes; - - LineLen = ptr1 - ptr2 - 1; - - if (LineLen == 0) // Blank line - Rect.bottom = Rect.top + 40; - else - DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); - - if (Rect.bottom >= VRes) - { - EndPage(hDC); - StartPage(hDC); - - Rect.top = 50; - Rect.bottom = VRes; - if (LineLen == 0) // Blank line - Rect.bottom = Rect.top + 40; - else - DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); - } - - if (LineLen == 0) // Blank line - Rect.bottom = Rect.top + 40; - else - DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); - - if (*(ptr1) == '\n') - { - ptr1++; - Msglen--; - } - - ptr2 = ptr1; - } - } - - free(Save); - - EndPage(hDC); - - } - return 0; -} - -#endif - - -int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) -{ - char FileName[MAX_PATH] = "Messages.in"; - int Files = 0; - int WriteLen=0; - FILE *in; - CIRCUIT dummyconn; - struct UserInfo User; - int Index = 0; - - char Buffer[100000]; - char *buf = Buffer; - - if (FN[0]) // Name supplled - strcpy(FileName, FN); - - else - { -#ifndef LINBPQ - OPENFILENAME Ofn; - - memset(&Ofn, 0, sizeof(Ofn)); - - Ofn.lStructSize = sizeof(OPENFILENAME); - Ofn.hInstance = hInst; - Ofn.hwndOwner = MainWnd; - Ofn.lpstrFilter = NULL; - Ofn.lpstrFile= FileName; - Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); - Ofn.lpstrFileTitle = NULL; - Ofn.nMaxFileTitle = 0; - Ofn.lpstrInitialDir = BaseDir; - Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; - Ofn.lpstrTitle = NULL;//; - - if (!GetOpenFileName(&Ofn)) - return 0; -#endif - } - - in = fopen(FileName, "rb"); - - if (!(in)) - { - char msg[500]; - sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); - if (conn) - nodeprintf(conn, "%s\r", msg); -#ifdef WIN32 - else - if (Nopopup == FALSE) - MessageBox(NULL, msg, "BPQMailChat", MB_OK); -#endif - return 0; - } - - memset(&dummyconn, 0, sizeof(CIRCUIT)); - memset(&User, 0, sizeof(struct UserInfo)); - - if (conn == 0) - { - conn = &dummyconn; - - dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. - strcpy(User.Call, "IMPORT"); - User.flags |= F_EMAIL; - dummyconn.sysop = TRUE; - dummyconn.BBSFlags = BBS; - - strcpy(dummyconn.Callsign, "IMPORT"); - } - - while(fgets(Buffer, 99999, in)) - { - // First line should start SP/SB ?ST? - - char * From = NULL; - char * BID = NULL; - char * ATBBS = NULL; - char seps[] = " \t\r"; - struct MsgInfo * Msg; - char To[100]= ""; - int msglen; - char * Context; - char * Arg1, * Cmd; - -NextMessage: - - From = NULL; - BID = NULL; - ATBBS = NULL; - To[0]= 0; - - Sleep(100); - - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - - if (Buffer[0] == 0) //Blank Line - continue; - - WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); - - if (dummyconn.sysop == 0) - { - nodeprintf(conn, "%s\r", Buffer); - Flush(conn); - } - - Cmd = strtok_s(Buffer, seps, &Context); - - if (Cmd == NULL) - { - fclose(in); - return Files; - } - - Arg1 = strtok_s(NULL, seps, &Context); - - if (Arg1 == NULL) - { - if (dummyconn.sysop) - Debugprintf("Bad Import Line %s", Buffer); - else - nodeprintf(conn, "Bad Import Line %s\r", Buffer); - - fclose(in); - return Files; - } - - strcpy(To, Arg1); - - if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) - { - if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) - { - Msg = conn->TempMsg; - - // SP is Ok, read message; - - ClearQueue(conn); - - fgets(Buffer, 99999, in); - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - if (strlen(Buffer) > 60) - Buffer[60] = 0; - - strcpy(Msg->title, Buffer); - - // Read the lines - - conn->Flags |= GETTINGMESSAGE; - - Buffer[0] = 0; - - fgets(Buffer, 99999, in); - - while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) - { - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - msglen = (int)strlen(Buffer); - Buffer[msglen++] = 13; - ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - } - - // Message completed (or off end of file) - - Files ++; - - ClearQueue(conn); - - if (Buffer[0]) - goto NextMessage; // We have read the SP/SB line; - else - { - fclose(in); - return Files; - } - } - else - { - // Create failed - - Flush(conn); - } - } - - // Search for next message - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - - while (Buffer[0]) - { - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - - if (_stricmp(Buffer, "/EX") == 0) - { - // Found end - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - - if (dummyconn.sysop) - ClearQueue(conn); - else - Flush(conn); - - if (Buffer[0]) - goto NextMessage; // We have read the SP/SB line; - } - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - } - } - - fclose(in); - - if (dummyconn.sysop) - ClearQueue(conn); - else - Flush(conn); - - return Files; -} -char * ReadMessageFileEx(struct MsgInfo * MsgRec) -{ - // Sets Message Size from File Size - - int msgno = MsgRec->number; - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); - - if (stat(MsgFile, &STAT) == -1) - return NULL; - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - return NULL; - - MsgBytes=malloc(FileSize+1); - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize]=0; - MsgRec->length = FileSize; - - return MsgBytes; -} - -char * ReadMessageFile(int msgno) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); - - if (stat(MsgFile, &STAT) == -1) - return NULL; - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - return NULL; - - MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize]=0; - - return MsgBytes; -} - - -int QueueMsg(ConnectionInfo * conn, char * msg, int len) -{ - // Add Message to queue for this connection - - // UCHAR * OutputQueue; // Messages to user - // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. - - // Create or extend buffer - - GetSemaphore(&OutputSEM, 0); - - conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); - - if (conn->OutputQueue == NULL) - { - // relloc failed - should never happen, but clean up - - CriticalErrorHandler("realloc failed to expand output queue"); - FreeSemaphore(&OutputSEM); - return 0; - } - - memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); - conn->OutputQueueLength += len; - FreeSemaphore(&OutputSEM); - - return len; -} - -void TrytoSend() -{ - // call Flush on any connected streams with queued data - - ConnectionInfo * conn; - struct ConsoleInfo * Cons; - - int n; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (conn->Active == TRUE) - { - Flush(conn); - - // if an FLARQ mail has been sent see if queues have cleared - - if (conn->BBSFlags & YAPPTX) - { - YAPPSendData(conn); - } - else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) - { - int n = TXCount(conn->BPQStream); // All Sent and Acked? - - if (n == 0) - { - struct MsgInfo * Msg = conn->FwdMsg; - - conn->ARQClearCount--; - - if (conn->ARQClearCount <= 0) - { - Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); - - // Mark mail as sent, and look for more - - clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); - set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) - { - Msg->status = 'F'; // Mark as forwarded - Msg->datechanged=time(NULL); - } - - conn->BBSFlags &= ~ARQMAILACK; - conn->UserPointer->ForwardingInfo->MsgCount--; - - SaveMessageDatabase(); - SendARQMail(conn); // See if any more - close if not - } - } - else - conn->ARQClearCount = 10; - } - } - } -#ifndef LINBPQ - for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) - { - if (Cons->Console) - Flush(Cons->Console); - } -#endif -} - - -void Flush(CIRCUIT * conn) -{ - int tosend, len, sent; - - // Try to send data to user. May be stopped by user paging or node flow control - - // UCHAR * OutputQueue; // Messages to user - // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. - - // BOOL Paging; // Set if user wants paging - // int LinesSent; // Count when paging - // int PageLen; // Lines per page - - - if (conn->OutputQueue == NULL) - { - // Nothing to send. If Close after Flush is set, disconnect - - if (conn->CloseAfterFlush) - { - conn->CloseAfterFlush--; - - if (conn->CloseAfterFlush) - return; - - Disconnect(conn->BPQStream); - conn->ErrorCount = 0; - } - - return; // Nothing to send - } - tosend = conn->OutputQueueLength - conn->OutputGetPointer; - - sent=0; - - while (tosend > 0) - { - if (TXCount(conn->BPQStream) > 15) - return; // Busy - - if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting - return; - - if (conn->Paging && (conn->LinesSent >= conn->PageLen)) - return; - - if (tosend <= conn->paclen) - len=tosend; - else - len=conn->paclen; - - GetSemaphore(&OutputSEM, 0); - - if (conn->Paging) - { - // look for CR chars in message to send. Increment LinesSent, and stop if at limit - - UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; - UCHAR * ptr2; - int lenleft = len; - - ptr2 = memchr(ptr1, 0x0d, len); - - while (ptr2) - { - conn->LinesSent++; - ptr2++; - lenleft = len - (int)(ptr2 - ptr1); - - if (conn->LinesSent >= conn->PageLen) - { - len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); - - SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); - conn->OutputGetPointer+=len; - tosend-=len; - SendUnbuffered(conn->BPQStream, "bort, Continue..>", 25); - FreeSemaphore(&OutputSEM); - return; - - } - ptr2 = memchr(ptr2, 0x0d, lenleft); - } - } - - SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); - - conn->OutputGetPointer+=len; - - FreeSemaphore(&OutputSEM); - - tosend-=len; - sent++; - - if (sent > 15) - return; - } - - // All Sent. Free buffers and reset pointers - - conn->LinesSent = 0; - - ClearQueue(conn); -} - -VOID ClearQueue(ConnectionInfo * conn) -{ - if (conn->OutputQueue == NULL) - return; - - GetSemaphore(&OutputSEM, 0); - - free(conn->OutputQueue); - - conn->OutputQueue=NULL; - conn->OutputGetPointer=0; - conn->OutputQueueLength=0; - - FreeSemaphore(&OutputSEM); -} - - - -VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) -{ - struct UserInfo * user; - - Msg->status='K'; - Msg->datechanged=time(NULL); - - // Remove any forwarding references - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - for (user = BBSChain; user; user = user->BBSNext) - { - if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) - { - user->ForwardingInfo->MsgCount--; - clear_fwd_bit(Msg->fbbs, user->BBSNumber); - } - } - } - if (SaveDB) - SaveMessageDatabase(); - RebuildNNTPList(); -} - -void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - struct MsgInfo * Msg; - - while (Arg1) - { - msgno = atoi(Arg1); - - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - - Msg = GetMsgFromNumber(msgno); - - if (Msg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - goto Next; - } - - if (Msg->type != 'T') - { - nodeprintf(conn, "Message %d not an NTS Message\r", msgno); - goto Next; - } - - if (Msg->status == 'N') - nodeprintf(conn, "Warning - Message has status N\r"); - - Msg->status = 'D'; - Msg->datechanged=time(NULL); - SaveMessageDatabase(); - - nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); - Next: - Arg1 = strtok_s(NULL, " \r", &Context); - } - - return; -} - -void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - int i; - struct MsgInfo * Msg; - - // Param is either ALL or a list of numbers - - if (Arg1 == NULL) - { - nodeprintf(conn, "No message number\r"); - return; - } - - if (_stricmp(Arg1, "ALL") == 0) - { - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - - if (Msg->status == 'H') - { - if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - else - Msg->status = 'N'; - - nodeprintf(conn, "Message #%d Unheld\r", Msg->number); - } - } - return; - } - - while (Arg1) - { - msgno = atoi(Arg1); - Msg = GetMsgFromNumber(msgno); - - if (Msg) - { - if (Msg->status == 'H') - { - if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - else - Msg->status = 'N'; - - nodeprintf(conn, "Message #%d Unheld\r", msgno); - } - else - { - nodeprintf(conn, "Message #%d was not held\r", msgno); - } - } - else - nodeprintf(conn, "Message #%d not found\r", msgno); - - Arg1 = strtok_s(NULL, " \r", &Context); - } - - return; -} - -void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - int i; - struct MsgInfo * Msg; - - switch (toupper(Cmd[1])) - { - - case 0: // Just K - - while (Arg1) - { - msgno = atoi(Arg1); - KillMessage(conn, user, msgno); - - Arg1 = strtok_s(NULL, " \r", &Context); - } - - SaveMessageDatabase(); - return; - - case 'M': // Kill Mine - - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - - if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) - { - if (Msg->type == 'P' && Msg->status == 'Y') - { - FlagAsKilled(Msg, FALSE); - nodeprintf(conn, "Message #%d Killed\r", Msg->number); - } - } - } - - SaveMessageDatabase(); - return; - - case 'H': // Kill Held - - if (conn->sysop) - { - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - - if (Msg->status == 'H') - { - FlagAsKilled(Msg, FALSE); - nodeprintf(conn, "Message #%d Killed\r", Msg->number); - } - } - } - SaveMessageDatabase(); - return; - - case '>': // K> - Kill to - - if (conn->sysop) - { - if (Arg1) - if (KillMessagesTo(conn, user, Arg1) == 0) - BBSputs(conn, "No Messages found\r"); - - return; - } - - case '<': - - if (conn->sysop) - { - if (Arg1) - if (KillMessagesFrom(conn, user, Arg1) == 0); - BBSputs(conn, "No Messages found\r"); - - return; - } - } - - nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); - - return; - -} - -int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) -{ - int i, Msgs = 0; - struct MsgInfo * Msg; - - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) - { - Msgs++; - KillMessage(conn, user, MsgHddrPtr[i]->number); - } - } - - SaveMessageDatabase(); - return(Msgs); -} - -int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) -{ - int i, Msgs = 0; - struct MsgInfo * Msg; - - - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) - { - Msgs++; - KillMessage(conn, user, MsgHddrPtr[i]->number); - } - } - - SaveMessageDatabase(); - return(Msgs); -} - -BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) -{ - if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) - return TRUE; - - if (Msg->type == 'P') - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return TRUE; - - if (Msg->type == 'B') - if (_stricmp(Msg->from, Call) == 0) - return TRUE; - - return FALSE; -} - -void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) -{ - struct MsgInfo * Msg; - - Msg = GetMsgFromNumber(msgno); - - if (Msg == NULL || Msg->status == 'K') - { - nodeprintf(conn, "Message %d not found\r", msgno); - return; - } - - if (OkToKillMessage(conn->sysop, user->Call, Msg)) - { - FlagAsKilled(Msg, FALSE); - nodeprintf(conn, "Message #%d Killed\r", msgno); - } - else - nodeprintf(conn, "Not your message\r"); -} - - -BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) -{ - char FullFrom[80]; - char FullTo[80]; - - strcpy(FullFrom, Msg->from); - - if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || - Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) - strcat(FullFrom, Msg->emailfrom); - - if (_stricmp(Msg->to, "RMS") == 0) - { - sprintf(FullTo, "RMS:%s", Msg->via); - nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); - } - else - - if (Msg->to[0] == 0 && Msg->via[0] != 0) - { - sprintf(FullTo, "smtp:%s", Msg->via); - nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); - } - - else - if (Msg->via[0] != 0) - { - char Via[80]; - strcpy(Via, Msg->via); - strlop(Via, '.'); // Only show first part of via - nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); - } - else - nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); - - // if paging, stop two before page lengh. This lets us send the continue prompt, save status - // and exit without triggering the system paging code. We can then read a message then resume listing - - if (Temp->ListActive && conn->Paging) - { - Temp->LinesSent++; - - if ((Temp->LinesSent + 1) >= conn->PageLen) - { - nodeprintf(conn, "bort, , = Continue..>"); - Temp->LastListedInPagedMode = Msg->number; - Temp->ListSuspended = TRUE; - return TRUE; - } - } - - return FALSE; -} - -void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) -{ - struct TempUserInfo * Temp = user->Temp; - struct MsgInfo * Msg; - - // Allow compound selection, eg LTN or LFP - - // types P N T - // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New - // Status N Y H F K D - - // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. - - Temp->ListActive = TRUE; - Temp->LinesSent = 0; - - if (Resuming) - { - // Entered after a paging pause. Selection fields are already set up - - // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode - -// Start = Temp->LastListedInPagedMode; - Temp->ListSuspended = FALSE; - } - else - { - Temp->ListRangeEnd = LatestMsg; - Temp->ListRangeStart = 1; - Temp->LLCount = 0; - Temp->SendFullFrom = 0; - Temp->ListType = 0; - Temp->ListStatus = 0; - Temp->ListSelector = 0; - Temp->UpdateLatest = 0; - Temp->LastListParams[0] = 0; - Temp->IncludeKilled = 1; // SYSOP include Killed except LM - - //Analyse L params. - - _strupr(Cmd); - - if (strcmp(Cmd, "LC") == 0) // List Bull Categories - { - ListCategories(conn); - return; - } - - // if command is just L or LR start from last listed - - if (Arg1 == NULL) - { - if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) - { - if (LatestMsg == conn->lastmsg) - { - BBSputs(conn, "No New Messages\r"); - return; - } - - Temp->UpdateLatest = 1; - Temp->ListRangeStart = conn->lastmsg; - } - } - - if (strchr(Cmd, 'V')) // Verbose - Temp->SendFullFrom = 'V'; - - if (strchr(Cmd, 'R')) - Temp->ListDirn = 'R'; - else - Temp->ListDirn = '*'; // Default newest first - - Cmd++; // skip L - - if (strchr(Cmd, 'T')) - Temp->ListType = 'T'; - else if (strchr(Cmd, 'P')) - Temp->ListType = 'P'; - else if (strchr(Cmd, 'B')) - Temp->ListType = 'B'; - - if (strchr(Cmd, 'N')) - Temp->ListStatus = 'N'; - else if (strchr(Cmd, 'Y')) - Temp->ListStatus = 'Y'; - else if (strchr(Cmd, 'F')) - Temp->ListStatus = 'F'; - else if (strchr(Cmd, '$')) - Temp->ListStatus = '$'; - else if (strchr(Cmd, 'H')) - Temp->ListStatus = 'H'; - else if (strchr(Cmd, 'K')) - Temp->ListStatus = 'K'; - else if (strchr(Cmd, 'D')) - Temp->ListStatus = 'D'; - - // H or K only by Sysop - - switch (Temp->ListStatus) - { - case 'K': - case 'H': // List Status - - if (conn->sysop) - break; - - BBSputs(conn, "LH or LK can only be used by SYSOP\r"); - return; - } - - if (strchr(Cmd, '<')) - Temp->ListSelector = '<'; - else if (strchr(Cmd, '>')) - Temp->ListSelector = '>'; - else if (strchr(Cmd, '@')) - Temp->ListSelector = '@'; - else if (strchr(Cmd, 'M')) - { - Temp->ListSelector = 'M'; - Temp->IncludeKilled = FALSE; - } - - // Param could be single number, number range or call - - if (Arg1) - { - if (strchr(Cmd, 'L')) // List Last - { - // Param is number - - if (Arg1) - Temp->LLCount = atoi(Arg1); - } - else - { - // Range nnn-nnn or single value or callsign - - char * Arg2, * Arg3, * Range; - char seps[] = " \t\r"; - UINT From=LatestMsg, To=0; - - Arg2 = strtok_s(NULL, seps, &Context); - Arg3 = strtok_s(NULL, seps, &Context); - - if (Temp->ListSelector && Temp->ListSelector != 'M') - { - // < > or @ - first param is callsign - - strcpy(Temp->LastListParams, Arg1); - - // Just possible number range - - Arg1 = Arg2; - Arg2 = Arg3; - Arg3 = strtok_s(NULL, seps, &Context); - } - - if (Arg1) - { - Range = strchr(Arg1, '-'); - - // A number could be a Numeric Bull Dest (eg 44) - // I think this can only resaonably be > - - if (isdigits(Arg1)) - To = From = atoi(Arg1); - - if (Arg2) - From = atoi(Arg2); - else - { - if (Range) - { - Arg3 = strlop(Arg1, '-'); - - To = atoi(Arg1); - - if (Arg3 && Arg3[0]) - From = atoi(Arg3); - else - From = LatestMsg; - } - } - if (From > 100000 || To > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - Temp->ListRangeStart = To; - Temp->ListRangeEnd = From; - } - } - } - } - - // Run through all messages (either forwards or backwards) and list any that match all selection criteria - - while (1) - { - if (Temp->ListDirn == 'R') - Msg = GetMsgFromNumber(Temp->ListRangeStart); - else - Msg = GetMsgFromNumber(Temp->ListRangeEnd); - - - if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message - { - // Check filters - - if (Temp->ListStatus && Temp->ListStatus != Msg->status) - goto skip; - - if (Temp->ListType && Temp->ListType != Msg->type) - goto skip; - - if (Temp->ListSelector == '<') - if (_stricmp(Msg->from, Temp->LastListParams) != 0) - goto skip; - - if (Temp->ListSelector == '>') - if (_stricmp(Msg->to, Temp->LastListParams) != 0) - goto skip; - - if (Temp->ListSelector == '@') - if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && - (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) - goto skip; - - if (Temp->ListSelector == 'M') - if (_stricmp(Msg->to, user->Call) != 0 && - (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) - - goto skip; - - if (ListMessage(Msg, conn, Temp)) - { - if (Temp->ListDirn == 'R') - Temp->ListRangeStart++; - else - Temp->ListRangeEnd--; - - return; // Hit page limit - } - - if (Temp->LLCount) - { - Temp->LLCount--; - if (Temp->LLCount == 0) - return; // LL count reached - } -skip:; - } - - if (Temp->ListRangeStart == Temp->ListRangeEnd) - { - // if using L or LR (list new) update last listed field - - if (Temp->UpdateLatest) - conn->lastmsg = LatestMsg; - - return; - } - - if (Temp->ListDirn == 'R') - Temp->ListRangeStart++; - else - Temp->ListRangeEnd--; - - if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! - return; - - } - -/* - - switch (Cmd[0]) - { - - case '*': // Just L - case 'R': // LR = List Reverse - - if (Arg1) - { - // Range nnn-nnn or single value - - char * Arg2, * Arg3; - char * Context; - char seps[] = " -\t\r"; - UINT From=LatestMsg, To=0; - char * Range = strchr(Arg1, '-'); - - Arg2 = strtok_s(Arg1, seps, &Context); - Arg3 = strtok_s(NULL, seps, &Context); - - if (Arg2) - To = From = atoi(Arg2); - - if (Arg3) - From = atoi(Arg3); - else - if (Range) - From = LatestMsg; - - if (From > 100000 || To > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - - if (Cmd[1] == 'R') - { - if (Start) - To = Start + 1; - - ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); - } - else - { - if (Start) - From = Start - 1; - - ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); - } - } - else - - if (LatestMsg == conn->lastmsg) - BBSputs(conn, "No New Messages\r"); - else if (Cmd[1] == 'R') - ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); - else - ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); - - conn->lastmsg = LatestMsg; - - return; - - - case 'L': // List Last - - if (Arg1) - { - int i = atoi(Arg1); - int m = NumberofMessages; - - if (Resuming) - i = Temp->LLCount; - else - Temp->LLCount = i; - - for (; i>0 && m != 0; i--) - { - m = GetUserMsg(m, user->Call, conn->sysop); - - if (m > 0) - { - if (Start && MsgHddrPtr[m]->number >= Start) - { - m--; - i++; - continue; - } - - Temp->LLCount--; - - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - m--; - } - } - } - return; - - case 'M': // LM - List Mine - - if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - return; - - case '>': // L> - List to - - if (Arg1) - if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - - - return; - - case '<': - - if (Arg1) - if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - - return; - - case '@': - - if (Arg1) - if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - - return; - - case 'N': - case 'Y': - case 'F': - case '$': - case 'D': // Delivered NTS Traffic can be listed by anyone - { - int m = NumberofMessages; - - while (m > 0) - { - m = GetUserMsg(m, user->Call, conn->sysop); - - if (m > 0) - { - if (Start && MsgHddrPtr[m]->number >= Start) - { - m--; - continue; - } - - if (Temp->ListType) - { - if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - } - else - { - if (MsgHddrPtr[m]->status == toupper(Cmd[1])) - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - } - m--; - } - } - } - return; - - case 'K': - case 'H': // List Status - - if (conn->sysop) - { - int i, Msgs = Start; - - for (i=NumberofMessages; i>0; i--) - { - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if (MsgHddrPtr[i]->status == toupper(Cmd[1])) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) - return; // Hit page limit - - } - } - - if (Msgs == 0) - BBSputs(conn, "No Messages found\r"); - } - else - BBSputs(conn, "LH or LK can only be used by SYSOP\r"); - - return; - - case 'C': - { - struct NNTPRec * ptr = FirstNNTPRec; - char Cat[100]; - char NextCat[100]; - int Line = 0; - int Count; - - while (ptr) - { - // if the next name is the same, combine counts - - strcpy(Cat, ptr->NewsGroup); - strlop(Cat, '.'); - Count = ptr->Count; - Catloop: - if (ptr->Next) - { - strcpy(NextCat, ptr->Next->NewsGroup); - strlop(NextCat, '.'); - if (strcmp(Cat, NextCat) == 0) - { - ptr = ptr->Next; - Count += ptr->Count; - goto Catloop; - } - } - - nodeprintf(conn, "%-6s %-3d", Cat, Count); - Line += 10; - if (Line > 80) - { - Line = 0; - nodeprintf(conn, "\r"); - } - - ptr = ptr->Next; - } - - if (Line) - nodeprintf(conn, "\r\r"); - else - nodeprintf(conn, "\r"); - - return; - } - } - - // Could be P B or T if specified without a status - - switch (Temp->ListType) - { - case 'P': - case 'B': - case 'T': // NTS Traffic can be listed by anyone - { - int m = NumberofMessages; - - while (m > 0) - { - m = GetUserMsg(m, user->Call, conn->sysop); - - if (m > 0) - { - if (Start && MsgHddrPtr[m]->number >= Start) - { - m--; - continue; - } - - if (MsgHddrPtr[m]->type == Temp->ListType) - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - m--; - } - } - - return; - } - } - -*/ - nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); - -} - -void ListCategories(ConnectionInfo * conn) -{ - // list bull categories - struct NNTPRec * ptr = FirstNNTPRec; - char Cat[100]; - char NextCat[100]; - int Line = 0; - int Count; - - while (ptr) - { - // if the next name is the same, combine counts - - strcpy(Cat, ptr->NewsGroup); - strlop(Cat, '.'); - Count = ptr->Count; -Catloop: - if (ptr->Next) - { - strcpy(NextCat, ptr->Next->NewsGroup); - strlop(NextCat, '.'); - if (strcmp(Cat, NextCat) == 0) - { - ptr = ptr->Next; - Count += ptr->Count; - goto Catloop; - } - } - - nodeprintf(conn, "%-6s %-3d", Cat, Count); - Line += 10; - if (Line > 80) - { - Line = 0; - nodeprintf(conn, "\r"); - } - - ptr = ptr->Next; - } - - if (Line) - nodeprintf(conn, "\r\r"); - else - nodeprintf(conn, "\r"); - - return; -} - -/* -int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) -{ - int i, Msgs = Start; - - for (i=NumberofMessages; i>0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || - ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && - _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) - break; // Hit page limit - } - } - - return(Msgs); -} - -int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) -{ - int i, Msgs = 0; - - for (i=NumberofMessages; i>0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) - return Msgs; // Hit page limit - - } - } - - return(Msgs); -} - -int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) -{ - int i, Msgs = 0; - - for (i=NumberofMessages; i>0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || - (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) - break; // Hit page limit - } - } - - return(Msgs); -} -*/ -int GetUserMsg(int m, char * Call, BOOL SYSOP) -{ - struct MsgInfo * Msg; - - // Get Next (usually backwards) message which should be shown to this user - // ie Not Deleted, and not Private unless to or from Call - - do - { - Msg=MsgHddrPtr[m]; - - if (SYSOP) return m; // Sysop can list or read anything - - if (Msg->status != 'K') - { - - if (Msg->status != 'H') - { - if (Msg->type == 'B' || Msg->type == 'T') return m; - - if (Msg->type == 'P') - { - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return m; - } - } - } - - m--; - - } while (m> 0); - - return 0; -} - - -BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) -{ - // Return TRUE if user is allowed to read message - - if (Msg->status == 'K' && IncludeKilled == 0) - return FALSE; - - if (SYSOP) - return TRUE; // Sysop can list or read anything - - if ((Msg->status != 'K') && (Msg->status != 'H')) - { - if (Msg->type == 'B' || Msg->type == 'T') return TRUE; - - if (Msg->type == 'P') - { - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return TRUE; - } - } - - return FALSE; -} -/* -int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) -{ - struct MsgInfo * Msg; - - // Get Next (usually backwards) message which should be shown to this user - // ie Not Deleted, and not Private unless to or from Call - - do - { - Msg=MsgHddrPtr[m]; - - if (Msg->status != 'K') - { - if (SYSOP) return m; // Sysop can list or read anything - - if (Msg->status != 'H') - { - if (Msg->type == 'B' || Msg->type == 'T') return m; - - if (Msg->type == 'P') - { - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return m; - } - } - } - - m++; - - } while (m <= NumberofMessages); - - return 0; - -} - - -void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) -{ - int m; - struct MsgInfo * Msg; - - for (m = Start; m >= End; m--) - { - Msg = GetMsgFromNumber(m); - - if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) - if (ListMessage(Msg, conn, Temp->SendFullFrom)) - return; // Hit page limit - - } -} - - -void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) -{ - int m; - struct MsgInfo * Msg; - - for (m = Start; m <= End; m++) - { - Msg = GetMsgFromNumber(m); - - if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) - if (ListMessage(Msg, conn, Temp->SendFullFrom)) - return; // Hit page limit - } -} -*/ - -void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - int i; - struct MsgInfo * Msg; - - - switch (toupper(Cmd[1])) - { - case 0: // Just R - - while (Arg1) - { - msgno = atoi(Arg1); - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - - ReadMessage(conn, user, msgno); - Arg1 = strtok_s(NULL, " \r", &Context); - } - - return; - - case 'M': // Read Mine (Unread Messages) - - if (toupper(Cmd[2]) == 'R') - { - for (i = 1; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - - if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) - if (Msg->status == 'N') - ReadMessage(conn, user, Msg->number); - } - } - else - { - for (i = NumberofMessages; i > 0; i--) - { - Msg = MsgHddrPtr[i]; - - if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) - if (Msg->status == 'N') - ReadMessage(conn, user, Msg->number); - } - } - - return; - } - - nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); - - return; -} - -int RemoveLF(char * Message, int len) -{ - // Remove lf chars and nulls - - char * ptr1, * ptr2; - - ptr1 = ptr2 = Message; - - while (len-- > 0) - { - while (*ptr1 == 0 && len) - { - ptr1++; - len--; - } - - *ptr2 = *ptr1; - - if (*ptr1 == '\r') - if (*(ptr1+1) == '\n') - { - ptr1++; - len--; - } - ptr1++; - ptr2++; - } - - return (int)(ptr2 - Message); -} - - - -int RemoveNulls(char * Message, int len) -{ - // Remove nulls - - char * ptr1, * ptr2; - - ptr1 = ptr2 = Message; - - while (len-- > 0) - { - while (*ptr1 == 0 && len) - { - ptr1++; - len--; - } - - *ptr2 = *ptr1; - - ptr1++; - ptr2++; - } - - return (int)(ptr2 - Message); -} - -void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) -{ - struct MsgInfo * Msg; - char * MsgBytes, * Save; - char FullTo[100]; - int Index = 0; - - Msg = GetMsgFromNumber(msgno); - - if (Msg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - return; - } - - if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) - { - nodeprintf(conn, "Message %d not for you\r", msgno); - return; - } - - if (_stricmp(Msg->to, "RMS") == 0) - sprintf(FullTo, "RMS:%s", Msg->via); - else - if (Msg->to[0] == 0) - sprintf(FullTo, "smtp:%s", Msg->via); - else - strcpy(FullTo, Msg->to); - - - nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", - Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); - - MsgBytes = Save = ReadMessageFile(msgno); - - if (Msg->type == 'P') - Index = PMSG; - else if (Msg->type == 'B') - Index = BMSG; - else if (Msg->type == 'T') - Index = TMSG; - - if (MsgBytes) - { - int Length = Msg->length; - - if (Msg->B2Flags & B2Msg) - { - char * ptr; - - // if message has attachments, display them if plain text - - if (Msg->B2Flags & Attachments) - { - char * FileName[100]; - int FileLen[100]; - int Files = 0; - int BodyLen, NewLen; - int i; - char *ptr2; - char Msg[512]; - int Len; - - ptr = MsgBytes; - - Len = sprintf(Msg, "Message has Attachments\r\r"); - QueueMsg(conn, Msg, Len); - - while(*ptr != 13) - { - ptr2 = strchr(ptr, 10); // Find CR - - if (memcmp(ptr, "Body: ", 6) == 0) - { - BodyLen = atoi(&ptr[6]); - } - - if (memcmp(ptr, "File: ", 6) == 0) - { - char * ptr1 = strchr(&ptr[6], ' '); // Find Space - - FileLen[Files] = atoi(&ptr[6]); - - FileName[Files++] = &ptr1[1]; - *(ptr2 - 1) = 0; - } - - ptr = ptr2; - ptr++; - } - - ptr += 2; // Over Blank Line and Separator - - NewLen = RemoveLF(ptr, BodyLen); - - QueueMsg(conn, ptr, NewLen); // Display Body - - ptr += BodyLen + 2; // to first file - - for (i = 0; i < Files; i++) - { - char Msg[512]; - int Len, n; - char * p = ptr; - char c; - - // Check if message is probably binary - - int BinCount = 0; - - NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own - - for (n = 0; n < NewLen; n++) - { - c = *p; - - if (c == 10) - *p = 13; - - if (c==0 || (c & 128)) - BinCount++; - - p++; - - } - - if (BinCount > NewLen/10) - { - // File is probably Binary - - Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); - QueueMsg(conn, Msg, Len); - } - else - { - Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); - QueueMsg(conn, Msg, Len); - - user->Total.MsgsSent[Index] ++; - user->Total.BytesForwardedOut[Index] += NewLen; - - QueueMsg(conn, ptr, NewLen); - } - - ptr += FileLen[i]; - ptr +=2; // Over separator - } - goto sendEOM; - } - - // Remove B2 Headers (up to the File: Line) - - ptr = strstr(MsgBytes, "Body:"); - - if (ptr) - { - MsgBytes = ptr; - Length = (int)strlen(ptr); - } - } - - // Remove lf chars - - Length = RemoveLF(MsgBytes, Length); - - user->Total.MsgsSent[Index] ++; - user->Total.BytesForwardedOut[Index] += Length; - - QueueMsg(conn, MsgBytes, Length); - -sendEOM: - - free(Save); - - nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); - - if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) - { - if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) - { - if (Msg->status != 'Y') - { - Msg->status = 'Y'; - Msg->datechanged=time(NULL); - SaveMessageDatabase(); - } - } - } - } - else - { - nodeprintf(conn, "File for Message %d not found\r", msgno); - } -} - struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) - { - int m=NumberofMessages; - - struct MsgInfo * Msg; - - do - { - m = GetUserMsg(m, Call, sysop); - - if (m == 0) - return NULL; - - Msg=MsgHddrPtr[m]; - - if (Msg->number == msgno) - return Msg; - - m--; - - } while (m> 0); - - return NULL; - -} - - -char * ReadInfoFile(char * File) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - char * ptr1 = 0, * ptr2; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); - - if (stat(MsgFile, &STAT) == -1) - return NULL; - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - return NULL; - - MsgBytes=malloc(FileSize+1); - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize] = 0; - - ptr1 = MsgBytes; - - // Replace LF or CRLF with CR - - // First remove cr from crlf - - while(ptr2 = strstr(ptr1, "\r\n")) - { - memmove(ptr2, ptr2 + 1, strlen(ptr2)); - } - - // Now replace lf with cr - - ptr1 = MsgBytes; - - while (*ptr1) - { - if (*ptr1 == '\n') - *(ptr1) = '\r'; - - ptr1++; - } - - return MsgBytes; -} - -char * FormatDateAndTime(time_t Datim, BOOL DateOnly) -{ - struct tm *tm; - static char Date[]="xx-xxx hh:mmZ"; - - tm = gmtime(&Datim); - - if (tm) - sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", - tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); - - if (DateOnly) - { - Date[6]=0; - return Date; - } - - return Date; -} - -BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); - - -BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY - - char * From = NULL; - char * BID = NULL; - char * ATBBS = NULL; - char seps[] = " \t\r"; - struct MsgInfo * OldMsg; - char OldTitle[62]; - char NewTitle[62]; - char To[100]= ""; - int msgno; - - if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP - - switch (toupper(Cmd[1])) - { - case 'B': - - if (RefuseBulls) - { - nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); - return FALSE; - } - - if (user->flags & F_NOBULLS) - { - nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); - return FALSE; - } - - - case 'P': - case 'T': - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); - return FALSE; - } - - strcpy(To, Arg1); - - if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) - return FALSE; - - return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); - - case 'R': - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: Message Number is missing\r"); - return FALSE; - } - - msgno = atoi(Arg1); - - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return FALSE; - } - - OldMsg = FindMessage(user->Call, msgno, conn->sysop); - - if (OldMsg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - return FALSE; - } - - Arg1=&OldMsg->from[0]; - - strcpy(To, Arg1); - - if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) - { - // SMTP message. Need to get the real sender from the message - - sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); - } - - if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) - return FALSE; - - strcpy(OldTitle, OldMsg->title); - - if (strlen(OldTitle) > 57) OldTitle[57] = 0; - - strcpy(NewTitle, "Re:"); - strcat(NewTitle, OldTitle); - - return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); - - return TRUE; - - case 'C': - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: Message Number is missing\r"); - return FALSE; - } - - msgno = atoi(Arg1); - - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return FALSE; - } - - Arg1 = strtok_s(NULL, seps, &Context); - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); - return FALSE; - } - - strcpy(To, Arg1); - - if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) - return FALSE; - - OldMsg = FindMessage(user->Call, msgno, conn->sysop); - - if (OldMsg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - return FALSE; - } - - strcpy(OldTitle, OldMsg->title); - - if (strlen(OldTitle) > 56) OldTitle[56] = 0; - - strcpy(NewTitle, "Fwd:"); - strcat(NewTitle, OldTitle); - - conn->CopyBuffer = ReadMessageFile(msgno); - - return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); - } - - - nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); - - return FALSE; -} - -char * CheckToAddress(CIRCUIT * conn, char * Addr) -{ - // Check one element of Multiple Address - - if (conn == NULL || !(conn->BBSFlags & BBS)) - { - // if a normal user, check that TO and/or AT are known and warn if not. - - if (_stricmp(Addr, "SYSOP") == 0) - { - return _strdup(Addr); - } - - if (SendBBStoSYSOPCall) - if (_stricmp(Addr, BBSName) == 0) - return _strdup(SYSOPCall); - - - if (strchr(Addr, '@') == 0) - { - // No routing, if not a user and not known to forwarding or WP warn - - struct UserInfo * ToUser = LookupCall(Addr); - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->HomeBBS[0]) - { - char * NewAddr = malloc(250); - if (conn) - nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); - sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); - return NewAddr; - } - } - else - { - WPRecP WP = LookupWP(Addr); - - if (WP) - { - char * NewAddr = malloc(250); - - if (conn) - nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); - sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); - return NewAddr; - } - } - } - } - - // Check SMTP and RMS Addresses - - if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) - { - Addr[3] = ':'; // Replace RMS/ with RMS: - - if (conn && !FindRMS()) - { - nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); - return FALSE; - } - } - else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) - { - Addr[4] = ':'; // Replace smpt/ with smtp: - - if (ISP_Gateway_Enabled) - { - if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) - { - nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); - return FALSE; - } - } - else - { - if (conn) - nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); - return FALSE; - } - } - - return _strdup(Addr); -} - - -char Winlink[] = "WINLINK.ORG"; - -BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) -{ - char * ptr; - char seps[] = " \t\r"; - WPRecP WP; - char * ToCopy = _strdup(To); - int Len; - - conn->ToCount = 0; - - // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY - - // Having trailing ; will mess up parsing multiple addresses, so remove. - - while (To[strlen(To) - 1] == ';') - To[strlen(To) - 1] = 0; - - if (strchr(Context, ';') || strchr(To, ';')) - { - // Multiple Addresses - put address list back together - - char * p; - - To[strlen(To)] = ' '; - Context = To; - - while (p = strchr(Context, ';')) - { - // Multiple Addressees - - To = strtok_s(NULL, ";", &Context); - Len = (int)strlen(To); - conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); - if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) - conn->ToCount++; - } - - To = strtok_s(NULL, seps, &Context); - - Len = (int)strlen(To); - conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); - if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) - conn->ToCount++; - } - else - { - // Single Call - - // accept CALL!CALL for source routed message - - if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ - { - char * bang = strchr(To, '!'); - - memmove(bang + 1, bang, strlen(bang)); // Move !call down one - - *ATBBS = strlop(To, '!');; - } - - // Accept call@call (without spaces) - but check for smtp addresses - - if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) - { - ptr = strchr(To, '@'); - - if (ptr) - { - // If looks like a valid email address, treat as such - - int tolen; - *ATBBS = strlop(To, '@'); - - strlop(To, '-'); // Cant have SSID on BBS Name - - tolen = (int)strlen(To); - - if (tolen > 6 || !CheckifPacket(*ATBBS)) - { - // Probably Email address. Add smtp: or rms: - - if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route - sprintf(To, "rms:%s", ToCopy); - else if (ISP_Gateway_Enabled) - sprintf(To, "smtp:%s", ToCopy); - else if (isAMPRMsg(ToCopy)) - sprintf(To, "rms:%s", ToCopy); - - } - } - } - } - - free(ToCopy); - - // Look for Optional fields; - - ptr = strtok_s(NULL, seps, &Context); - - while (ptr) - { - if (strcmp(ptr, "@") == 0) - { - *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); - } - else if(strcmp(ptr, "<") == 0) - { - *From = strtok_s(NULL, seps, &Context); - } - else if (ptr[0] == '$') - *BID = &ptr[1]; - else - { - nodeprintf(conn, "*** Error: Invalid Format\r"); - return FALSE; - } - ptr = strtok_s(NULL, seps, &Context); - } - - // Only allow < from a BBS - - if (*From) - { - if (!(conn->BBSFlags & BBS)) - { - nodeprintf(conn, "*** < can only be used by a BBS\r"); - return FALSE; - } - } - - if (!*From) - *From = conn->UserPointer->Call; - - if (!(conn->BBSFlags & BBS)) - { - // if a normal user, check that TO and/or AT are known and warn if not. - - if (_stricmp(To, "SYSOP") == 0) - { - conn->LocalMsg = TRUE; - return TRUE; - } - - if (!*ATBBS && conn->ToCount == 0) - { - // No routing, if not a user and not known to forwarding or WP warn - - struct UserInfo * ToUser = LookupCall(To); - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->flags & F_RMSREDIRECT) - { - // sent to Winlink - - *ATBBS = Winlink; - nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); - } - else if (ToUser->HomeBBS[0]) - { - *ATBBS = ToUser->HomeBBS; - nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); - } - else - { - conn->LocalMsg = TRUE; - } - } - else - { - conn->LocalMsg = FALSE; - WP = LookupWP(To); - - if (WP) - { - *ATBBS = WP->first_homebbs; - nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); - } - } - } - } - return TRUE; -} - -BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) -{ - struct MsgInfo * Msg, * TestMsg; - char * via = NULL; - char * FromHA; - - // Create a temp msg header entry - - if (conn->ToCount) - { - } - else - { - if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType)) - { - if ((conn->BBSFlags & BBS)) - { - nodeprintf(conn, "NO - REJECTED\r"); - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - else - nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); - - return FALSE; - } - } - - Msg = malloc(sizeof (struct MsgInfo)); - - if (Msg == 0) - { - CriticalErrorHandler("malloc failed for new message header"); - return FALSE; - } - - memset(Msg, 0, sizeof (struct MsgInfo)); - - conn->TempMsg = Msg; - - Msg->type = MsgType; - - if (conn->UserPointer->flags & F_HOLDMAIL) - Msg->status = 'H'; - else - Msg->status = 'N'; - - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - if (BID) - { - BIDRec * TempBID; - - // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message - // If we do, reject it. If not, accept it again. (do we need some loop protection ???) - - TempBID = LookupBID(BID); - - if (TempBID) - { - if (MsgType == 'B') - { - // Duplicate bid - - if ((conn->BBSFlags & BBS)) - { - nodeprintf(conn, "NO - BID\r"); - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - else - nodeprintf(conn, "*** Error- Duplicate BID\r"); - - return FALSE; - } - - TestMsg = GetMsgFromNumber(TempBID->u.msgno); - - // if the same TO we will assume the same message - - if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) - { - // We have this message. If we have already forwarded it, we should accept it again - - if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) - { - // Duplicate bid - - if ((conn->BBSFlags & BBS)) - { - nodeprintf(conn, "NO - BID\r"); - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - else - nodeprintf(conn, "*** Error- Duplicate BID\r"); - - return FALSE; - } - } - } - - if (strlen(BID) > 12) BID[12] = 0; - strcpy(Msg->bid, BID); - - // Save BID in temp list in case we are offered it again before completion - - TempBID = AllocateTempBIDRecord(); - strcpy(TempBID->BID, BID); - TempBID->u.conn = conn; - - } - - if (conn->ToCount) - { - } - else - { - if (_memicmp(ToCall, "rms:", 4) == 0) - { - if (!FindRMS()) - { - nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); - return FALSE; - } - - via=strlop(ToCall, ':'); - _strupr(ToCall); - } - else if (_memicmp(ToCall, "rms/", 4) == 0) - { - if (!FindRMS()) - { - nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); - return FALSE; - } - - via=strlop(ToCall, '/'); - _strupr(ToCall); - } - else if (_memicmp(ToCall, "smtp:", 5) == 0) - { - if (ISP_Gateway_Enabled) - { - if ((conn->UserPointer->flags & F_EMAIL) == 0) - { - nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); - return FALSE; - } - via=strlop(ToCall, ':'); - ToCall[0] = 0; - } - else - { - nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); - return FALSE; - } - } - else - { - _strupr(ToCall); - if (ATBBS) - via=_strupr(ATBBS); - } - - strlop(ToCall, '-'); // Remove any (illegal) ssid - if (strlen(ToCall) > 6) ToCall[6] = 0; - - strcpy(Msg->to, ToCall); - - if (SendBBStoSYSOPCall) - if (_stricmp(ToCall, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); - - if (via) - { - if (strlen(via) > 40) via[40] = 0; - - strcpy(Msg->via, via); - } - - } // End of Multiple Dests - - // Look for HA in From (even if we shouldn't be getting it!) - - FromHA = strlop(From, '@'); - - - strlop(From, '-'); // Remove any (illegal) ssid - if (strlen(From) > 6) From[6] = 0; - strcpy(Msg->from, From); - - if (FromHA) - { - if (strlen(FromHA) > 39) FromHA[39] = 0; - Msg->emailfrom[0] = '@'; - strcpy(&Msg->emailfrom[1], _strupr(FromHA)); - } - - if (Title) // Only used by SR and SC - { - strcpy(Msg->title, Title); - conn->Flags |= GETTINGMESSAGE; - - // Create initial buffer of 10K. Expand if needed later - - conn->MailBuffer=malloc(10000); - conn->MailBufferSize=10000; - - nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); - return TRUE; - } - - if (conn->BBSFlags & FLARQMODE) - return TRUE; - - if (!(conn->BBSFlags & FBBCompressed)) - conn->Flags |= GETTINGTITLE; - - if (!(conn->BBSFlags & BBS)) - nodeprintf(conn, "Enter Title (only):\r"); - else - if (!(conn->BBSFlags & FBBForwarding)) - nodeprintf(conn, "OK\r"); - - return TRUE; -} - -VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) -{ - - conn->Flags &= ~GETTINGTITLE; - - if (msglen == 1) - { - nodeprintf(conn, "*** Message Cancelled\r"); - SendPrompt(conn, user); - return; - } - - if (msglen > 60) msglen = 60; - - Buffer[msglen-1] = 0; - - strcpy(conn->TempMsg->title, Buffer); - - // Create initial buffer of 10K. Expand if needed later - - conn->MailBuffer=malloc(10000); - conn->MailBufferSize=10000; - - if (conn->MailBuffer == NULL) - { - nodeprintf(conn, "Failed to create Message Buffer\r"); - return; - } - - conn->Flags |= GETTINGMESSAGE; - - if (!conn->BBSFlags & BBS) - nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); - -} - -VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) -{ - char * ptr2 = NULL; - - if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) - { - int Index = 0; - - if (conn->TempMsg->type == 'P') - Index = PMSG; - else if (conn->TempMsg->type == 'B') - Index = BMSG; - else if (conn->TempMsg->type == 'T') - Index = TMSG; - - conn->Flags &= ~GETTINGMESSAGE; - - user->Total.MsgsReceived[Index]++; - user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; - - if (conn->ToCount) - { - // Multiple recipients - - struct MsgInfo * Msg = conn->TempMsg; - int i; - struct MsgInfo * SaveMsg = Msg; - char * SaveBody = conn->MailBuffer; - int SaveMsgLen = Msg->length; - BOOL SentToRMS = FALSE; - int ToLen = 0; - char * ToString = zalloc(conn->ToCount * 100); - - // If no BID provided, allocate one - - if (Msg->bid[0] == 0) - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); - - for (i = 0; i < conn->ToCount; i++) - { - char * Addr = conn->To[i]; - char * Via; - - if (_memicmp (Addr, "SMTP:", 5) == 0) - { - // For Email - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 10); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - Msg->to[0] = 0; - strcpy(Msg->via, &Addr[5]); - - CreateMessageFromBuffer(conn); - continue; - } - - if (_memicmp (Addr, "RMS:", 4) == 0) - { - // Add to B2 Message for RMS - - Addr+=4; - - Via = strlop(Addr, '@'); - - if (Via && _stricmp(Via, "winlink.org") == 0) - { - if (CheckifLocalRMSUser(Addr)) - { - // Local RMS - Leave Here - - Via = 0; // Drop Through - goto PktMsg; - } - else - { - ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); - continue; - } - } - - ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, Addr, Via); - continue; - } - - _strupr(Addr); - - Via = strlop(Addr, '@'); - - if (Via && _stricmp(Via, "winlink.org") == 0) - { - if (CheckifLocalRMSUser(Addr)) - { - // Local RMS - Leave Here - - Via = 0; // Drop Through - } - else - { - ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); - - // Add to B2 Message for RMS - - continue; - } - } - - PktMsg: - - conn->LocalMsg = FALSE; - - // Normal BBS Message - - if (_stricmp(Addr, "SYSOP") == 0) - conn->LocalMsg = TRUE; - else - { - struct UserInfo * ToUser = LookupCall(Addr); - - if (ToUser) - conn->LocalMsg = TRUE; - } - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 10); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - strcpy(Msg->to, Addr); - - if (Via) - { - Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe - strcpy(Msg->via, Via); - } - - CreateMessageFromBuffer(conn); - } - - if (ToLen) - { - char * B2Hddr = zalloc(ToLen + 1000); - int B2HddrLen; - char DateString[80]; - struct tm * tm; - time_t Date = time(NULL); - char Type[16] = "Private"; - - // Get Type - - if (conn->TempMsg->type == 'B') - strcpy(Type, "Bulletin"); - else if (conn->TempMsg->type == 'T') - strcpy(Type, "Traffic"); - - tm = gmtime(&Date); - - sprintf(DateString, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); - - Msg->B2Flags = B2Msg; - - B2HddrLen = sprintf(B2Hddr, - "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", - SaveMsg->bid, DateString, Type, - SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); - - memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); - memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); - - Msg->length += B2HddrLen; - - strcpy(Msg->to, "RMS"); - - CreateMessageFromBuffer(conn); - - free(B2Hddr); - } - - free(SaveMsg); - free(SaveBody); - conn->MailBuffer = NULL; - conn->MailBufferSize=0; - - if (!(conn->BBSFlags & BBS)) - SendPrompt(conn, conn->UserPointer); - else - if (!(conn->BBSFlags & FBBForwarding)) - { - if (conn->BBSFlags & OUTWARDCONNECT) - BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward - else - BBSputs(conn, ">\r"); - } - - /* - // From a client - Create one copy with all RMS recipients, and another for each packet recipient - - // Merge all RMS To: lines - - ToLen = 0; - ToString[0] = 0; - - for (i = 0; i < Recipients; i++) - { - if (LocalMsg[i]) - continue; // For a local RMS user - - if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || - _stricmp(RecpTo[i], "RMS") == 0) - { - ToLen += strlen(HddrTo[i]); - strcat(ToString, HddrTo[i]); - } - } - - if (ToLen) - { - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 1000); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - - memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); - memcpy(&conn->MailBuffer[B2To], ToString, ToLen); - - conn->TempMsg->length += ToLen; - - strcpy(Msg->to, "RMS"); - strcpy(Msg->via, "winlink.org"); - - // Must Change the BID - - Msg->bid[0] = 0; - - CreateMessageFromBuffer(conn); - } - - } - - free(ToString); - - for (i = 0; i < Recipients; i++) - { - // Only Process Non - RMS Dests or local RMS Users - - if (LocalMsg[i] == 0) - if (_stricmp (Via[i], "WINLINK.ORG") == 0 || - _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || - _stricmp(RecpTo[i], "RMS") == 0) - continue; - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 1000); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - // Add our To: - - ToLen = strlen(HddrTo[i]); - - if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: - memcpy(HddrTo[i], "To", 2); - - memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); - memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); - - conn->TempMsg->length += ToLen; - - strcpy(Msg->to, RecpTo[i]); - strcpy(Msg->via, Via[i]); - - Msg->bid[0] = 0; - - CreateMessageFromBuffer(conn); - } - } // End not from RMS - - free(SaveMsg); - free(SaveBody); - conn->MailBuffer = NULL; - conn->MailBufferSize=0; - - SetupNextFBBMessage(conn); - return; - - } My__except_Routine("Process Multiple Destinations"); - - BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - - return; -*/ - - conn->ToCount = 0; - - return; - } - - - CreateMessageFromBuffer(conn); - return; - - } - - Buffer[msglen++] = 0x0a; - - if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - nodeprintf(conn, "Failed to extend Message Buffer\r"); - - conn->Flags &= ~GETTINGMESSAGE; - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); - - conn->TempMsg->length += msglen; -} - -VOID CreateMessageFromBuffer(CIRCUIT * conn) -{ - struct MsgInfo * Msg; - BIDRec * BIDRec; - char * ptr1, * ptr2 = NULL; - char * ptr3, * ptr4; - int FWDCount = 0; - char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; - time_t Age; - int OurCount; - char * HoldReason = "User has Hold Messages flag set"; - struct UserInfo * user; - - -#ifndef LINBPQ - struct _EXCEPTION_POINTERS exinfo; -#endif - - // If doing SC, Append Old Message - - if (conn->CopyBuffer) - { - if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) - { - conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - nodeprintf(conn, "Failed to extend Message Buffer\r"); - - conn->Flags &= ~GETTINGMESSAGE; - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); - - conn->TempMsg->length += (int)strlen(OldMess); - - memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); - - conn->TempMsg->length += (int)strlen(conn->CopyBuffer); - - free(conn->CopyBuffer); - conn->CopyBuffer = NULL; - } - - // Allocate a message Record slot - - Msg = AllocateMsgRecord(); - memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); - - free(conn->TempMsg); - - // Set number here so they remain in sequence - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - FreeSemaphore(&MsgNoSemaphore); - MsgnotoMsg[Msg->number] = Msg; - - if (Msg->status == 0) - Msg->status = 'N'; - - // Create BID if non supplied - - if (Msg->bid[0] == 0) - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - // if message body had R: lines, get date created from last (not very accurate, but best we can do) - - // Also check if we have had message before to detect loops - - ptr1 = conn->MailBuffer; - OurCount = 0; - - // If it is a B2 Message, Must Skip B2 Header - - if (Msg->B2Flags & B2Msg) - { - ptr1 = strstr(ptr1, "\r\n\r\n"); - if (ptr1) - ptr1 += 4; - else - ptr1 = conn->MailBuffer; - } - -nextline: - - if (memcmp(ptr1, "R:", 2) == 0) - { - // Is if ours? - - // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 - - ptr3 = strchr(ptr1, '@'); - ptr4 = strchr(ptr1, '.'); - - if (ptr3 && ptr4 && (ptr4 > ptr3)) - { - if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) - OurCount++; - } - - GetWPBBSInfo(ptr1); // Create WP /I record from R: Line - - // see if another - - ptr2 = ptr1; // save - ptr1 = strchr(ptr1, '\r'); - if (ptr1 == 0) - { - Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); - return; - } - ptr1++; - if (*ptr1 == '\n') ptr1++; - - goto nextline; - } - - // ptr2 points to last R: line (if any) - - if (ptr2) - { - struct tm rtime; - time_t result; - - memset(&rtime, 0, sizeof(struct tm)); - - if (ptr2[10] == '/') - { - // Dodgy 4 char year - - sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); - rtime.tm_year -= 1900; - rtime.tm_mon--; - } - else if (ptr2[8] == '/') - { - sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); - - if (rtime.tm_year < 90) - rtime.tm_year += 100; // Range 1990-2089 - rtime.tm_mon--; - } - - // Otherwise leave date as zero, which should be rejected - - // result = _mkgmtime(&rtime); - - if ((result = mktime(&rtime)) != (time_t)-1 ) - { - result -= (time_t)_MYTIMEZONE; - - Msg->datecreated = result; - Age = (time(NULL) - result)/86400; - - if ( Age < -7) - { - Msg->status = 'H'; - HoldReason = "Suspect Date Sent"; - } - else if (Age > BidLifetime || Age > MaxAge) - { - Msg->status = 'H'; - HoldReason = "Message too old"; - - } - else - GetWPInfoFromRLine(Msg->from, ptr2, result); - } - else - { - // Can't decode R: Datestamp - - Msg->status = 'H'; - HoldReason = "Corrupt R: Line - can't determine age"; - } - - if (OurCount > 1) - { - // Message is looping - - Msg->status = 'H'; - HoldReason = "Message may be looping"; - - } - } - - if (strcmp(Msg->to, "WP") == 0) - { - // If Reject WP Bulls is set, Kill message here. - // It should only get here if B2 - otherwise it should be - // rejected earlier - - if (Msg->type == 'B' && FilterWPBulls) - Msg->status = 'K'; - - } - - conn->MailBuffer[Msg->length] = 0; - - if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) - { - Msg->status = 'H'; - HoldReason = "Bad word in title or body"; - } - - if (CheckHoldFilters(Msg->from, Msg->to, Msg->via, Msg->bid)) - { - Msg->status = 'H'; - HoldReason = "Matched Hold Filters"; - } - - if (CheckValidCall(Msg->from) == 0) - { - Msg->status = 'H'; - HoldReason = "Probable Invalid From Call"; - } - - // Process any WP Messages - - if (strcmp(Msg->to, "WP") == 0) - { - if (Msg->status == 'N') - { - ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); - - if (Msg->type == 'P') // Kill any processed private WP messages. - { - char VIA[80]; - - strcpy(VIA, Msg->via); - strlop(VIA, '.'); - - if (strcmp(VIA, BBSName) == 0) - Msg->status = 'K'; - } - } - } - - CreateMessageFile(conn, Msg); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - if (Msg->length > MaxTXSize) - { - Msg->status = 'H'; - HoldReason = "Message too long"; - - if (!(conn->BBSFlags & BBS)) - nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); - } - - // Check for message to internal server - - if (Msg->via[0] == 0 - || _stricmp(Msg->via, BBSName) == 0 // our BBS a - || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address - { - if (CheckforMessagetoServer(Msg)) - { - // Flag as killed and send prompt - - FlagAsKilled(Msg, TRUE); - - if (!(conn->BBSFlags & BBS)) - { - nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); - SendPrompt(conn, conn->UserPointer); - } - return; // no need to process further - } - } - - if (Msg->to[0]) - FWDCount = MatchMessagetoBBSList(Msg, conn); - else - { - // If addressed @winlink.org, and to a local user, Keep here. - - char * Call; - char * AT; - - // smtp or rms - don't warn no route - - FWDCount = 1; - - Call = _strupr(_strdup(Msg->via)); - AT = strlop(Call, '@'); - - if (AT && _stricmp(AT, "WINLINK.ORG") == 0) - { - struct UserInfo * user = LookupCall(Call); - - if (user) - { - if (user->flags & F_POLLRMS) - { - Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); - strcpy(Msg->to, Call); - strcpy(Msg->via, AT); - if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it - set_fwd_bit(Msg->fbbs, user->BBSNumber); - - } - } - } - free(Call); - } - - // Warn SYSOP if P or T forwarded in, and has nowhere to go - - if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && - strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) - { - if (Msg->via[0]) - { - if (_stricmp(Msg->via, BBSName)) // Not for our BBS a - if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address - SendWarningToSYSOP(Msg); - } - else - { - // No via - is it for a local user? - - if (LookupCall(Msg->to) == 0) - SendWarningToSYSOP(Msg); - } - } - - if ((conn->BBSFlags & SYNCMODE) == 0) - { - if (!(conn->BBSFlags & BBS)) - { - nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); - - if (Msg->via[0]) - { - if (_stricmp(Msg->via, BBSName)) // Not for our BBS a - if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address - - if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg - nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); - } - else - { - if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') - // Not Local and no forward route - nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); - } - if (conn->ToCount == 0) - SendPrompt(conn, conn->UserPointer); - } - else - { - if (!(conn->BBSFlags & FBBForwarding)) - { - if (conn->ToCount == 0) - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - } - } - - if(Msg->to[0] == 0) - SMTPMsgCreated=TRUE; - - if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - - if (Msg->status == 'H') - { - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); - sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); - SendMessageToSYSOP(Title, MailBuffer, Length); - } - - BuildNNTPList(Msg); // Build NNTP Groups list - - SaveMessageDatabase(); - SaveBIDDatabase(); - - // If Event Notifications enabled report a new message event - - if (reportNewMesageEvents) - { - char msg[200]; - - //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject - - struct tm *tm = gmtime((time_t *)&Msg->datecreated); - - sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", - Msg->number, Msg->type, Msg->length, Msg->to, - Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); - -#ifdef WIN32 - if (pRunEventProgram) - pRunEventProgram("MailNewMsg.exe", msg); -#else - { - char prog[256]; - sprintf(prog, "%s/%s", BPQDirectory, "MailNewMsg"); - RunEventProgram(prog, msg); - } -#endif - } - - - if (EnableUI) -#ifdef LINBPQ - SendMsgUI(Msg); -#else - __try - { - SendMsgUI(Msg); - } - My__except_Routine("SendMsgUI"); -#endif - - user = LookupCall(Msg->to); - - if (user && (user->flags & F_APRSMFOR)) - { - char APRS[128]; - char Call[16]; - int SSID = user->flags >> 28; - - if (SSID) - sprintf(Call, "%s-%d", Msg->to, SSID); - else - strcpy(Call, Msg->to); - - sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); - APISendAPRSMessage(APRS, Call); - } - - return; -} - -VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) -{ - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - char Mess[255]; - int len; - BOOL AutoImport = FALSE; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is - // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines - - if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) - { - UCHAR * ptr = conn->MailBuffer; - - // If it is a B2 Message, Must Skip B2 Header - - if (Msg->B2Flags & B2Msg) - { - ptr = strstr(ptr, "\r\n\r\n"); - if (ptr) - ptr += 4; - else - ptr = conn->MailBuffer; - } - - if (memcmp(ptr, "R:", 2) == 0) - { - ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines - if (ptr) - ptr += 4; - } - - if (*(ptr) == 'S' && ptr[2] == ' ') - { - int HeaderLen = (int)(ptr - conn->MailBuffer); - Msg->length -= HeaderLen; - memmove(conn->MailBuffer, ptr, Msg->length); - Msg->status = 'K'; - AutoImport = TRUE; - } - } - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - if (AutoImport) - ImportMessages(NULL, MsgFile, TRUE); - - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - - if (WriteLen != Msg->length) - { - len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); - QueueMsg(conn, Mess, len); - Debugprintf(Mess); - } - return; -} - - - - -VOID SendUnbuffered(int stream, char * msg, int len) -{ -#ifndef LINBPQ - if (stream < 0) - WritetoConsoleWindow(stream, msg, len); - else -#endif - SendMsg(stream, msg, len); -} - -BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); - -BOOL FindMessagestoForward (CIRCUIT * conn) -{ - struct UserInfo * user = conn->UserPointer; - -#ifndef LINBPQ - - struct _EXCEPTION_POINTERS exinfo; - - __try { -#endif - - // This is a hack so Winpack or WLE users can use forwarding - // protocols to get their messages without being defined as a BBS - - // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! - // !! Anything sent using this must be killed if sent or rejected. - - // I'm not sure about this. I think I only need the PacLinkCalls - // if from RMS Express, and it always sends an FW; line - // Ah, no. What about WinPack ?? - // If from RMS Express must have Temp_B2 or BBS set or SID will - // be rejected. So maybe just Temp_B2 && BBS = 0?? - // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? - - if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) - { - if (conn->PacLinkCalls == NULL) - { - // create a list with just the user call - - char * ptr1; - - conn->PacLinkCalls = zalloc(30); - - ptr1 = (char *)conn->PacLinkCalls; - ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) - strcpy(ptr1, user->Call); - - conn->PacLinkCalls[0] = ptr1; - } - } - - if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) - { - conn->LastForwardType = 'T'; - return TRUE; - } - - if (conn->LastForwardType == 'T') - conn->NextMessagetoForward = FirstMessageIndextoForward; - - if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) - { - conn->LastForwardType = 'P'; - return TRUE; - } - - if (conn->LastForwardType == 'P') - conn->NextMessagetoForward = FirstMessageIndextoForward; - - if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) - { - conn->LastForwardType = 'B'; - return TRUE; - } - - conn->LastForwardType = 0; - return FALSE; -#ifndef LINBPQ - } My__except_Routine("FindMessagestoForward"); -#endif - return FALSE; - -} - - -BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) -{ - // See if any messages are queued for this BBS - - int m; - struct MsgInfo * Msg; - struct UserInfo * user = conn->UserPointer; - struct FBBHeaderLine * FBBHeader; - BOOL Found = FALSE; - char RLine[100]; - int TotalSize = 0; - time_t NOW = time(NULL); - -// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", -// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); - - if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset - conn->NextMessagetoForward = 1; - - conn->FBBIndex = 0; - - for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - // If an NTS MPS, see if anything matches - - if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) - { - struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; - int depth; - - if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) - { - depth = CheckBBSToForNTS(Msg, ForwardingInfo); - - if (depth > -1 && Msg->Locked == 0) - goto Forwardit; - - depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); - - if (depth && Msg->Locked == 0) - goto Forwardit; - - depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); - - if (depth > -1 && Msg->Locked == 0) - goto Forwardit; - } - } - - // If forwarding to Paclink or RMS Express, look for any message matching the - // requested call list with status 'N' (maybe should also be 'P' ??) - - if (conn->PacLinkCalls) - { - int index = 1; - - char * Call = conn->PacLinkCalls[0]; - - while (Call) - { - if (Msg->type == Type && Msg->status == 'N') - { -// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); - if (_stricmp(Msg->to, Call) == 0) - if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) - goto Forwardit; - else - Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); - } - Call = conn->PacLinkCalls[index++]; - } -// continue; - } - - if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') - && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) - { - // Message to be sent - do a consistancy check (State, etc) - - Forwardit: - - if (Msg->Defered > 0) // = response received - { - Msg->Defered--; - Debugprintf("Message %d deferred", Msg->number); - continue; - } - - if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) - { - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); - sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); - SendMessageToSYSOP(Title, MailBuffer, Length); - - Msg->status = 'H'; - continue; - } - - conn->NextMessagetoForward = m + 1; // So we don't offer again if defered - - Msg->Locked = 1; // So other MPS can't pick it up - - // if FBB forwarding add to list, eise save pointer - - if (conn->BBSFlags & FBBForwarding) - { - struct tm *tm; - time_t temp; - - FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; - - FBBHeader->FwdMsg = Msg; - FBBHeader->MsgType = Msg->type; - FBBHeader->Size = Msg->length; - TotalSize += Msg->length; - strcpy(FBBHeader->From, Msg->from); - strcpy(FBBHeader->To, Msg->to); - strcpy(FBBHeader->ATBBS, Msg->via); - strcpy(FBBHeader->BID, Msg->bid); - - // Set up R:Line, so se can add its length to the sise - - memcpy(&temp, &Msg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - Msg->number, BBSName, HRoute, RlineVer); - - // If using B2 forwarding we need the message size and Compressed size for FC proposal - - if (conn->BBSFlags & FBBB2Mode) - { - if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) - { - char * MailBuffer = malloc(100); - char Title[100]; - int Length; - - // Corrupt B2 Message - - Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); - Msg->status = 'H'; - SaveMessageDatabase(); - - conn->FBBIndex--; - TotalSize -= Msg->length; - memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); - - Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); - sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); - SendMessageToSYSOP(Title, MailBuffer, Length); - - continue; - } - } - - if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) - return TRUE; // Got max number or too big - - Found = TRUE; // Remember we have some - } - else - { - conn->FwdMsg = Msg; - return TRUE; - } - } - } - - return Found; -} - -BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) -{ - // See if any messages are queued for this BBS - - // if Conn is not NULL, also check Msg Type - - int m; - struct MsgInfo * Msg; - - for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) - { - if (conn) - { - char Type = Msg->type; - - if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) - { -// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); - return TRUE; - } - } - else - { -// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); - return TRUE; - } - } - } - - return FALSE; -} - -int CountMessagestoForward (struct UserInfo * user) -{ - // See if any messages are queued for this BBS - - int m, n=0; - struct MsgInfo * Msg; - int BBSNumber = user->BBSNumber; - int FirstMessage = FirstMessageIndextoForward; - - if ((user->flags & F_NTSMPS)) - FirstMessage = 1; - - for (m = FirstMessage; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) - { - n++; - continue; // So we dont count twice in Flag set and NTS MPS - } - - // if an NTS MPS, also check for any matches - - if (Msg->type == 'T' && (user->flags & F_NTSMPS)) - { - struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; - int depth; - - if (Msg->status == 'N' && ForwardingInfo) - { - depth = CheckBBSToForNTS(Msg, ForwardingInfo); - - if (depth > -1 && Msg->Locked == 0) - { - n++; - continue; - } - depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); - - if (depth && Msg->Locked == 0) - { - n++; - continue; - } - - depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); - - if (depth > -1 && Msg->Locked == 0) - { - n++; - continue; - } - } - } - } - - return n; -} - -int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) -{ - // See if any messages are queued for this BBS - - int m, n=0; - struct MsgInfo * Msg; - int BBSNumber = user->BBSNumber; - int FirstMessage = FirstMessageIndextoForward; - - if ((user->flags & F_NTSMPS)) - FirstMessage = 1; - - for (m = FirstMessage; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; // So we dont count twice in Flag set and NTS MPS - } - - // if an NTS MPS, also check for any matches - - if (Msg->type == 'T' && (user->flags & F_NTSMPS)) - { - struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; - int depth; - - if (Msg->status == 'N' && ForwardingInfo) - { - depth = CheckBBSToForNTS(Msg, ForwardingInfo); - - if (depth > -1 && Msg->Locked == 0) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; - } - depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); - - if (depth && Msg->Locked == 0) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; - } - - depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); - - if (depth > -1 && Msg->Locked == 0) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; - } - } - } - } - - return n; -} - -VOID SendWarningToSYSOP(struct MsgInfo * Msg) -{ - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go", Msg->number); - sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); - SendMessageToSYSOP(Title, MailBuffer, Length); -} - - - -VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) -{ - struct MsgInfo * Msg = AllocateMsgRecord(); - BIDRec * BIDRec; - - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - - Msg->length = Length; - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - - FreeSemaphore(&MsgNoSemaphore); - - strcpy(Msg->from, "SYSTEM"); - if (SendSYStoSYSOPCall) - strcpy(Msg->to, SYSOPCall); - else - strcpy(Msg->to, "SYSOP"); - - strcpy(Msg->title, Title); - - Msg->type = 'P'; - Msg->status = 'N'; - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - BIDRec = AllocateBIDRecord(); - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - MatchMessagetoBBSList(Msg, NULL); - free(MailBuffer); -} - -VOID CheckBBSNumber(int i) -{ - // Make sure number is unique - - int Count = 0; - struct UserInfo * user; - - for (user = BBSChain; user; user = user->BBSNext) - { - if (user->BBSNumber == i) - { - Count++; - - if (Count > 1) - { - // Second with same number - Renumber this one - - user->BBSNumber = FindFreeBBSNumber(); - - if (user->BBSNumber == 0) - user->BBSNumber = NBBBS; // cant really do much else - - Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); - - } - } - } -} - - -int FindFreeBBSNumber() -{ - // returns the lowest number not used by any bbs or message. - - struct MsgInfo * Msg; - struct UserInfo * user; - int i, m; - - for (i = 1; i<= NBBBS; i++) - { - for (user = BBSChain; user; user = user->BBSNext) - { - if (user->BBSNumber == i) - goto nexti; // In use - } - - // Not used by BBS - check messages - - for (m = 1; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if (check_fwd_bit(Msg->fbbs, i)) - goto nexti; // In use - - if (check_fwd_bit(Msg->forw, i)) - goto nexti; // In use - } - - // Not in Use - - return i; - -nexti:; - - } - - return 0; // All used -} - -BOOL SetupNewBBS(struct UserInfo * user) -{ - user->BBSNumber = FindFreeBBSNumber(); - - if (user->BBSNumber == 0) - return FALSE; - - user->BBSNext = BBSChain; - BBSChain = user; - - SortBBSChain(); - - ReinitializeFWDStruct(user); - - return TRUE; -} - -VOID DeleteBBS(struct UserInfo * user) -{ - struct UserInfo * BBSRec, * PrevBBS = NULL; - -#ifndef LINBPQ - RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); -#endif - for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) - { - if (user == BBSRec) - { - if (PrevBBS == NULL) // First in chain; - { - BBSChain = BBSRec->BBSNext; - break; - } - PrevBBS->BBSNext = BBSRec->BBSNext; - break; - } - } -} - - -VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); - -VOID SetupForwardingStruct(struct UserInfo * user) -{ - struct BBSForwardingInfo * ForwardingInfo; - - char Key[100] = "BBSForwarding."; - char Temp[100]; - - HKEY hKey=0; - char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; - - int m; - struct MsgInfo * Msg; - - ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - - if (UsingingRegConfig == 0) - { - // Config from file - - if (isdigit(user->Call[0]) || user->Call[0] == '_') - strcat(Key, "*"); - - strcat(Key, user->Call); - - group = config_lookup (&cfg, Key); - - if (group == NULL) // No info - return; - else - { - ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); - ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); - ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); - ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); - ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); - ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); - - ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); - ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); - ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); - ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); - ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); - ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); - ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); - - if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) - ForwardingInfo->AllowCompressed = TRUE; - - if (ForwardingInfo->AllowCompressed) - ForwardingInfo->AllowBlocked = TRUE; - - ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); - ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); - ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); - ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); - ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); - ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); - - if (ForwardingInfo->MaxFBBBlockSize == 0) - ForwardingInfo->MaxFBBBlockSize = 10000; - - if (ForwardingInfo->FwdInterval == 0) - ForwardingInfo->FwdInterval = 3600; - - if (ForwardingInfo->ConTimeout == 0) - ForwardingInfo->ConTimeout = 120; - - GetStringValue(group, "BBSHA", Temp); - - if (Temp[0]) - ForwardingInfo->BBSHA = _strdup(Temp); - else - ForwardingInfo->BBSHA = _strdup(""); - } - } - else - { -#ifndef LINBPQ - - int retCode,Type,Vallen; - - strcat(RegKey, user->Call); - retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); - - if (retCode != ERROR_SUCCESS) - return; - else - { - ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); - ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); - ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); - ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); - ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); - ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "Enabled", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "RequestReverse", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); - - if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) - ForwardingInfo->AllowCompressed = TRUE; - - Vallen=4; - retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); - - Vallen=4; - RegQueryValueEx(hKey,"FWDInterval",0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); - - Vallen=4; - RegQueryValueEx(hKey,"RevFWDInterval",0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); - - RegQueryValueEx(hKey,"MaxFBBBlock",0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); - - if (ForwardingInfo->MaxFBBBlockSize == 0) - ForwardingInfo->MaxFBBBlockSize = 10000; - - if (ForwardingInfo->FwdInterval == 0) - ForwardingInfo->FwdInterval = 3600; - - Vallen=0; - retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); - - if (retCode != 0) - { - // No Key - Get from WP?? - - WPRec * ptr = LookupWP(user->Call); - - if (ptr) - { - if (ptr->first_homebbs) - { - ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); - } - } - } - - if (Vallen) - { - ForwardingInfo->BBSHA = malloc(Vallen); - RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); - } - - RegCloseKey(hKey); - } -#endif - } - - // Convert FWD Times and H Addresses - - if (ForwardingInfo->FWDTimes) - SetupFwdTimes(ForwardingInfo); - - if (ForwardingInfo->Haddresses) - SetupHAddreses(ForwardingInfo); - - if (ForwardingInfo->HaddressesP) - SetupHAddresesP(ForwardingInfo); - - if (ForwardingInfo->BBSHA) - { - if (ForwardingInfo->BBSHA[0]) - SetupHAElements(ForwardingInfo); - else - { - free(ForwardingInfo->BBSHA); - ForwardingInfo->BBSHA = NULL; - } - } - - for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - // If any forward bits are set, increment count on BBS record. - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) - { - user->ForwardingInfo->MsgCount++; - } - } - } -} - -VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) -{ - char * ptr1; - char * MultiString = NULL; - const char * ptr; - int Count = 0; - char ** Value; - config_setting_t *setting; - char * Save; - - Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values - Value[0] = NULL; - - setting = config_setting_get_member (group, ValueName); - - if (setting) - { - ptr = config_setting_get_string (setting); - - Save = _strdup(ptr); // DOnt want to change config string - ptr = Save; - - while (ptr && strlen(ptr)) - { - ptr1 = strchr(ptr, '|'); - - if (ptr1) - *(ptr1++) = 0; - - if (strlen(ptr)) // ignore null elements - { - Value = realloc(Value, (Count+2) * sizeof(void *)); - Value[Count++] = _strdup(ptr); - } - ptr = ptr1; - } - free(Save); - } - - Value[Count] = NULL; - return Value; -} - - -VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) -{ -#ifdef LINBPQ - return NULL; -#else - int retCode,Type,Vallen; - char * MultiString = NULL; - int ptr, len; - int Count = 0; - char ** Value; - - Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values - - Value[0] = NULL; - - Vallen=0; - - - retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); - - if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring - { - free(Value); - return FALSE; - } - - MultiString = malloc(Vallen); - - retCode = RegQueryValueEx(hKey, ValueName, 0, - (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); - - ptr=0; - - while (MultiString[ptr]) - { - len=strlen(&MultiString[ptr]); - - Value = realloc(Value, (Count+2) * sizeof(void *)); - Value[Count++] = _strdup(&MultiString[ptr]); - ptr+= (len + 1); - } - - Value[Count] = NULL; - - free(MultiString); - - return Value; -#endif -} - -VOID FreeForwardingStruct(struct UserInfo * user) -{ - struct BBSForwardingInfo * ForwardingInfo; - int i; - - - ForwardingInfo = user->ForwardingInfo; - - FreeList(ForwardingInfo->TOCalls); - FreeList(ForwardingInfo->ATCalls); - FreeList(ForwardingInfo->Haddresses); - FreeList(ForwardingInfo->HaddressesP); - - i=0; - if(ForwardingInfo->HADDRS) - { - while(ForwardingInfo->HADDRS[i]) - { - FreeList(ForwardingInfo->HADDRS[i]); - i++; - } - free(ForwardingInfo->HADDRS); - free(ForwardingInfo->HADDROffet); - } - - i=0; - if(ForwardingInfo->HADDRSP) - { - while(ForwardingInfo->HADDRSP[i]) - { - FreeList(ForwardingInfo->HADDRSP[i]); - i++; - } - free(ForwardingInfo->HADDRSP); - } - - FreeList(ForwardingInfo->ConnectScript); - FreeList(ForwardingInfo->FWDTimes); - if (ForwardingInfo->FWDBands) - { - i=0; - while(ForwardingInfo->FWDBands[i]) - { - free(ForwardingInfo->FWDBands[i]); - i++; - } - free(ForwardingInfo->FWDBands); - } - if (ForwardingInfo->BBSHAElements) - { - i=0; - while(ForwardingInfo->BBSHAElements[i]) - { - free(ForwardingInfo->BBSHAElements[i]); - i++; - } - free(ForwardingInfo->BBSHAElements); - } - free(ForwardingInfo->BBSHA); - -} - -VOID FreeList(char ** Hddr) -{ - VOID ** Save; - - if (Hddr) - { - Save = (void **)Hddr; - while(Hddr[0]) - { - free(Hddr[0]); - Hddr++; - } - free(Save); - } -} - - -VOID ReinitializeFWDStruct(struct UserInfo * user) -{ - if (user->ForwardingInfo) - { - FreeForwardingStruct(user); - free(user->ForwardingInfo); - } - - SetupForwardingStruct(user); - -} - -VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) -{ - char ** Times = ForwardingInfo->FWDTimes; - int Start, End; - int Count = 0; - - ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); - - if (Times) - { - while(Times[0]) - { - ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); - ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); - - Start = atoi(Times[0]); - End = atoi(&Times[0][5]); - - ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; - ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; - - Count++; - Times++; - } - ForwardingInfo->FWDBands[Count] = NULL; - } -} -void StartForwarding(int BBSNumber, char ** TempScript) -{ - struct UserInfo * user; - struct BBSForwardingInfo * ForwardingInfo ; - time_t NOW = time(NULL); - - - for (user = BBSChain; user; user = user->BBSNext) - { - // See if any messages are queued for this BBS - - ForwardingInfo = user->ForwardingInfo; - - if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) - if (ForwardingInfo) - if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable - if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) - if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || - (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse - { - user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used - - // See if TempScript requested - - if (user->ForwardingInfo->TempConnectScript) - FreeList(user->ForwardingInfo->TempConnectScript); - - user->ForwardingInfo->TempConnectScript = TempScript; - - if (ConnecttoBBS(user)) - ForwardingInfo->Forwarding = TRUE; - } - } - - return; -} - -size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) -{ - if (_File) - return fwrite(_Str, _Size, _Count, _File); - - // Appending to MailBuffer - - memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); - conn->InputLen += (int)_Count; - - return _Count; -} - - -BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) -{ - BOOL AddCRLF = FALSE; - BOOL AutoImport = FALSE; - FILE * Handle = NULL; - char * Context; - BOOL Email = FALSE; - time_t now = time(NULL); - char * param; - - FN = strtok_s(FN, " ,", &Context); - - param = strtok_s(NULL, " ,", &Context); - - if (param) - { - if (_stricmp(param, "ADDCRLF") == 0) - AddCRLF = TRUE; - - if (_stricmp(param, "AutoImport") == 0) - AutoImport = TRUE; - - param = strtok_s(NULL, " ,", &Context); - - if (param) - { - if (_stricmp(param, "ADDCRLF") == 0) - AddCRLF = TRUE; - - if (_stricmp(param, "AutoImport") == 0) - AutoImport = TRUE; - - } - } - // If FN is an email address, write to a temp file, and send via rms or emali gateway - - if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) - { - Email = TRUE; - AddCRLF =TRUE; - conn->MailBuffer=malloc(100000); - conn->MailBufferSize=100000; - conn->InputLen = 0; - } - else - { - Handle = fopen(FN, "ab"); - - if (Handle == NULL) - { - int err = GetLastError(); - Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); - return FALSE; - } - } - - while (FindMessagestoForward(conn)) - { - struct MsgInfo * Msg; - struct tm * tm; - time_t temp; - char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); - int MsgLen; - char * MsgPtr; - char Line[256]; - int len; - struct UserInfo * user = conn->UserPointer; - int Index = 0; - - Msg = conn->FwdMsg; - - if (Email) - if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) - break; - - if (Msg->type == 'P') - Index = PMSG; - else if (Msg->type == 'B') - Index = BMSG; - else if (Msg->type == 'T') - Index = TMSG; - - - if (Msg->via[0]) - len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, - Msg->via, Msg->from, Msg->bid); - else - len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); - - fwritex(conn, Line, 1, len, Handle); - - len = sprintf(Line, "%s\r\n", Msg->title); - fwritex(conn, Line, 1, len, Handle); - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r\n"); - conn->FwdMsg->length = (int)strlen(MsgBytes); - } - - MsgPtr = MsgBytes; - MsgLen = conn->FwdMsg->length; - - // If a B2 Message, remove B2 Header - - if (conn->FwdMsg->B2Flags & B2Msg) - { - // Remove all B2 Headers, and all but the first part. - - MsgPtr = strstr(MsgBytes, "Body:"); - - if (MsgPtr) - { - MsgLen = atoi(&MsgPtr[5]); - MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (MsgPtr) - MsgPtr +=4; - else - MsgPtr = MsgBytes; - - } - else - MsgPtr = MsgBytes; - } - - memcpy(&temp, &Msg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - conn->FwdMsg->number, BBSName, HRoute, RlineVer); - - fwritex(conn, Line, 1, len, Handle); - - if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - fwritex(conn, "\r\n", 1, 2, Handle); - - fwritex(conn, MsgPtr, 1, MsgLen, Handle); - - if (MsgPtr[MsgLen - 2] == '\r') - fwritex(conn, "/EX\r\n", 1, 5, Handle); - else - fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); - - if (AddCRLF) - fwritex(conn, "\r\n", 1, 2, Handle); - - free(MsgBytes); - - user->Total.MsgsSent[Index]++; - user->Total.BytesForwardedOut[Index] += MsgLen; - - Msg->datechanged = now; - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - conn->UserPointer->ForwardingInfo->MsgCount--; - } - - if (Email) - { - struct MsgInfo * Msg; - BIDRec * BIDRec; - - if (conn->InputLen == 0) - { - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - - return TRUE; - } - - // Allocate a message Record slot - - Msg = AllocateMsgRecord(); - - // Set number here so they remain in sequence - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - FreeSemaphore(&MsgNoSemaphore); - MsgnotoMsg[Msg->number] = Msg; - - Msg->type = 'P'; - Msg->status = 'N'; - Msg->datecreated = Msg->datechanged = Msg->datereceived = now; - - strcpy(Msg->from, BBSName); - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - if (AutoImport) - sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); - else - sprintf(Msg->title, "Batched messages from BBS %s", BBSName); - - Msg->length = conn->InputLen; - CreateMessageFile(conn, Msg); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - if (_memicmp(FN, "SMTP:", 5) == 0) - { - strcpy(Msg->via, &FN[5]); - SMTPMsgCreated=TRUE; - } - else - { - strcpy(Msg->to, "RMS"); - if (_memicmp(FN, "RMS:", 4) == 0) - strcpy(Msg->via, &FN[4]); - else - strcpy(Msg->via, FN); - } - - MatchMessagetoBBSList(Msg, conn); - - SaveMessageDatabase(); - SaveBIDDatabase(); - } - else - fclose(Handle); - - SaveMessageDatabase(); - return TRUE; -} - -BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) -{ - struct tm * tm; - time_t temp; - - char * MsgBytes = ReadMessageFile(Msg->number); - char * MsgPtr; - char Line[256]; - int len; - int MsgLen = Msg->length; - - if (Msg->via[0]) - len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, - Msg->via, Msg->from, Msg->bid); - else - len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); - - fwrite(Line, 1, len, Handle); - - len = sprintf(Line, "%s\r\n", Msg->title); - fwrite(Line, 1, len, Handle); - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r\n"); - MsgLen = (int)strlen(MsgBytes); - } - - MsgPtr = MsgBytes; - - // If a B2 Message, remove B2 Header - - if (Msg->B2Flags & B2Msg) - { - // Remove all B2 Headers, and all but the first part. - - MsgPtr = strstr(MsgBytes, "Body:"); - - if (MsgPtr) - { - MsgLen = atoi(&MsgPtr[5]); - - MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (MsgPtr) - MsgPtr +=4; - else - MsgPtr = MsgBytes; - - } - else - MsgPtr = MsgBytes; - } - - memcpy(&temp, &Msg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - Msg->number, BBSName, HRoute, RlineVer); - - fwrite(Line, 1, len, Handle); - - if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - fwrite("\r\n", 1, 2, Handle); - - fwrite(MsgPtr, 1, MsgLen, Handle); - - if (MsgPtr[MsgLen - 2] == '\r') - fwrite("/EX\r\n", 1, 5, Handle); - else - fwrite("\r\n/EX\r\n", 1, 7, Handle); - - free(MsgBytes); - - return TRUE; - -} - -BOOL ConnecttoBBS (struct UserInfo * user) -{ - int n, p; - CIRCUIT * conn; - struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; - - for (n = NumberofStreams-1; n >= 0 ; n--) - { - conn = &Connections[n]; - - if (conn->Active == FALSE) - { - p = conn->BPQStream; - memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything - conn->BPQStream = p; - - // Can't set Active until Connected or Stuck Session detertor can clear session. - // But must set Active before Connected() runs or will appear is Incoming Connect. - // Connected() is semaphored, so get semaphore before ConnectUsingAppl - // Probably better to semaphore lost session code instead - - - strcpy(conn->Callsign, user->Call); - conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); - conn->UserPointer = user; - - Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); - - ForwardingInfo->MoreLines = TRUE; - - GetSemaphore(&ConSemaphore, 1); - conn->Active = TRUE; - ConnectUsingAppl(conn->BPQStream, BBSApplMask); - FreeSemaphore(&ConSemaphore); - - // If we are sending to a dump pms we may need to connect using the message sender's callsign. - // But we wont know until we run the connect script, which is a bit late to change call. Could add - // flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script. - - if (strstr(ForwardingInfo->ConnectScript[0], "SETCALLTOSENDER")) - { - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; - conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; - - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - // We have a message to send - - struct MsgInfo * Msg; - unsigned char AXCall[7]; - - Msg = conn->FwdMsg; - ConvToAX25(Msg->from, AXCall); - ChangeSessionCallsign(p, AXCall); - - conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER | NEWPACCOM; - conn->NextMessagetoForward = 0; // was set by FindMessages - } - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; - } -#ifdef LINBPQ - { - BPQVECSTRUC * SESS; - SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; - - if (SESS->HOSTSESSION == NULL) - { - Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); - return FALSE; - } - - SESS->HOSTSESSION->Secure_Session = 1; - } -#endif - - strcpy(conn->Callsign, user->Call); - - // Connected Event will trigger connect to remote system - - RefreshMainWindow(); - - return TRUE; - } - } - - Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); - - return FALSE; - -} - -struct DelayParam -{ - struct UserInfo * User; - CIRCUIT * conn; - int Delay; -}; - -struct DelayParam DParam; // Not 100% safe, but near enough - -VOID ConnectDelayThread(struct DelayParam * DParam) -{ - struct UserInfo * User = DParam->User; - int Delay = DParam->Delay; - - User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects - - Sleep(Delay); - - User->ForwardingInfo->Forwarding = TRUE; - ConnecttoBBS(User); - - return; -} - -VOID ConnectPauseThread(struct DelayParam * DParam) -{ - CIRCUIT * conn = DParam->conn; - int Delay = DParam->Delay; - char Msg[] = "Pause Ok\r "; - - Sleep(Delay); - - ProcessBBSConnectScript(conn, Msg, 9); - - return; -} - - -/* -BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); - - -BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) -{ - BOOL Ret; - GetSemaphore(&ScriptSEM); - Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); - FreeSemaphore(&ScriptSEM); - - return Ret; -} -*/ - -BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) -{ - struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; - char ** Scripts; - char callsign[10]; - int port, sesstype, paclen, maxframe, l4window; - char * ptr, * ptr2; - - WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); - - Buffer[len]=0; - _strupr(Buffer); - - if (ForwardingInfo->TempConnectScript) - Scripts = ForwardingInfo->TempConnectScript; - else - Scripts = ForwardingInfo->ConnectScript; - - if (ForwardingInfo->ScriptIndex == -1) - { - // First Entry - if first line is TIMES, check and skip forward if necessary - - int n = 0; - int Start, End; - time_t now = time(NULL), StartSecs, EndSecs; - char * Line; - - if (Localtime) - now -= (time_t)_MYTIMEZONE; - - now %= 86400; - Line = Scripts[n]; - - if (_memicmp(Line, "TIMES", 5) == 0) - { - NextBand: - Start = atoi(&Line[6]); - End = atoi(&Line[11]); - - StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; - EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; - - if ((StartSecs <= now) && (EndSecs >= now)) - goto InBand; // In band - - // Look for next TIME - NextLine: - Line = Scripts[++n]; - - if (Line == NULL) - { - // No more lines - Disconnect - - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Line, "TIMES", 5) != 0) - goto NextLine; - else - goto NextBand; -InBand: - ForwardingInfo->ScriptIndex = n; - } - - } - else - { - // Dont check first time through - - if (strcmp(Buffer, "*** CONNECTED ") != 0) - { - if (Scripts[ForwardingInfo->ScriptIndex] == NULL || - _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished - _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished - { - ForwardingInfo->MoreLines = FALSE; - } - if (!ForwardingInfo->MoreLines) - goto CheckForSID; - } - } - - if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || - (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || - strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || - strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || - strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || - strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) - { - // Connect Failed - - char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - int Delay = 1000; - - // Look for an alternative connect block (Starting with ELSE) - - ElseLoop: - - // Skip any comments - - while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) - Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - - // TIMES terminates a script - - if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Cmd, "ELSE", 4) != 0) - { - Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - goto ElseLoop; - } - - if (_memicmp(&Cmd[5], "DELAY", 5) == 0) - Delay = atoi(&Cmd[10]) * 1000; - else - Delay = 1000; - - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - - DParam.Delay = Delay; - DParam.User = conn->UserPointer; - - _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); - - return FALSE; - } - - // The pointer is only updated when we get the connect, so we can tell when the last line is acked - // The first entry is always from Connected event, so don't have to worry about testing entry -1 below - - - // NETROM to KA node returns - - //c 1 milsw - //WIRAC:N9PMO-2} Connected to MILSW - //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A - //You have reached N9ZXS's KA-Node MILSW - //ENTER COMMAND: B,C,J,N, or Help ? - - //C KB9PRF-7 - //###LINK MADE - //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A - - // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not - // an indication of a connect. - - if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || - strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) - { - // If connected to SYNC, save IP address and port - - char * Cmd; - - if (strstr(Buffer, "*** CONNECTED TO SYNC")) - { - char * IPAddr = &Buffer[22]; - char * Port = strlop(IPAddr, ':'); - - if (Port) - { - if (conn->SyncHost) - free(conn->SyncHost); - - conn->SyncHost = _strdup(IPAddr); - conn->SyncPort = atoi(Port); - } - } - - if (conn->SkipConn) - { - conn->SkipConn = FALSE; - return TRUE; - } - - LoopBack: - - Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - - // Only Check until script is finished - - if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) - goto LoopBack; // Blank line - - if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished - { - if (_memicmp(Cmd, "MSGTYPE", 7) == 0) - { - char * ptr; - - // Select Types to send. Only send types in param. Only reverse if R in param - - _strupr(Cmd); - - Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); - - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; - - strcpy(conn->MSGTYPES, &Cmd[8]); - - if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; - - ptr = strchr(&Cmd[8], 'B'); - - if (ptr) - { - conn->SendB = TRUE; - conn->MaxBLen = atoi(++ptr); - if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; - } - - ptr = strchr(&Cmd[8], 'T'); - - if (ptr) - { - conn->SendT = TRUE; - conn->MaxTLen = atoi(++ptr); - if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; - } - ptr = strchr(&Cmd[8], 'P'); - - if (ptr) - { - conn->SendP = TRUE; - conn->MaxPLen = atoi(++ptr); - if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; - } - - // If nothing to do, terminate script - - if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) - goto LoopBack; - - Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) - { - // Used to limit connects on a port to 1 - - int Port; - char Option[80]; - - Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); - - sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); - - if (CountConnectionsOnPort(Port)) - { - Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - goto LoopBack; - } - - if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) - { - // Generate a Password to enable RADIO commands on a remote node - char AuthCommand[80]; - - _strupr(Cmd); - strcpy(AuthCommand, Cmd); - - CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); - - nodeprintf(conn, "%s\r", AuthCommand); - return TRUE; - } - - if (_memicmp(Cmd, "SKIPCON", 7) == 0) - { - // Remote Node sends Connected in CTEXT - we need to swallow it - - conn->SkipConn = TRUE; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) - { - // Send ;FW: command - - conn->SendWL2KFW = TRUE; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) - { - // Remote Node sends > at end of CTEXT - we need to swallow it - - conn->SkipPrompt++; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) - { - conn->BBSFlags |= TEXTFORWARDING; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SETCALLTOSENDER", 15) == 0) - { - conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "RADIOONLY", 9) == 0) - { - conn->BBSFlags |= WINLINKRO; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SYNC", 4) == 0) - { - conn->BBSFlags |= SYNCMODE; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "NEEDLF", 6) == 0) - { - conn->BBSFlags |= NEEDLF; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "MCASTRX", 6) == 0) - { - conn->BBSFlags |= MCASTRX; - conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs - - // send MCAST to Node - - nodeprintfEx(conn, "MCAST\r"); - return TRUE; - } - - if (_memicmp(Cmd, "FLARQ", 5) == 0) - { - conn->BBSFlags |= FLARQMAIL; - - CheckForEnd: - if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || - memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished - memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished - ForwardingInfo->MoreLines = FALSE; - - goto LoopBack; - } - if (_memicmp(Cmd, "PAUSE", 5) == 0) - { - // Pause script - - Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); - - DParam.Delay = atoi(&Cmd[6]) * 1000; - DParam.conn = conn; - - _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); - - return TRUE; - } - - if (_memicmp(Cmd, "FILE", 4) == 0) - { - if (Cmd[4] == 0) - { - // Missing Filename - - Logprintf(LOG_BBS, conn, '!', "Export file name missing"); - } - else - ForwardMessagestoFile(conn, &Cmd[5]); - - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Cmd, "SMTP", 4) == 0) - { - conn->NextMessagetoForward = FirstMessageIndextoForward; - conn->UserPointer->Total.ConnectsOut++; - - SendAMPRSMTP(conn); - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - - if (_memicmp(Cmd, "IMPORT", 6) == 0) - { - char * File, * Context; - int Num; - char * Temp = _strdup(Cmd); - - File = strtok_s(&Temp[6], " ", &Context); - - if (File && File[0]) - { - Num = ImportMessages(NULL, File, TRUE); - - Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); - - if (Context && _stricmp(Context, "delete") == 0) - DeleteFile(File); - } - free(Temp); - - if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || - memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished - memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - goto LoopBack; - } - - // Anything else is sent to Node - - // Replace \ with # so can send commands starting with # - - if (Cmd[0] == '\\') - { - Cmd[0] = '#'; - nodeprintfEx(conn, "%s\r", Cmd); - Cmd[0] = '\\'; // Put \ back in script - } - else - nodeprintfEx(conn, "%s\r", Cmd); - - return TRUE; - } - - // End of script. - - ForwardingInfo->MoreLines = FALSE; - - if (conn->BBSFlags & MCASTRX) - { - // No session with Multicast, so no SID - - conn->BBSFlags &= ~RunningConnectScript; - return TRUE; - } - - if (conn->BBSFlags & FLARQMAIL) - { - // FLARQ doesnt send a prompt - Just send message(es) - - conn->UserPointer->Total.ConnectsOut++; - conn->BBSFlags &= ~RunningConnectScript; - ForwardingInfo->LastReverseForward = time(NULL); - - // Update Paclen - - GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); - - if (paclen > 0) - conn->paclen = paclen; - - SendARQMail(conn); - return TRUE; - } - - - return TRUE; - } - - ptr = strchr(Buffer, '}'); - - if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext - { - // Could be respsonse to Node Command - - ptr+=2; - - ptr2 = strchr(&ptr[0], ' '); - - if (ptr2) - { - if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command - { - if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) - { - // stray match or misconfigured - - return TRUE; - } - - ForwardingInfo->ScriptIndex++; - - if (Scripts[ForwardingInfo->ScriptIndex]) - if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) - nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); - - return TRUE; - } - } - } - - // Not Success or Fail. If last line is still outstanding, wait fot Response - // else look for SID or Prompt - - if (conn->SkipPrompt && Buffer[len-2] == '>') - { - conn->SkipPrompt--; - return TRUE; - } - - if (ForwardingInfo->MoreLines) - return TRUE; - - // No more steps, Look for SID or Prompt - -CheckForSID: - - if (strstr(Buffer, "POSYNCHELLO")) // RMS RELAY Sync process - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - conn->NextMessagetoForward = FirstMessageIndextoForward; - conn->UserPointer->Total.ConnectsOut++; - ForwardingInfo->LastReverseForward = time(NULL); - - ProcessLine(conn, 0, Buffer, len); - return FALSE; - } - - if (strstr(Buffer, "SORRY, NO")) // URONODE - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (memcmp(Buffer, ";PQ: ", 5) == 0) - { - // Secure CMS challenge - - int Len; - struct UserInfo * User = conn->UserPointer; - char * Pass = User->CMSPass; - int Response ; - char RespString[12]; - char ConnectingCall[10]; - -#ifdef LINBPQ - BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; -#else - BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - - SESS += conn->BPQStream - 1; - - ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); - - strlop(ConnectingCall, ' '); - - if (Pass[0] == 0) - { - Pass = User->pass; // Old Way - if (Pass[0] == 0) - { - strlop(ConnectingCall, '-'); - User = LookupCall(ConnectingCall); - if (User) - Pass = User->CMSPass; - } - } - - // - - Response = GetCMSHash(&Buffer[5], Pass); - - sprintf(RespString, "%010d", Response); - - Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); - - // Save challengs in case needed for FW lines - - strcpy(conn->PQChallenge, &Buffer[5]); - - return FALSE; - } - - - if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID - { - // Update PACLEN - - GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); - - if (paclen > 0) - conn->paclen = paclen; - - - Parse_SID(conn, &Buffer[1], len-4); - - if (conn->BBSFlags & FBBForwarding) - { - conn->FBBIndex = 0; // ready for first block; - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - conn->FBBChecksum = 0; - } - - return TRUE; - } - - if (memcmp(Buffer, "[PAKET ", 7) == 0) - { - conn->BBSFlags |= BBS; - conn->BBSFlags |= MBLFORWARDING; - } - - if (Buffer[len-2] == '>') - { - if (conn->SkipPrompt) - { - conn->SkipPrompt--; - return TRUE; - } - - conn->NextMessagetoForward = FirstMessageIndextoForward; - conn->UserPointer->Total.ConnectsOut++; - conn->BBSFlags &= ~RunningConnectScript; - ForwardingInfo->LastReverseForward = time(NULL); - - if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) - { - // PK232. Don't send a SID, and switch to Text Mode - - conn->BBSFlags |= (BBS | TEXTFORWARDING); - conn->Flags |= SENDTITLE; - - // Send Message. There is no mechanism for reverse forwarding - - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - struct MsgInfo * Msg; - - // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ - - Msg = conn->FwdMsg; - - if ((conn->BBSFlags & SETCALLTOSENDER)) - nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); - else - nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, - Msg->from, Msg->bid); - } - else - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - return TRUE; - } - - if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) - { - // Build a ;FW: line with all calls with PollRMS Set - - // According to Lee if you use secure login the first - // must be the BBS call - // Actually I don't think we need the first, - // as that is implied - - // If a secure password is available send the new - // call|response format. - - // I think this should use the session callsign, which - // normally will be the BBS ApplCall, and not the BBS Name, - // but coudl be changed by *** LINKED - - int i, s; - char FWLine[10000] = ";FW: "; - struct UserInfo * user; - char RMSCall[20]; - char ConnectingCall[10]; - -#ifdef LINBPQ - BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; -#else - BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - - SESS += conn->BPQStream - 1; - - ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); - strlop(ConnectingCall, ' '); - - strcat (FWLine, ConnectingCall); - - for (i = 0; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - if (user->flags & F_POLLRMS) - { - if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; - - for (s = 0; s < 16; s++) - { - if (user->RMSSSIDBits & (1 << s)) - { - if (s) - sprintf(RMSCall, "%s-%d", user->Call, s); - else - sprintf(RMSCall, "%s", user->Call); - - // We added connectingcall at front - - if (strcmp(RMSCall, ConnectingCall) != 0) - { - strcat(FWLine, " "); - strcat(FWLine, RMSCall); - - if (user->CMSPass[0]) - { - int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); - char RespString[12]; - - sprintf(RespString, "%010d", Response); - strcat(FWLine, "|"); - strcat(FWLine, &RespString[2]); - } - } - } - } - } - } - - strcat(FWLine, "\r"); - - nodeprintf(conn, FWLine); - } - - // Only declare B1 and B2 if other end did, and we are configued for it - - nodeprintfEx(conn, BBSSID, "BPQ-", - Ver[0], Ver[1], Ver[2], Ver[3], - (conn->BBSFlags & FBBCompressed) ? "B" : "", - (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", - (conn->BBSFlags & FBBB2Mode) ? "2" : "", - (conn->BBSFlags & FBBForwarding) ? "F" : "", - (conn->BBSFlags & WINLINKRO) ? "" : "J"); - - if (conn->SecureMsg[0]) - { - struct UserInfo * user; - BBSputs(conn, conn->SecureMsg); - conn->SecureMsg[0] = 0; - - // Also send a Location Comment Line - - //; GM8BPQ-10 DE G8BPQ (IO92KX) - //; WL2K DE GM8BPQ () (PAT) - - user = LookupCall(BBSName); - - if (LOC && LOC[0]) - nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); - } - - if (conn->BPQBBS && conn->MSGTYPES[0]) - - // Send a ; MSGTYPES to control what he sends us - - nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); - - if (conn->BBSFlags & FBBForwarding) - { - if (!FBBDoForward(conn)) // Send proposal if anthing to forward - { - if (conn->DoReverse) - FBBputs(conn, "FF\r"); - else - { - FBBputs(conn, "FQ\r"); - conn->CloseAfterFlush = 20; // 2 Secs - } - } - - return TRUE; - } - - return TRUE; - } - - return TRUE; -} - -VOID Parse_SID(CIRCUIT * conn, char * SID, int len) -{ - ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions - - // scan backwards for first '-' - - if (strstr(SID, "BPQCHATSERVER")) - { - Disconnect(conn->BPQStream); - return; - } - - if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) - { - conn->RMSExpress = TRUE; - conn->Paclink = FALSE; - conn->PAT = FALSE; - - // Set new RMS Users as RMS User - - if (conn->NewUser) - conn->UserPointer->flags |= F_Temp_B2_BBS; - } - - if (stristr(SID, "PAT")) - { - // Set new PAT Users as RMS User - - conn->RMSExpress = FALSE; - conn->Paclink = FALSE; - conn->PAT = TRUE; - - if (conn->NewUser) - conn->UserPointer->flags |= F_Temp_B2_BBS; - } - if (strstr(SID, "Paclink")) - { - conn->RMSExpress = FALSE; - conn->Paclink = TRUE; - } - - if (strstr(SID, "WL2K-")) - { - conn->WL2K = TRUE; - conn->BBSFlags |= WINLINKRO; - } - - if (strstr(SID, "MFJ-")) - { - conn->BBSFlags |= MFJMODE; - } - - if (_memicmp(SID, "OpenBCM", 7) == 0) - { - // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency - - - conn->OpenBCM = TRUE; - } - - if (_memicmp(SID, "PMS-3.2", 7) == 0) - { - // Paccom TNC that doesn't send newline prompt ater receiving subject - - conn->BBSFlags |= NEWPACCOM; - } - - // See if BPQ for selective forwarding - - if (strstr(SID, "BPQ")) - conn->BPQBBS = TRUE; - - while (len > 0) - { - switch (SID[len--]) - { - case '-': - - len=0; - break; - - case '$': - - conn->BBSFlags |= BBS | MBLFORWARDING; - conn->Paging = FALSE; - - break; - - case 'F': // FBB Blocked Forwarding - - // We now support blocked uncompressed. Not necessarily compatible with FBB - - if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) - { - // We need to allocate a forwarding structure - - conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; - conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; - conn->UserPointer->BBSNumber = NBBBS; - } - - if (conn->UserPointer->ForwardingInfo->AllowBlocked) - { - conn->BBSFlags |= FBBForwarding | BBS; - conn->BBSFlags &= ~MBLFORWARDING; - - conn->Paging = FALSE; - - if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) - { - // We need to allocate a forwarding structure - - conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; - conn->UserPointer->BBSNumber = NBBBS; - } - - // Allocate a Header Block - - conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); - } - break; - - case 'J': - - // Suspected to be associated with Winlink Radio Only - - conn->BBSFlags &= ~WINLINKRO; - break; - - case 'B': - - if (conn->UserPointer->ForwardingInfo->AllowCompressed) - { - conn->BBSFlags |= FBBCompressed; - conn->DontSaveRestartData = FALSE; // Allow restarts - - // Look for 1 or 2 or 12 as next 2 chars - - if (SID[len+2] == '1') - { - if (conn->UserPointer->ForwardingInfo->AllowB1 || - conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 - conn->BBSFlags |= FBBB1Mode; - - if (SID[len+3] == '2') - if (conn->UserPointer->ForwardingInfo->AllowB2) - conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) - - break; - } - - if (SID[len+2] == '2') - { - if (conn->UserPointer->ForwardingInfo->AllowB2) - conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) - - if (conn->UserPointer->ForwardingInfo->AllowB1) - conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) - - } - break; - } - - break; - } - } - - // Only allow blocked non-binary to other BPQ Nodes - - if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) - { - // Switch back to MBL - - conn->BBSFlags |= MBLFORWARDING; - conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked - } - - return; -} - -VOID BBSSlowTimer() -{ - ConnectionInfo * conn; - int n; - - // Called every 10 seconds - - MCastTimer(); - - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (conn->Active == TRUE) - { - // Check for stuck BBS sessions (BBS session but no Node Session) - - int state; - - GetSemaphore(&ConSemaphore, 1); - SessionStateNoAck(conn->BPQStream, &state); - FreeSemaphore(&ConSemaphore); - - if (state == 0) // No Node Session - { - // is it safe just to clear Active ?? - - conn->InputMode = 0; // So Disconnect wont save partial transfer - conn->BBSFlags = 0; - Disconnected (conn->BPQStream); - continue; - } - - if (conn->BBSFlags & MCASTRX) - MCastConTimer(conn); - - - // Check SIDTImers - used to detect failure to compete SID Handshake - - if (conn->SIDResponseTimer) - { - conn->SIDResponseTimer--; - if (conn->SIDResponseTimer == 0) - { - // Disconnect Session - - Disconnect(conn->BPQStream); - } - } - } - } - - // Flush logs - - for (n = 0; n < 4; n++) - { - if (LogHandle[n]) - { - time_t LT = time(NULL); - if ((LT - LastLogTime[n]) > 30) - { - LastLogTime[n] = LT; - fclose(LogHandle[n]); - LogHandle[n] = NULL; - } - } - } -} - - -VOID FWDTimerProc() -{ - struct UserInfo * user; - struct BBSForwardingInfo * ForwardingInfo ; - time_t NOW = time(NULL); - - for (user = BBSChain; user; user = user->BBSNext) - { - // See if any messages are queued for this BBS - - ForwardingInfo = user->ForwardingInfo; - ForwardingInfo->FwdTimer+=10; - - if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) - { - ForwardingInfo->FwdTimer=0; - - if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) - { - // Check Timebands - - struct FWDBAND ** Bands = ForwardingInfo->FWDBands; - int Count = 0; - time_t now = time(NULL); - - if (Localtime) - now -= (time_t)_MYTIMEZONE; - - now %= 86400; // Secs in day - - while(Bands[Count]) - { - if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) - goto FWD; // In band - - Count++; - } - continue; // Out of bands - } - FWD: - if (ForwardingInfo->Enabled) - { - if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) - { - //Temp Debug Code - -// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", -// ForwardingInfo->ReverseFlag, -// SeeifMessagestoForward(user->BBSNumber, NULL), -// CountMessagestoForward(user)); - - if (SeeifMessagestoForward(user->BBSNumber, NULL) || - (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) - - { - user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used - - - // remove any old TempScript - - if (user->ForwardingInfo->TempConnectScript) - { - FreeList(user->ForwardingInfo->TempConnectScript); - user->ForwardingInfo->TempConnectScript = NULL; - } - - if (ConnecttoBBS(user)) - ForwardingInfo->Forwarding = TRUE; - } - } - } - } - } -} - -VOID * _zalloc_dbg(size_t len, int type, char * file, int line) -{ - // ?? malloc and clear - - void * ptr; - -#ifdef WIN32 - ptr=_malloc_dbg(len, type, file, line); -#else - ptr = malloc(len); -#endif - if (ptr) - memset(ptr, 0, len); - - return ptr; -} - -struct MsgInfo * FindMessageByNumber(int msgno) - { - int m=NumberofMessages; - - struct MsgInfo * Msg; - - do - { - Msg=MsgHddrPtr[m]; - - if (Msg->number == msgno) - return Msg; - - if (Msg->number && Msg->number < msgno) // sometimes get zero msg number - return NULL; // Not found - - m--; - - } while (m > 0); - - return NULL; -} - -struct MsgInfo * FindMessageByBID(char * BID) -{ - int m = NumberofMessages; - - struct MsgInfo * Msg; - - while (m > 0) - { - Msg = MsgHddrPtr[m]; - - if (strcmp(Msg->bid, BID) == 0) - return Msg; - - m--; - } - - return NULL; -} - -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) -{ - unsigned char hash[50]; - unsigned char key[100]; - unsigned int i, j = 0, val1, val2; - unsigned char hostname[100]=""; - - gethostname(hostname, 100); - - strcpy(key, hostname); - strcat(key, ISPPOP3Name); - - md5(key, hash); - memcpy(&hash[16], hash, 16); // in case very long password - - // String is now encoded as hex pairs, but still need to decode old format - - for (i=0; i < len; i++) - { - if (Encrypt[i] < '0' || Encrypt[i] > 'F') - goto OldFormat; - } - - // Only '0' to 'F' - - for (i=0; i < len; i++) - { - val1 = Encrypt[i++]; - val1 -= '0'; - if (val1 > 9) - val1 -= 7; - - val2 = Encrypt[i]; - val2 -= '0'; - if (val2 > 9) - val2 -= 7; - - Pass[j] = (val1 << 4) | val2; - Pass[j] ^= hash[j]; - j++; - } - - return; - -OldFormat: - - for (i=0; i < len; i++) - { - Pass[i] = Encrypt[i] ^ hash[i]; - } - - return; -} - -int EncryptPass(char * Pass, char * Encrypt) -{ - unsigned char hash[50]; - unsigned char key[100]; - unsigned int i, val; - unsigned char hostname[100]; - unsigned char extendedpass[100]; - unsigned int passlen; - unsigned char * ptr; - - gethostname(hostname, 100); - - strcpy(key, hostname); - strcat(key, ISPPOP3Name); - - md5(key, hash); - memcpy(&hash[16], hash, 16); // in case very long password - - // if password is less than 16 chars, extend with zeros - - passlen=(int)strlen(Pass); - - strcpy(extendedpass, Pass); - - if (passlen < 16) - { - for (i=passlen+1; i <= 16; i++) - { - extendedpass[i] = 0; - } - - passlen = 16; - } - - ptr = Encrypt; - Encrypt[0] = 0; - - for (i=0; i < passlen; i++) - { - val = extendedpass[i] ^ hash[i]; - ptr += sprintf(ptr, "%02X", val); - } - - return passlen * 2; -} - - - -VOID SaveIntValue(config_setting_t * group, char * name, int value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_INT); - if(setting) - config_setting_set_int(setting, value); -} - -VOID SaveInt64Value(config_setting_t * group, char * name, long long value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_INT64); - if(setting) - config_setting_set_int64(setting, value); -} - -VOID SaveFloatValue(config_setting_t * group, char * name, double value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); - if (setting) - config_setting_set_float(setting, value); -} - -VOID SaveStringValue(config_setting_t * group, char * name, char * value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, value); - -} - - -VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) -{ - config_setting_t *setting; - struct Override ** Calls; - char Multi[10000]; - char * ptr = &Multi[1]; - - *ptr = 0; - - if (values) - { - Calls = values; - - while(Calls[0]) - { - ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); - Calls++; - } - *(--ptr) = 0; - } - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, &Multi[1]); - -} - - -VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) -{ - config_setting_t *setting; - char ** Calls; - char Multi[100000]; - char * ptr = &Multi[1]; - - *ptr = 0; - - if (values) - { - Calls = values; - - while(Calls[0]) - { - strcpy(ptr, Calls[0]); - ptr += strlen(Calls[0]); - *(ptr++) = '|'; - Calls++; - } - *(--ptr) = 0; - } - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, &Multi[1]); - -} - -int configSaved = 0; - -VOID SaveConfig(char * ConfigName) -{ - struct UserInfo * user; - struct BBSForwardingInfo * ForwardingInfo ; - config_setting_t *root, *group, *bbs; - int i; - char Size[80]; - struct BBSForwardingInfo DummyForwardingInfo; - char Line[1024]; - - if (configSaved == 0) - { - // only create backup once per run - - CopyConfigFile(ConfigName); - configSaved = 1; - } - - memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); - - // Get rid of old config before saving - - config_destroy(&cfg); - - memset((void *)&cfg, 0, sizeof(config_t)); - - config_init(&cfg); - - root = config_root_setting(&cfg); - - group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); - - SaveIntValue(group, "Streams", MaxStreams); - SaveIntValue(group, "BBSApplNum", BBSApplNum); - SaveStringValue(group, "BBSName", BBSName); - SaveStringValue(group, "SYSOPCall", SYSOPCall); - SaveStringValue(group, "H-Route", HRoute); - SaveStringValue(group, "AMPRDomain", AMPRDomain); - SaveIntValue(group, "EnableUI", EnableUI); - SaveIntValue(group, "RefuseBulls", RefuseBulls); - SaveIntValue(group, "OnlyKnown", OnlyKnown); - SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); - SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); - SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); - SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); - SaveIntValue(group, "AllowAnon", AllowAnon); - SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); - SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); - SaveIntValue(group, "UserCantKillT", UserCantKillT); - - SaveIntValue(group, "ForwardToMe", ForwardToMe); - SaveIntValue(group, "SMTPPort", SMTPInPort); - SaveIntValue(group, "POP3Port", POP3InPort); - SaveIntValue(group, "NNTPPort", NNTPInPort); - SaveIntValue(group, "RemoteEmail", RemoteEmail); - SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); - - SaveIntValue(group, "MailForInterval", MailForInterval); - SaveStringValue(group, "MailForText", MailForText); - - EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); - - SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); - - SaveIntValue(group, "MulticastRX", MulticastRX); - - SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); - SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); - SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); - SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); - - SaveStringValue(group, "MyDomain", MyDomain); - SaveStringValue(group, "ISPSMTPName", ISPSMTPName); - SaveStringValue(group, "ISPEHLOName", ISPEHLOName); - SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); - SaveStringValue(group, "ISPAccountName", ISPAccountName); - SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); - - - // Save Window Sizes - -#ifndef LINBPQ - - if (ConsoleRect.right) - { - sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, - ConsoleRect.top, ConsoleRect.bottom); - - SaveStringValue(group, "ConsoleSize", Size); - } - - sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); - SaveStringValue(group, "MonitorSize", Size); - - sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); - SaveStringValue(group, "WindowSize", Size); - - SaveIntValue(group, "Bells", Bells); - SaveIntValue(group, "FlashOnBell", FlashOnBell); - SaveIntValue(group, "StripLF", StripLF); - SaveIntValue(group, "WarnWrap", WarnWrap); - SaveIntValue(group, "WrapInput", WrapInput); - SaveIntValue(group, "FlashOnConnect", FlashOnConnect); - SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); - -#endif - - SaveIntValue(group, "Log_BBS", LogBBS); - SaveIntValue(group, "Log_TCP", LogTCP); - - sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); - SaveStringValue(group, "Version", Size); - - // Save Welcome Messages and prompts - - SaveStringValue(group, "WelcomeMsg", WelcomeMsg); - SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); - SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); - - SaveStringValue(group, "Prompt", Prompt); - SaveStringValue(group, "NewUserPrompt", NewPrompt); - SaveStringValue(group, "ExpertPrompt", ExpertPrompt); - SaveStringValue(group, "SignoffMsg", SignoffMsg); - - SaveMultiStringValue(group, "RejFrom", RejFrom); - SaveMultiStringValue(group, "RejTo", RejTo); - SaveMultiStringValue(group, "RejAt", RejAt); - SaveMultiStringValue(group, "RejBID", RejBID); - - SaveMultiStringValue(group, "HoldFrom", HoldFrom); - SaveMultiStringValue(group, "HoldTo", HoldTo); - SaveMultiStringValue(group, "HoldAt", HoldAt); - SaveMultiStringValue(group, "HoldBID", HoldBID); - - SaveIntValue(group, "SendWP", SendWP); - SaveIntValue(group, "SendWPType", SendWPType); - SaveIntValue(group, "FilterWPBulls", FilterWPBulls); - SaveIntValue(group, "NoWPGuesses", NoWPGuesses); - - SaveStringValue(group, "SendWPTO", SendWPTO); - SaveStringValue(group, "SendWPVIA", SendWPVIA); - - SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); - - // Save Forwarding Config - - // Interval and Max Sizes and Aliases are not user specific - - SaveIntValue(group, "MaxTXSize", MaxTXSize); - SaveIntValue(group, "MaxRXSize", MaxRXSize); - SaveIntValue(group, "ReaddressLocal", ReaddressLocal); - SaveIntValue(group, "ReaddressReceived", ReaddressReceived); - SaveIntValue(group, "WarnNoRoute", WarnNoRoute); - SaveIntValue(group, "Localtime", Localtime); - SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); - - SaveMultiStringValue(group, "FWDAliases", AliasText); - - bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); - - for (i=1; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - ForwardingInfo = user->ForwardingInfo; - - if (ForwardingInfo == NULL) - continue; - - if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) - continue; // Ignore empty records; - - if (isdigit(user->Call[0]) || user->Call[0] == '_') - { - char Key[20] = "*"; - strcat (Key, user->Call); - group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); - } - else - group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); - - SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); - SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); - SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); - SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); - SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); - SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); - - SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); - SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); - SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); - SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); - SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); - SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); - SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); - - SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); - SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); - SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); - SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); - SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); - SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); - - SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); - } - - - // Save Housekeeping config - - group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); - - SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); - SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); - SaveIntValue(group, "MaxMsgno", MaxMsgno); - SaveIntValue(group, "BidLifetime", BidLifetime); - SaveIntValue(group, "MaxAge", MaxAge); - SaveIntValue(group, "LogLifetime", LogAge); - SaveIntValue(group, "LogLifetime", LogAge); - SaveIntValue(group, "MaintInterval", MaintInterval); - SaveIntValue(group, "UserLifetime", UserLifetime); - SaveIntValue(group, "MaintTime", MaintTime); - SaveFloatValue(group, "PR", PR); - SaveFloatValue(group, "PUR", PUR); - SaveFloatValue(group, "PF", PF); - SaveFloatValue(group, "PNF", PNF); - SaveIntValue(group, "BF", BF); - SaveIntValue(group, "BNF", BNF); - SaveIntValue(group, "NTSD", NTSD); - SaveIntValue(group, "NTSF", NTSF); - SaveIntValue(group, "NTSU", NTSU); -// SaveIntValue(group, "AP", AP); -// SaveIntValue(group, "AB", AB); - SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); - SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); - SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); - SaveIntValue(group, "OverrideUnsent", OverrideUnsent); - SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); - SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); - - SaveOverride(group, "LTFROM", LTFROM); - SaveOverride(group, "LTTO", LTTO); - SaveOverride(group, "LTAT", LTAT); - - // Save UI config - - for (i=1; i<=32; i++) - { - char Key[100]; - - sprintf(Key, "UIPort%d", i); - - group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); - - if (group) - { - SaveIntValue(group, "Enabled", UIEnabled[i]); - SaveIntValue(group, "SendMF", UIMF[i]); - SaveIntValue(group, "SendHDDR", UIHDDR[i]); - SaveIntValue(group, "SendNull", UINull[i]); - - if (UIDigi[i]) - SaveStringValue(group, "Digis", UIDigi[i]); - } - } - - // Save User Config - - bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); - - for (i=1; i <= NumberofUsers; i++) - { - char stats[256], stats2[256]; - struct MsgStats * Stats; - char Key[20] = "*"; - - user = UserRecPtr[i]; - - if (isdigit(user->Call[0]) || user->Call[0] == '_') - { - strcat (Key, user->Call); -// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); - } - else - { - strcpy(Key, user->Call); -// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); - } - /* - SaveStringValue(group, "Name", user->Name); - SaveStringValue(group, "Address", user->Address); - SaveStringValue(group, "HomeBBS", user->HomeBBS); - SaveStringValue(group, "QRA", user->QRA); - SaveStringValue(group, "pass", user->pass); - SaveStringValue(group, "ZIP", user->ZIP); - SaveStringValue(group, "CMSPass", user->CMSPass); - - SaveIntValue(group, "lastmsg", user->lastmsg); - SaveIntValue(group, "flags", user->flags); - SaveIntValue(group, "PageLen", user->PageLen); - SaveIntValue(group, "BBSNumber", user->BBSNumber); - SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); - SaveIntValue(group, "WebSeqNo", user->WebSeqNo); - - SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); -*/ - Stats = &user->Total; - -// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", - Stats->ConnectsIn, Stats->ConnectsOut, - Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], - Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], - Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], - Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], - Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], - Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); - -// SaveStringValue(group, "Totsl", stats); - - Stats = &user->Last; - - sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", -// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - Stats->ConnectsIn, Stats->ConnectsOut, - Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], - Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], - Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], - Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], - Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], - Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); - -// SaveStringValue(group, "Last", stats2); - - sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", - user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, - user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, - user->TimeLastConnected, stats, stats2); - - if (strlen(Line) < 10) - continue; - - SaveStringValue(bbs, Key, Line); - } - -/* - wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); - - for (i = 0; i <= NumberofWPrecs; i++) - { - char WPString[1024]; - long long val1, val2; - - WP = WPRecPtr[i]; - val1 = WP->last_modif; - val2 = WP->last_seen; - - sprintf(Key, "R%d", i); - - sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", - &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], - &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], - val1, val2); - - SaveStringValue(wp, Key, WPString); - } - - // Save Message Headers - - msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); - - memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); - - MsgHddrPtr[0]->type = 'X'; - MsgHddrPtr[0]->status = '2'; - MsgHddrPtr[0]->number = 0; - MsgHddrPtr[0]->length = LatestMsg; - - - for (i = 0; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); - - n = 39; - while (n >=0 && HEXString1[n] == '0') - HEXString1[n--] = 0; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); - - n = 39; - while (n >= 0 && HEXString2[n] == '0') - HEXString2[n--] = 0; - - sprintf(Key, "R%d", Msg->number); - - n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, - Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], - &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, - &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); - - SaveStringValue(msgs, Key, Line); - } - - // Save Bids - - msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); - - for (i=1; i <= NumberofBIDs; i++) - { - sprintf(Key, "R%s", BIDRecPtr[i]->BID); - sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); - SaveStringValue(msgs, Key, Line); - } - -#ifdef LINBPQ - - if(! config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) - { - print("Error while writing file.\n"); - config_destroy(&cfg); - return; - } - - CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); - -#else -*/ - if(! config_write_file(&cfg, ConfigName)) - { - fprintf(stderr, "Error while writing file.\n"); - config_destroy(&cfg); - return; - } - -//#endif - - config_destroy(&cfg); - -/* - -#ifndef LINBPQ - - // Save a copy with current Date/Time Stamp for debugging - - { - char Backup[MAX_PATH]; - time_t LT; - struct tm * tm; - - LT = time(NULL); - tm = gmtime(<); - - sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, - tm->tm_mday, tm->tm_hour, tm->tm_min); - - CopyFile(ConfigName, Backup, FALSE); // Copy to .bak - } -#endif -*/ -} - -int GetIntValue(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int (setting); - - return 0; -} - -long long GetInt64Value(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int64 (setting); - - return 0; -} - -double GetFloatValue(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - - if (setting) - { - return config_setting_get_float (setting); - } - return 0; -} - -int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int (setting); - - return Default; -} - - -BOOL GetStringValue(config_setting_t * group, char * name, char * value) -{ - const char * str; - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - { - str = config_setting_get_string (setting); - strcpy(value, str); - return TRUE; - } - value[0] = 0; - return FALSE; -} - -BOOL GetConfig(char * ConfigName) -{ - int i; - char Size[80]; - config_setting_t *setting; - const char * ptr; - - config_init(&cfg); - - /* Read the file. If there is an error, report it and exit. */ - - if(! config_read_file(&cfg, ConfigName)) - { - char Msg[256]; - sprintf(Msg, "Config File Line %d - %s\n", - config_error_line(&cfg), config_error_text(&cfg)); -#ifdef WIN32 - MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); -#else - printf("%s", Msg); -#endif - config_destroy(&cfg); - return(EXIT_FAILURE); - } - -#if LIBCONFIG_VER_MINOR > 5 - config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); -#else - config_set_auto_convert (&cfg, 1); -#endif - - group = config_lookup (&cfg, "main"); - - if (group == NULL) - return EXIT_FAILURE; - - SMTPInPort = GetIntValue(group, "SMTPPort"); - POP3InPort = GetIntValue(group, "POP3Port"); - NNTPInPort = GetIntValue(group, "NNTPPort"); - RemoteEmail = GetIntValue(group, "RemoteEmail"); - MaxStreams = GetIntValue(group, "Streams"); - BBSApplNum = GetIntValue(group, "BBSApplNum"); - EnableUI = GetIntValue(group, "EnableUI"); - MailForInterval = GetIntValue(group, "MailForInterval"); - RefuseBulls = GetIntValue(group, "RefuseBulls"); - OnlyKnown = GetIntValue(group, "OnlyKnown"); - SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); - SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); - DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); - DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); - ForwardToMe = GetIntValue(group, "ForwardToMe"); - AllowAnon = GetIntValue(group, "AllowAnon"); - UserCantKillT = GetIntValue(group, "UserCantKillT"); - - DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); - DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); - MaxTXSize = GetIntValue(group, "MaxTXSize"); - MaxRXSize = GetIntValue(group, "MaxRXSize"); - ReaddressLocal = GetIntValue(group, "ReaddressLocal"); - ReaddressReceived = GetIntValue(group, "ReaddressReceived"); - WarnNoRoute = GetIntValue(group, "WarnNoRoute"); - SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); - Localtime = GetIntValue(group, "Localtime"); - AliasText = GetMultiStringValue(group, "FWDAliases"); - GetStringValue(group, "BBSName", BBSName); - GetStringValue(group, "MailForText", MailForText); - GetStringValue(group, "SYSOPCall", SYSOPCall); - GetStringValue(group, "H-Route", HRoute); - GetStringValue(group, "AMPRDomain", AMPRDomain); - SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); - ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); - ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); - GetStringValue(group, "MyDomain", MyDomain); - GetStringValue(group, "ISPSMTPName", ISPSMTPName); - GetStringValue(group, "ISPPOP3Name", ISPPOP3Name); - ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); - ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); - GetStringValue(group, "ISPAccountName", ISPAccountName); - GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); - GetStringValue(group, "ISPAccountName", ISPAccountName); - - sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default - GetStringValue(group, "SignoffMsg", SignoffMsg); - - DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); - - SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); - LogBBS = GetIntValue(group, "Log_BBS"); - LogTCP = GetIntValue(group, "Log_TCP"); - - MulticastRX = GetIntValue(group, "MulticastRX"); - -#ifndef LINBPQ - - GetStringValue(group, "MonitorSize", Size); - sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); - - GetStringValue(group, "WindowSize", Size); - sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); - - Bells = GetIntValue(group, "Bells"); - - FlashOnBell = GetIntValue(group, "FlashOnBell"); - - StripLF = GetIntValue(group, "StripLF"); - CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); - WarnWrap = GetIntValue(group, "WarnWrap"); - WrapInput = GetIntValue(group, "WrapInput"); - FlashOnConnect = GetIntValue(group, "FlashOnConnect"); - - GetStringValue(group, "ConsoleSize", Size); - sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, - &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); - -#endif - - // Get Welcome Messages - - setting = config_setting_get_member (group, "WelcomeMsg"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - WelcomeMsg = _strdup(ptr); - } - else - WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); - - - setting = config_setting_get_member (group, "NewUserWelcomeMsg"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - NewWelcomeMsg = _strdup(ptr); - } - else - NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); - - - setting = config_setting_get_member (group, "ExpertWelcomeMsg"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - ExpertWelcomeMsg = _strdup(ptr); - } - else - ExpertWelcomeMsg = _strdup(""); - - // Get Prompts - - setting = config_setting_get_member (group, "Prompt"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - Prompt = _strdup(ptr); - } - else - { - Prompt = malloc(20); - sprintf(Prompt, "de %s>\r\n", BBSName); - } - - setting = config_setting_get_member (group, "NewUserPrompt"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - NewPrompt = _strdup(ptr); - } - else - { - NewPrompt = malloc(20); - sprintf(NewPrompt, "de %s>\r\n", BBSName); - } - - setting = config_setting_get_member (group, "ExpertPrompt"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - ExpertPrompt = _strdup(ptr); - } - else - { - ExpertPrompt = malloc(20); - sprintf(ExpertPrompt, "de %s>\r\n", BBSName); - } - - TidyPrompts(); - - RejFrom = GetMultiStringValue(group, "RejFrom"); - RejTo = GetMultiStringValue(group, "RejTo"); - RejAt = GetMultiStringValue(group, "RejAt"); - RejBID = GetMultiStringValue(group, "RejBID"); - - HoldFrom = GetMultiStringValue(group, "HoldFrom"); - HoldTo = GetMultiStringValue(group, "HoldTo"); - HoldAt = GetMultiStringValue(group, "HoldAt"); - HoldBID = GetMultiStringValue(group, "HoldBID"); - - // Send WP Params - - SendWP = GetIntValue(group, "SendWP"); - SendWPType = GetIntValue(group, "SendWPType"); - - GetStringValue(group, "SendWPTO", SendWPTO); - GetStringValue(group, "SendWPVIA", SendWPVIA); - - SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); - - FilterWPBulls = GetIntValue(group, "FilterWPBulls"); - NoWPGuesses = GetIntValue(group, "NoWPGuesses"); - - if (SendWPAddrs[0] == NULL && SendWPTO[0]) - { - // convert old format TO and VIA to entry in SendWPAddrs - - SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry - - if (SendWPVIA[0]) - { - char WP[256]; - - sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); - SendWPAddrs[0] = _strdup(WP); - } - else - SendWPAddrs[0] = _strdup(SendWPTO); - - - SendWPAddrs[1] = 0; - - SendWPTO[0] = 0; - SendWPVIA[0] = 0; - } - - GetStringValue(group, "Version", Size); - sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); - - for (i=1; i<=32; i++) - { - char Key[100]; - - sprintf(Key, "UIPort%d", i); - - group = config_lookup (&cfg, Key); - - if (group) - { - UIEnabled[i] = GetIntValue(group, "Enabled"); - UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); - UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); - UINull[i] = GetIntValue(group, "SendNull"); - Size[0] = 0; - GetStringValue(group, "Digis", Size); - if (Size[0]) - UIDigi[i] = _strdup(Size); - } - } - - group = config_lookup (&cfg, "Housekeeping"); - - if (group) - { - LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); - LastTrafficTime = GetIntValue(group, "LastTrafficTime"); - MaxMsgno = GetIntValue(group, "MaxMsgno"); - LogAge = GetIntValue(group, "LogLifetime"); - BidLifetime = GetIntValue(group, "BidLifetime"); - MaxAge = GetIntValue(group, "MaxAge"); - if (MaxAge == 0) - MaxAge = 30; - UserLifetime = GetIntValue(group, "UserLifetime"); - MaintInterval = GetIntValue(group, "MaintInterval"); - - if (MaintInterval == 0) - MaintInterval = 24; - - MaintTime = GetIntValue(group, "MaintTime"); - - PR = GetFloatValue(group, "PR"); - PUR = GetFloatValue(group, "PUR"); - PF = GetFloatValue(group, "PF"); - PNF = GetFloatValue(group, "PNF"); - - BF = GetIntValue(group, "BF"); - BNF = GetIntValue(group, "BNF"); - NTSD = GetIntValue(group, "NTSD"); - NTSU = GetIntValue(group, "NTSU"); - NTSF = GetIntValue(group, "NTSF"); -// AP = GetIntValue(group, "AP"); -// AB = GetIntValue(group, "AB"); - DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); - SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); - SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); - OverrideUnsent = GetIntValue(group, "OverrideUnsent"); - SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); - OverrideUnsent = GetIntValue(group, "OverrideUnsent"); - GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); - - LTFROM = GetOverrides(group, "LTFROM"); - LTTO = GetOverrides(group, "LTTO"); - LTAT = GetOverrides(group, "LTAT"); - } - - return EXIT_SUCCESS; -} - - -int Connected(int Stream) -{ - int n, Mask; - CIRCUIT * conn; - struct UserInfo * user = NULL; - char callsign[10]; - int port, paclen, maxframe, l4window; - char ConnectedMsg[] = "*** CONNECTED "; - char Msg[100]; - char Title[100]; - int Freq = 0; - int Mode = 0; - BPQVECSTRUC * SESS; - TRANSPORTENTRY * Sess1 = NULL, * Sess2; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (Stream == conn->BPQStream) - { - if (conn->Active) - { - // Probably an outgoing connect - - ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; - conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; - conn->ErrorCount = 0; - - if (conn->BBSFlags & RunningConnectScript) - { - // BBS Outgoing Connect - - conn->paclen = 236; - - // Run first line of connect script - - ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions - ProcessBBSConnectScript(conn, ConnectedMsg, 15); - return 0; - } - } - - // Incoming Connect - - // Try to find port, freq, mode, etc - -#ifdef LINBPQ - SESS = &BPQHOSTVECTOR[0]; -#else - SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - SESS +=(Stream - 1); - - if (SESS) - Sess1 = SESS->HOSTSESSION; - - if (Sess1) - { - Sess2 = Sess1->L4CROSSLINK; - - if (Sess2) - { - // See if L2 session - if so, get info from WL2K report line - - // if Session has report info, use it - - if (Sess2->Mode) - { - Freq = Sess2->Frequency; - Mode = Sess2->Mode; - } - else if (Sess2->L4CIRCUITTYPE & L2LINK) - { - LINKTABLE * LINK = Sess2->L4TARGET.LINK; - PORTCONTROLX * PORT = LINK->LINKPORT; - - Freq = PORT->WL2KInfo.Freq; - Mode = PORT->WL2KInfo.mode; - } - else - { - if (Sess2->RMSCall[0]) - { - Freq = Sess2->Frequency; - Mode = Sess2->Mode; - } - } - } - } - - memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything - conn->Active = TRUE; - conn->BPQStream = Stream; - ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions - - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; - conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; - conn->ErrorCount = 0; - - conn->Secure_Session = GetConnectionInfo(Stream, callsign, - &port, &conn->SessType, &paclen, &maxframe, &l4window); - - strlop(callsign, ' '); // Remove trailing spaces - - if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) - conn->RadioOnlyMode = 'T'; - else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) - conn->RadioOnlyMode = 'R'; - else - conn->RadioOnlyMode = 0; - - memcpy(conn->Callsign, callsign, 10); - - strlop(callsign, '-'); // Remove any SSID - - user = LookupCall(callsign); - - if (user == NULL) - { - int Length=0; - - if (OnlyKnown) - { - // Unknown users not allowed - - n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - Disconnect(Stream); - return 0; - } - - user = AllocateUserRecord(callsign); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - if (SendNewUserMessage) - { - char * MailBuffer = malloc(100); - Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %d\r\n", callsign, port, Freq, Mode); - - sprintf(Title, "New User %s", callsign); - - SendMessageToSYSOP(Title, MailBuffer, Length); - } - - if (user == NULL) return 0; // Cant happen?? - - if (!DontHoldNewUsers) - user->flags |= F_HOLDMAIL; - - if (DefaultNoWINLINK) - user->flags |= F_NOWINLINK; - - // Always set WLE User - can't see it doing any harm - - user->flags |= F_Temp_B2_BBS; - - conn->NewUser = TRUE; - } - - user->TimeLastConnected = time(NULL); - user->Total.ConnectsIn++; - - conn->UserPointer = user; - - conn->lastmsg = user->lastmsg; - - conn->NextMessagetoForward = FirstMessageIndextoForward; - - if (paclen == 0) - { - paclen = 236; - - if (conn->SessType & Sess_PACTOR) - paclen = 100; - } - - conn->paclen = paclen; - - // Set SYSOP flag if user is defined as SYSOP and Host Session - - if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) - conn->sysop = TRUE; - - if (conn->Secure_Session && (user->flags & F_SYSOP)) - conn->sysop = TRUE; - - Mask = 1 << (GetApplNum(Stream) - 1); - - if (user->flags & F_Excluded) - { - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - Disconnect(Stream); - return 0; - } - - if (port) - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", - user->Call, port, Freq, WL2KModes[Mode]); - else - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); - - // Send SID and Prompt (Unless Sync) - - if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) - conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks - else - conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID - - { - BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; - BOOL WL2KRO = FALSE; - - struct BBSForwardingInfo * ForwardingInfo; - - if (conn->RadioOnlyMode == 'R') - WL2KRO = 1; - - conn->PageLen = user->PageLen; - conn->Paging = (user->PageLen > 0); - - if (user->flags & F_Temp_B2_BBS) - { - // An RMS Express user that needs a temporary BBS struct - - if (user->ForwardingInfo == NULL) - { - // we now save the Forwarding info if BBS flag is cleared, - // so there may already be a ForwardingInfo - - user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - } - - if (user->BBSNumber == 0) - user->BBSNumber = NBBBS; - - ForwardingInfo = user->ForwardingInfo; - - ForwardingInfo->AllowCompressed = TRUE; - B1 = ForwardingInfo->AllowB1 = FALSE; - B2 = ForwardingInfo->AllowB2 = TRUE; - BLOCKED = ForwardingInfo->AllowBlocked = TRUE; - } - - if (conn->NewUser) - { - BLOCKED = TRUE; - BIN = TRUE; - B2 = TRUE; - } - - if (user->ForwardingInfo) - { - BLOCKED = user->ForwardingInfo->AllowBlocked; - if (BLOCKED) - { - BIN = user->ForwardingInfo->AllowCompressed; - B1 = user->ForwardingInfo->AllowB1; - B2 = user->ForwardingInfo->AllowB2; - } - } - - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - if (conn->RadioOnlyMode) - nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); - - if (!(conn->BBSFlags & SYNCMODE)) - { - - nodeprintf(conn, BBSSID, "BPQ-", - Ver[0], Ver[1], Ver[2], Ver[3], - BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", - BLOCKED ? "FW": "", WL2KRO ? "" : "J"); - - // if (user->flags & F_Temp_B2_BBS) - // nodeprintf(conn,";PQ: 66427529\r"); - - // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); - } - } - - if ((user->Name[0] == 0) & AllowAnon) - strcpy(user->Name, user->Call); - - if (!(conn->BBSFlags & SYNCMODE)) - { - if (user->Name[0] == 0) - { - conn->Flags |= GETTINGUSER; - BBSputs(conn, NewUserPrompt); - } - else - SendWelcomeMsg(Stream, conn, user); - } - else - { - // Seems to be a timing problem - see if this fixes it - - Sleep(500); - } - - RefreshMainWindow(); - - return 0; - } - } - - return 0; -} - -int Disconnected (int Stream) -{ - struct UserInfo * user = NULL; - CIRCUIT * conn; - int n; - char Msg[255]; - int len; - char DiscMsg[] = "DISCONNECTED "; - - for (n = 0; n <= NumberofStreams-1; n++) - { - conn=&Connections[n]; - - if (Stream == conn->BPQStream) - { - if (conn->Active == FALSE) - return 0; - - // if still running connect script, reenter it to see if - // there is an else - - if (conn->BBSFlags & RunningConnectScript) - { - // We need to see if we got as far as connnected, - // as if we have we need to reset the connect script - // over the ELSE - - struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; - char ** Scripts; - - if (ForwardingInfo->TempConnectScript) - Scripts = ForwardingInfo->TempConnectScript; - else - Scripts = ForwardingInfo->ConnectScript; - - // First see if any script left - - if (Scripts[ForwardingInfo->ScriptIndex]) - { - if (ForwardingInfo->MoreLines == FALSE) - { - // Have reached end of script, so need to set back over ELSE - - ForwardingInfo->ScriptIndex--; - ForwardingInfo->MoreLines = TRUE; - } - - // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || - // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished - // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished - - - ProcessBBSConnectScript(conn, DiscMsg, 15); - return 0; - } - } - - // if sysop was chatting to user clear link -#ifndef LINBPQ - if (conn->BBSFlags & SYSOPCHAT) - { - SendUnbuffered(-1, "User has disconnected\n", 23); - BBSConsole.Console->SysopChatStream = 0; - } -#endif - ClearQueue(conn); - - if (conn->PacLinkCalls) - free(conn->PacLinkCalls); - - if (conn->InputBuffer) - { - free(conn->InputBuffer); - conn->InputBuffer = NULL; - conn->InputBufferLen = 0; - } - - if (conn->InputMode == 'B') - { - // Save partly received message for a restart - - if (conn->BBSFlags & FBBB1Mode) - if (conn->Paclink == 0) // Paclink doesn't do restarts - if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. - if (conn->DontSaveRestartData == FALSE) - SaveFBBBinary(conn); - } - - conn->Active = FALSE; - - if (conn->FwdMsg) - conn->FwdMsg->Locked = 0; // Unlock - - RefreshMainWindow(); - - RemoveTempBIDS(conn); - - len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); - WriteLogLine(conn, '|',Msg, len, LOG_BBS); - - if (conn->FBBHeaders) - { - struct FBBHeaderLine * FBBHeader; - int n; - - for (n = 0; n < 5; n++) - { - FBBHeader = &conn->FBBHeaders[n]; - - if (FBBHeader->FwdMsg) - FBBHeader->FwdMsg->Locked = 0; // Unlock - - } - - free(conn->FBBHeaders); - conn->FBBHeaders = NULL; - } - - if (conn->UserPointer) - { - struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; - - if (FWDInfo) - { - FWDInfo->Forwarding = FALSE; - -// if (FWDInfo->UserCall[0]) // Will be set if RMS -// { -// FindNextRMSUser(FWDInfo); -// } -// else - FWDInfo->FwdTimer = 0; - } - } - - conn->BBSFlags = 0; // Clear ARQ Mode - - return 0; - } - } - return 0; -} - -int DoReceivedData(int Stream) -{ - int count, InputLen; - size_t MsgLen; - int n; - CIRCUIT * conn; - struct UserInfo * user; - char * ptr, * ptr2; - char * Buffer; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (Stream == conn->BPQStream) - { - conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. - - do - { - // May have several messages per packet, or message split over packets - - OuterLoop: - if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode - { - conn->InputBufferLen += 1000; - conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); - } - - GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); - - if (InputLen == 0 && conn->InputMode != 'Y') - return 0; - - conn->InputLen += InputLen; - - if (conn->InputLen == 0) return 0; - - conn->Watchdog = 900; // 15 Minutes - - if (conn->InputMode == 'Y') // YAPP - { - if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process - goto OuterLoop; - - return 0; - } - - if (conn->InputMode == 'B') - { - // if in OpenBCM mode, remove FF transparency - - if (conn->OpenBCM) // Telnet, so escape any 0xFF - { - unsigned char * ptr1 = conn->InputBuffer; - unsigned char * ptr2; - int Len; - unsigned char c; - - // We can come through here again for the - // same data as we wait for a full packet - // So only check last InputLen bytes - - ptr1 += (conn->InputLen - InputLen); - ptr2 = ptr1; - Len = InputLen; - - while (Len--) - { - c = *(ptr1++); - - if (conn->InTelnetExcape) // Last char was ff - { - conn->InTelnetExcape = FALSE; - continue; - } - - *(ptr2++) = c; - - if (c == 0xff) // - conn->InTelnetExcape = TRUE; - } - - conn->InputLen = (int)(ptr2 - conn->InputBuffer); - } - - UnpackFBBBinary(conn); - goto OuterLoop; - } - else - { - - loop: - - if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null - { - conn->InputLen = 0; - return 0; - } - - user = conn->UserPointer; - - if (conn->BBSFlags & (MCASTRX | SYNCMODE)) - { - // MCAST and SYNCMODE deliver full packets - - if (conn->BBSFlags & RunningConnectScript) - ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); - else - ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); - - conn->InputLen=0; - continue; - } - - // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, - // but this relies on both arriving in same packet. - // Need to check for LF and start of packet and ignore it - // But what if client is only using LF?? - // (WLE sends SID with CRLF, other packets with CR only) - - // We don't get here on the data part of a binary transfer, so - // don't need to worry about messing up binary data. - - ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); - ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); - - if (ptr) - conn->usingCR = 1; - - if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR - ptr = ptr2; // Use LF - - if (ptr) // CR or LF in buffer - { - conn->lastLineEnd = *(ptr); - - *(ptr) = '\r'; // In case was LF - - ptr2 = &conn->InputBuffer[conn->InputLen]; - - if (++ptr == ptr2) - { - // Usual Case - single msg in buffer - - // if Length is 1 and Term is LF and normal line end is CR - // this is from a split CRLF - Ignore it - - if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) - Debugprintf("BPQMail split Line End Detected"); - else - { - if (conn->BBSFlags & RunningConnectScript) - ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); - else - ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); - } - conn->InputLen=0; - } - else - { - // buffer contains more that 1 message - - MsgLen = conn->InputLen - (ptr2-ptr); - - Buffer = malloc(MsgLen + 100); - - memcpy(Buffer, conn->InputBuffer, MsgLen); - - // if Length is 1 and Term is LF and normal line end is CR - // this is from a split CRLF - Ignore it - - if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) - Debugprintf("BPQMail split Line End Detected"); - else - { - if (conn->BBSFlags & RunningConnectScript) - ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); - else - ProcessLine(conn, user, Buffer, (int)MsgLen); - } - free(Buffer); - - if (*ptr == 0 || *ptr == '\n') - { - /// CR LF or CR Null - - ptr++; - conn->InputLen--; - } - - memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); - - conn->InputLen -= (int)MsgLen; - - goto loop; - - } - } - else - { - // Could be a YAPP Header - - - if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init - { - UCHAR YAPPRR[2]; - YAPPRR[0] = ACK; - YAPPRR[1] = 1; - - conn->InputMode = 'Y'; - QueueMsg(conn, YAPPRR, 2); - - conn->InputLen = 0; - return 0; - } - } - } - - } while (count > 0); - - return 0; - } - } - - // Socket not found - - return 0; - -} -int DoBBSMonitorData(int Stream) -{ -// UCHAR Buffer[1000]; - UCHAR buff[500]; - - int len = 0,count=0; - int stamp; - - do - { - stamp=GetRaw(Stream, buff,&len,&count); - - if (len == 0) return 0; - - SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); - } - - while (count > 0); - - - return 0; - -} - -VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) -{ - Buffer[MsgLen] = 0; - - if (MsgLen == 1 && Buffer[0] == 13) - return; - - if (strcmp(Buffer, "ARQ::ETX\r") == 0) - { - // Decode it. - - UCHAR * ptr1, * ptr2, * ptr3; - int len, linelen; - struct MsgInfo * Msg = conn->TempMsg; - time_t Date; - char FullTo[100]; - char FullFrom[100]; - char ** RecpTo = NULL; // May be several Recipients - char ** HddrTo = NULL; // May be several Recipients - char ** Via = NULL; // May be several Recipients - int LocalMsg[1000] ; // Set if Recipient is a local wl2k address - - int B2To; // Offset to To: fields in B2 header - int Recipients = 0; - int RMSMsgs = 0, BBSMsgs = 0; - -// Msg->B2Flags |= B2Msg; - - - ptr1 = conn->MailBuffer; - len = Msg->length; - ptr1[len] = 0; - - if (strstr(ptr1, "ARQ:ENCODING::")) - { - // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now - - _strupr(conn->MailBuffer); - if (strstr(conn->MailBuffer, "BBSPOLL")) - { - SendARQMail(conn); - } - - free(conn->MailBuffer); - conn->MailBuffer = NULL; - conn->MailBufferSize = 0; - - return; - } - Loop: - ptr2 = strchr(ptr1, '\r'); - - linelen = (int)(ptr2 - ptr1); - - if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: - { - char SaveFrom[100]; - char * FromHA; - - memcpy(FullFrom, ptr1, linelen); - FullFrom[linelen] = 0; - - // B2 From may now contain an @BBS - - strcpy(SaveFrom, FullFrom); - - FromHA = strlop(SaveFrom, '@'); - - if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; - - strcpy(Msg->from, &SaveFrom[6]); - - if (FromHA) - { - if (strlen(FromHA) > 39) FromHA[39] = 0; - Msg->emailfrom[0] = '@'; - strcpy(&Msg->emailfrom[1], _strupr(FromHA)); - } - - // Remove any SSID - - ptr3 = strchr(Msg->from, '-'); - if (ptr3) *ptr3 = 0; - - } - else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) - { - HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); - HddrTo[Recipients] = zalloc(100); - - memset(FullTo, 0, 99); - memcpy(FullTo, &ptr1[4], linelen-4); - memcpy(HddrTo[Recipients], ptr1, linelen+2); - LocalMsg[Recipients] = FALSE; - - _strupr(FullTo); - - B2To = (int)(ptr1 - conn->MailBuffer); - - if (_memicmp(FullTo, "RMS:", 4) == 0) - { - // remove RMS and add @winlink.org - - strcpy(FullTo, "RMS"); - strcpy(Msg->via, &FullTo[4]); - } - else - { - ptr3 = strchr(FullTo, '@'); - - if (ptr3) - { - *ptr3++ = 0; - strcpy(Msg->via, ptr3); - } - else - Msg->via[0] = 0; - } - - if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) - { - // Airmail Sends MARS messages as SMTP - - if (CheckifPacket(Msg->via)) - { - // Packet Message - - memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); - _strupr(FullTo); - _strupr(Msg->via); - - // Update the saved to: line (remove the smtp:) - - strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); - BBSMsgs++; - goto BBSMsg; - } - - // If a winlink.org address we need to convert to call - - if (_stricmp(Msg->via, "winlink.org") == 0) - { - memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); - _strupr(FullTo); - LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); - } - else - { - memcpy(Msg->via, &ptr1[9], linelen); - Msg->via[linelen - 9] = 0; - strcpy(FullTo,"RMS"); - } -// FullTo[0] = 0; - - BBSMsg: - _strupr(FullTo); - _strupr(Msg->via); - } - - if (memcmp(FullTo, "RMS:", 4) == 0) - { - // remove RMS and add @winlink.org - - memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); - strcpy(Msg->via, "winlink.org"); - sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); - } - - if (strcmp(Msg->via, "RMS") == 0) - { - // replace RMS with @winlink.org - - strcpy(Msg->via, "winlink.org"); - sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); - } - - if (strlen(FullTo) > 6) - FullTo[6] = 0; - - strlop(FullTo, '-'); - - strcpy(Msg->to, FullTo); - - if (SendBBStoSYSOPCall) - if (_stricmp(FullTo, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); - - if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) - { - // No routing - check @BBS and WP - - struct UserInfo * ToUser = LookupCall(FullTo); - - Msg->via[0] = 0; // In case BPQ and not found - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->HomeBBS[0]) - { - strcpy(Msg->via, ToUser->HomeBBS); - } - } - else - { - WPRecP WP = LookupWP(FullTo); - - if (WP) - { - strcpy(Msg->via, WP->first_homebbs); - - } - } - - // Fix To: address in B2 Header - - if (Msg->via[0]) - sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); - else - sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); - - } - - RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); - RecpTo[Recipients] = zalloc(10); - - Via=realloc(Via, (Recipients+1) * sizeof(void *)); - Via[Recipients] = zalloc(50); - - strcpy(Via[Recipients], Msg->via); - strcpy(RecpTo[Recipients++], FullTo); - - // Remove the To: Line from the buffer - - } - else if (_memicmp(ptr1, "Type:", 4) == 0) - { - if (ptr1[6] == 'N') - Msg->type = 'T'; // NTS - else - Msg->type = ptr1[6]; - } - else if (_memicmp(ptr1, "Subject:", 8) == 0) - { - size_t Subjlen = ptr2 - &ptr1[9]; - if (Subjlen > 60) Subjlen = 60; - memcpy(Msg->title, &ptr1[9], Subjlen); - - goto ProcessBody; - } -// else if (_memicmp(ptr1, "Body:", 4) == 0) -// { -// MsgLen = atoi(&ptr1[5]); -// StartofMsg = ptr1; -// } - else if (_memicmp(ptr1, "File:", 5) == 0) - { - Msg->B2Flags |= Attachments; - } - else if (_memicmp(ptr1, "Date:", 5) == 0) - { - struct tm rtime; - char seps[] = " ,\t\r"; - - memset(&rtime, 0, sizeof(struct tm)); - - // Date: 2009/07/25 10:08 - - sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", - &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - rtime.tm_year -= 1900; - - Date = mktime(&rtime) - (time_t)_MYTIMEZONE; - - if (Date == (time_t)-1) - Date = time(NULL); - - Msg->datecreated = Date; - - } - - if (linelen) // Not Null line - { - ptr1 = ptr2 + 2; // Skip cr - goto Loop; - } - - - // Processed all headers -ProcessBody: - - ptr2 +=2; // skip crlf - - Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); - - memmove(conn->MailBuffer, ptr2, Msg->length); - - CreateMessageFromBuffer(conn); - - conn->BBSFlags = 0; // Clear ARQ Mode - return; - } - - // File away the data - - Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf - - if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); - - conn->TempMsg->length += MsgLen; - - return; - - // Not sure what to do yet with files, but will process emails (using text style forwarding - -/* -ARQ:FILE::flarqmail-1.eml -ARQ:EMAIL:: -ARQ:SIZE::96 -ARQ::STX -//FLARQ COMPOSER -Date: 16/01/2014 22:26:06 -To: g8bpq -From: -Subject: test message - -Hello -Hello - -ARQ::ETX -*/ - - return; -} - -VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) -{ - Buffer[len] = 0; -// Debugprintf(Buffer); - - // With TNC2 body prompt is a single CR, so that shouldn't be ignored. - - // If thia causes problems with other TNC PMS implementations I'll have to revisit this - -// if (len == 1 && Buffer[0] == 13) -// return; - - if (conn->Flags & SENDTITLE) - { - // Waiting for Subject: prompt - - struct MsgInfo * Msg = conn->FwdMsg; - - nodeprintf(conn, "%s\r", Msg->title); - - conn->Flags &= ~SENDTITLE; - conn->Flags |= SENDBODY; - - // New Paccom PMS (V3.2) doesn't prompt for body so drop through and send it - if ((conn->BBSFlags & NEWPACCOM) == 0) - return; - - } - - if (conn->Flags & SENDBODY) - { - // Waiting for Enter Message Prompt - - struct tm * tm; - time_t temp; - - char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); - char * MsgPtr; - int MsgLen; - int Index = 0; - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r"); - conn->FwdMsg->length = (int)strlen(MsgBytes); - } - - MsgPtr = MsgBytes; - MsgLen = conn->FwdMsg->length; - - // If a B2 Message, remove B2 Header - - if (conn->FwdMsg->B2Flags & B2Msg) - { - // Remove all B2 Headers, and all but the first part. - - MsgPtr = strstr(MsgBytes, "Body:"); - - if (MsgPtr) - { - MsgLen = atoi(&MsgPtr[5]); - MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (MsgPtr) - MsgPtr +=4; - else - MsgPtr = MsgBytes; - - } - else - MsgPtr = MsgBytes; - } - - memcpy(&temp, &conn->FwdMsg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - conn->FwdMsg->number, BBSName, HRoute, RlineVer); - - if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - BBSputs(conn, "\r"); - - MsgLen = RemoveLF(MsgPtr, MsgLen); - - QueueMsg(conn, MsgPtr, MsgLen); - - if (user->ForwardingInfo->SendCTRLZ) - nodeprintf(conn, "\r\x1a"); - else - nodeprintf(conn, "\r/ex\r"); - - free(MsgBytes); - - conn->FBBMsgsSent = TRUE; - - - if (conn->FwdMsg->type == 'P') - Index = PMSG; - else if (conn->FwdMsg->type == 'B') - Index = BMSG; - else if (conn->FwdMsg->type == 'T') - Index = TMSG; - - user->Total.MsgsSent[Index]++; - user->Total.BytesForwardedOut[Index] += MsgLen; - - conn->Flags &= ~SENDBODY; - conn->Flags |= WAITPROMPT; - - return; - } - - if (conn->Flags & WAITPROMPT) - { - if (Buffer[len-2] != '>') - return; - - conn->Flags &= ~WAITPROMPT; - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - SaveMessageDatabase(); - - conn->UserPointer->ForwardingInfo->MsgCount--; - - // See if any more to forward - - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - struct MsgInfo * Msg; - - // If we are using SETCALLTOSENDER make sure this message is from the same sender - -#ifdef LINBPQ - BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; -#else - BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - unsigned char AXCall[7]; - - Msg = conn->FwdMsg; - ConvToAX25(Msg->from, AXCall); - if (memcmp(SESS[conn->BPQStream - 1].HOSTSESSION->L4USER, AXCall, 7) != 0) - { - Disconnect(conn->BPQStream); - return; - } - - // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ - - conn->Flags |= SENDTITLE; - - - if ((conn->BBSFlags & SETCALLTOSENDER)) - nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); - else - nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, - Msg->from, Msg->bid); - } - else - { - Disconnect(conn->BPQStream); - } - return; - } -} - - -#define N 2048 /* buffer size */ -#define F 60 /* lookahead buffer size */ -#define THRESHOLD 2 -#define NIL N /* leaf of tree */ - -extern UCHAR * infile; - -BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); - - -VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) -{ - char * Cmd, * Arg1; - char * Context; - char seps[] = " \t\r"; - int CmdLen; - - if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - conn->BBSFlags |= SYNCMODE; - conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); - - Sleep(500); - - BBSputs(conn, "OK\r"); - Flush(conn); - return; - } - - if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) - { - // This is first message received after connecting to SYNC - // Save Callsign - - char Reply[32]; - conn->BBSFlags |= SYNCMODE; - conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); - - sprintf(Reply, "POSYNCLOGON %s\r", BBSName); - BBSputs(conn, Reply); - return; - } - - if (conn->BBSFlags & SYNCMODE) - { - ProcessSyncModeMessage(conn, user, Buffer, len); - return; - } - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) - - // *** Protocol Error - // Already Connected - // Invalid Command - - if (_memicmp(Buffer, "Already Connected", 17) == 0 || - _memicmp(Buffer, "Invalid Command", 15) == 0 || - _memicmp(Buffer, "*** Protocol Error", 18) == 0) - { - conn->BBSFlags |= DISCONNECTING; - Disconnect(conn->BPQStream); - return; - } - - if (conn->BBSFlags & FBBForwarding) - { - ProcessFBBLine(conn, user, Buffer, len); - return; - } - - if (conn->BBSFlags & FLARQMODE) - { - ProcessFLARQLine(conn, user, Buffer, len); - return; - } - - if (conn->BBSFlags & MCASTRX) - { - ProcessMCASTLine(conn, user, Buffer, len); - return; - } - - - if (conn->BBSFlags & TEXTFORWARDING) - { - ProcessTextFwdLine(conn, user, Buffer, len); - return; - } - - // if chatting to sysop pass message to BBS console - - if (conn->BBSFlags & SYSOPCHAT) - { - SendUnbuffered(-1, Buffer,len); - return; - } - - if (conn->Flags & GETTINGMESSAGE) - { - ProcessMsgLine(conn, user, Buffer, len); - return; - } - if (conn->Flags & GETTINGTITLE) - { - ProcessMsgTitle(conn, user, Buffer, len); - return; - } - - if (conn->BBSFlags & MBLFORWARDING) - { - ProcessMBLLine(conn, user, Buffer, len); - return; - } - - if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name - { - if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') - { - struct BBSForwardingInfo * ForwardingInfo; - - conn->Flags &= ~GETTINGUSER; - - // New User is a BBS - create a temp struct for it - - if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name - { - // Not defined as BBS - allocate and initialise forwarding structure - - user->flags |= F_Temp_B2_BBS; - - // An RMS Express user that needs a temporary BBS struct - - ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - - ForwardingInfo->AllowCompressed = TRUE; - ForwardingInfo->AllowBlocked = TRUE; - conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; - } - SaveUserDatabase(); - } - else - { - if (conn->Flags & GETTINGUSER) - { - conn->Flags &= ~GETTINGUSER; - if (len > 18) - len = 18; - - memcpy(user->Name, Buffer, len-1); - SendWelcomeMsg(conn->BPQStream, conn, user); - SaveUserDatabase(); - UpdateWPWithUserInfo(user); - return; - } - } - } - - // Process Command - - if (conn->Paging && (conn->LinesSent >= conn->PageLen)) - { - // Waiting for paging prompt - - if (len > 1) - { - if (_memicmp(Buffer, "Abort", 1) == 0) - { - ClearQueue(conn); - conn->LinesSent = 0; - - nodeprintf(conn, AbortedMsg); - - if (conn->UserPointer->Temp->ListSuspended) - nodeprintf(conn, "bort, , = Continue..>"); - - SendPrompt(conn, user); - return; - } - } - - conn->LinesSent = 0; - return; - } - - if (user->Temp->ListSuspended) - { - // Paging limit hit when listing. User may abort, continue, or read one or more messages - - ProcessSuspendedListCommand(conn, user, Buffer, len); - return; - } - if (len == 1) - { - SendPrompt(conn, user); - return; - } - - Buffer[len] = 0; - - if (strstr(Buffer, "ARQ:FILE:")) - { - // Message from FLARQ - - conn->BBSFlags |= FLARQMODE; - strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files - - // Create a Temp Messge Stucture - - CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); - - Buffer[len++] = 0x0a; // BBS Msgs stored with crlf - - if ((conn->TempMsg->length + len) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); - - conn->TempMsg->length += len; - - return; - } - if (Buffer[0] == ';') // WL2K Comment - { - if (memcmp(Buffer, ";FW:", 4) == 0) - { - // Paclink User Select (poll for list) - - char * ptr1,* ptr2, * ptr3; - int index=0; - - // Convert string to Multistring - - Buffer[len-1] = 0; - - conn->PacLinkCalls = zalloc(len*3); - - ptr1 = &Buffer[5]; - ptr2 = (char *)conn->PacLinkCalls; - ptr2 += (len * 2); - strcpy(ptr2, ptr1); - - while (ptr2) - { - ptr3 = strlop(ptr2, ' '); - - if (strlen(ptr2)) - conn->PacLinkCalls[index++] = ptr2; - - ptr2 = ptr3; - } - - return; - } - - if (memcmp(Buffer, ";FR:", 4) == 0) - { - // New Message from TriMode - Just igonre till I know what to do with it - - return; - } - - // Ignore other ';' message - - return; - } - - - - if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID - { - // If a BBS, set BBS Flag - - if (user->flags & ( F_BBS | F_Temp_B2_BBS)) - { - if (user->ForwardingInfo) - { - if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) - { - BBSputs(conn, "Already Connected\r"); - Flush(conn); - Sleep(500); - Disconnect(conn->BPQStream); - return; - } - } - - if (user->ForwardingInfo) - { - user->ForwardingInfo->Forwarding = TRUE; - user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately - } - } - - if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) - { - Parse_SID(conn, &Buffer[1], len-4); - - if (conn->BBSFlags & FBBForwarding) - { - conn->FBBIndex = 0; // ready for first block; - conn->FBBChecksum = 0; - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - } - else - FBBputs(conn, ">\r"); - - } - - return; - } - - Cmd = strtok_s(Buffer, seps, &Context); - - if (Cmd == NULL) - { - if (!CheckForTooManyErrors(conn)) - BBSputs(conn, "Invalid Command\r"); - - SendPrompt(conn, user); - return; - } - - Arg1 = strtok_s(NULL, seps, &Context); - CmdLen = (int)strlen(Cmd); - - // Check List first. If any other, save last listed to user record. - - if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) - { - DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); - SendPrompt(conn, user); - return; - } - - if (conn->lastmsg > user->lastmsg) - { - user->lastmsg = conn->lastmsg; - SaveUserDatabase(); - } - - if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) - { - DoShowRMSCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "AUTH") == 0) - { - DoAuthCmd(conn, user, Arg1, Context); - return; - } - - if (_memicmp(Cmd, "Abort", 1) == 0) - { - ClearQueue(conn); - conn->LinesSent = 0; - - nodeprintf(conn, AbortedMsg); - - if (conn->UserPointer->Temp->ListSuspended) - nodeprintf(conn, "bort, , = Continue..>"); - - SendPrompt(conn, user); - return; - } - if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) - { - ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); - Flush(conn); - Sleep(1000); - - if (conn->BPQStream > 0) - Disconnect(conn->BPQStream); -#ifndef LINBPQ - else - CloseConsole(conn->BPQStream); -#endif - return; - } - - if (_memicmp(Cmd, "Node", 4) == 0) - { - ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); - Flush(conn); - Sleep(1000); - - if (conn->BPQStream > 0) - ReturntoNode(conn->BPQStream); -#ifndef LINBPQ - else - CloseConsole(conn->BPQStream); -#endif - return; - } - - if (_memicmp(Cmd, "IDLETIME", 4) == 0) - { - DoSetIdleTime(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) - { - DoSetMsgNo(conn, user, Arg1, Context); - return; - } - - if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) - { - DoDeliveredCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "K", 1) == 0) - { - DoKillCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - - if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) - { - ListFiles(conn, user, Arg1); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "READFILE", 4) == 0) - { - ReadBBSFile(conn, user, Arg1); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) - { - if (conn->sysop == 0) - nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); - else - { - ReRouteMessages(); - nodeprintf(conn, "Ok\r"); - } - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "YAPP", 4) == 0) - { - YAPPSendFile(conn, user, Arg1); - return; - } - - if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) - { - DoUnholdCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - if (_stricmp(Cmd, "IMPORT") == 0) - { - DoImportCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "EXPORT") == 0) - { - DoExportCmd(conn, user, Arg1, Context); - return; - } - - if (_memicmp(Cmd, "I", 1) == 0) - { - char * Save; - char * MsgBytes; - - if (Arg1) - { - // User WP lookup - - DoWPLookup(conn, user, Cmd[1], Arg1); - SendPrompt(conn, user); - return; - } - - - MsgBytes = Save = ReadInfoFile("info.txt"); - if (MsgBytes) - { - int Length; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - QueueMsg(conn, MsgBytes, Length); - free(Save); - } - else - BBSputs(conn, "SYSOP has not created an INFO file\r"); - - - SendPrompt(conn, user); - return; - } - - - if (_memicmp(Cmd, "Name", CmdLen) == 0) - { - if (Arg1) - { - if (strlen(Arg1) > 17) - Arg1[17] = 0; - - strcpy(user->Name, Arg1); - UpdateWPWithUserInfo(user); - - } - - SendWelcomeMsg(conn->BPQStream, conn, user); - SaveUserDatabase(); - - return; - } - - if (_memicmp(Cmd, "OP", 2) == 0) - { - int Lines; - - // Paging Control. Param is number of lines per page - - if (Arg1) - { - Lines = atoi(Arg1); - - if (Lines) // Sanity Check - { - if (Lines < 10) - { - nodeprintf(conn,"Page Length %d is too short\r", Lines); - SendPrompt(conn, user); - return; - } - } - - user->PageLen = Lines; - conn->PageLen = Lines; - conn->Paging = (Lines > 0); - SaveUserDatabase(); - } - - nodeprintf(conn,"Page Length is %d\r", user->PageLen); - SendPrompt(conn, user); - - return; - } - - if (_memicmp(Cmd, "QTH", CmdLen) == 0) - { - if (Arg1) - { - // QTH may contain spaces, so put back together, and just split at cr - - Arg1[strlen(Arg1)] = ' '; - strtok_s(Arg1, "\r", &Context); - - if (strlen(Arg1) > 60) - Arg1[60] = 0; - - strcpy(user->Address, Arg1); - UpdateWPWithUserInfo(user); - - } - - nodeprintf(conn,"QTH is %s\r", user->Address); - SendPrompt(conn, user); - - SaveUserDatabase(); - - return; - } - - if (_memicmp(Cmd, "ZIP", CmdLen) == 0) - { - if (Arg1) - { - if (strlen(Arg1) > 8) - Arg1[8] = 0; - - strcpy(user->ZIP, _strupr(Arg1)); - UpdateWPWithUserInfo(user); - } - - nodeprintf(conn,"ZIP is %s\r", user->ZIP); - SendPrompt(conn, user); - - SaveUserDatabase(); - - return; - } - - if (_memicmp(Cmd, "CMSPASS", 7) == 0) - { - if (Arg1 == 0) - { - nodeprintf(conn,"Must specify a password\r"); - } - else - { - if (strlen(Arg1) > 15) - Arg1[15] = 0; - - strcpy(user->CMSPass, Arg1); - nodeprintf(conn,"CMS Password Set\r"); - SaveUserDatabase(); - } - - SendPrompt(conn, user); - - return; - } - - if (_memicmp(Cmd, "PASS", CmdLen) == 0) - { - if (Arg1 == 0) - { - nodeprintf(conn,"Must specify a password\r"); - } - else - { - if (strlen(Arg1) > 12) - Arg1[12] = 0; - - strcpy(user->pass, Arg1); - nodeprintf(conn,"BBS Password Set\r"); - SaveUserDatabase(); - } - - SendPrompt(conn, user); - - return; - } - - - if (_memicmp(Cmd, "R", 1) == 0) - { - DoReadCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "S", 1) == 0) - { - if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) - SendPrompt(conn, user); - return; - } - - if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) - { - char * Save; - char * MsgBytes = Save = ReadInfoFile("help.txt"); - - if (MsgBytes) - { - int Length; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - QueueMsg(conn, MsgBytes, Length); - free(Save); - } - else - { - BBSputs(conn, "A - Abort Output\r"); - BBSputs(conn, "B - Logoff\r"); - BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); - BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); - BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); - BBSputs(conn, "INFO - Display information about this BBS\r"); - BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); - BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); - BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); - BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); - - BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); - BBSputs(conn, "L - List Message(s) - \r"); - BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); - BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); - BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); - BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); - BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); - BBSputs(conn, " LC = List TO fields of all active bulletins\r"); - BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); - BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); - - BBSputs(conn, "N Name - Set Name\r"); - BBSputs(conn, "NODE - Return to Node\r"); - BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); - BBSputs(conn, "PASS Password - Set BBS Password\r"); - BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); - BBSputs(conn, "Q QTH - Set QTH\r"); - BBSputs(conn, "R - Read Message(s) - R num \r"); - BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); - BBSputs(conn, "READ Name - Read File\r"); - - BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); - BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); - BBSputs(conn, "X - Toggle Expert Mode\r"); - BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); - if (conn->sysop) - { - BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); - BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); - BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); - BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); - BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); - BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); - BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); - BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); - BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); - } - } - - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "Ver", CmdLen) == 0) - { - nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); - - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) - { - if (Arg1) - { - if (strlen(Arg1) > 40) Arg1[40] = 0; - - strcpy(user->HomeBBS, _strupr(Arg1)); - UpdateWPWithUserInfo(user); - - if (!strchr(Arg1, '.')) - BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); - } - - nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); - SendPrompt(conn, user); - - SaveUserDatabase(); - - return; - } - - if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) - { - DoEditUserCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "POLLRMS") == 0) - { - DoPollRMSCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) - { - DoHousekeepingCmd(conn, user, Arg1, Context); - return; - } - - - if (_stricmp(Cmd, "FWD") == 0) - { - DoFwdCmd(conn, user, Arg1, Context); - return; - } - - if (_memicmp(Cmd, "X", 1) == 0) - { - user->flags ^= F_Expert; - - if (user->flags & F_Expert) - BBSputs(conn, "Expert Mode\r"); - else - BBSputs(conn, "Expert Mode off\r"); - - SaveUserDatabase(); - SendPrompt(conn, user); - return; - } - - if (conn->Flags == 0) - { - if (!CheckForTooManyErrors(conn)) - BBSputs(conn, "Invalid Command\r"); - - SendPrompt(conn, user); - } - - // Send if possible - - Flush(conn); -} - -VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) -{ - // seems to be printf to a socket - - char buff[600]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(buff, format, arglist); - - BBSputs(conn, buff); -} - -// Code to delete obsolete files from Mail folder - -#ifdef WIN32 - -int DeleteRedundantMessages() -{ - WIN32_FIND_DATA ffd; - - char szDir[MAX_PATH]; - char File[MAX_PATH]; - HANDLE hFind = INVALID_HANDLE_VALUE; - int Msgno; - - // Prepare string for use with FindFile functions. First, copy the - // string to a buffer, then append '\*' to the directory name. - - strcpy(szDir, MailDir); - strcat(szDir, "\\*.mes"); - - - - // Find the first file in the directory. - - hFind = FindFirstFile(szDir, &ffd); - - if (INVALID_HANDLE_VALUE == hFind) - { - return 0; - } - - do - { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - OutputDebugString(ffd.cFileName); - } - else - { - Msgno = atoi(&ffd.cFileName[2]); - - if (MsgnotoMsg[Msgno] == 0) - { - sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); - Debugprintf("Tidy Mail - Delete %s\n", File); - -// if (DeletetoRecycleBin) - DeletetoRecycle(File); -// else -// DeleteFile(File); - } - } - } - while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); - return 0; -} - -#else - -#include - -int MsgFilter(const struct dirent * dir) -{ - return (strstr(dir->d_name, ".mes") != 0); -} - -int DeleteRedundantMessages() -{ - struct dirent **namelist; - int n; - struct stat STAT; - int Msgno = 0, res; - char File[100]; - - n = scandir("Mail", &namelist, MsgFilter, alphasort); - - if (n < 0) - perror("scandir"); - else - { - while(n--) - { - if (stat(namelist[n]->d_name, &STAT) == 0); - { - Msgno = atoi(&namelist[n]->d_name[2]); - - if (MsgnotoMsg[Msgno] == 0) - { - sprintf(File, "Mail/%s", namelist[n]->d_name); - printf("Deleting %s\n", File); - unlink(File); - } - } - free(namelist[n]); - } - free(namelist); - } - return 0; -} -#endif - -VOID TidyWelcomeMsg(char ** pPrompt) -{ - // Make sure Welcome Message doesn't ends with > - - char * Prompt = *pPrompt; - - int i = (int)strlen(Prompt) - 1; - - *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it - - Prompt = *pPrompt; - - while (Prompt[i] == 10 || Prompt[i] == 13) - { - Prompt[i--] = 0; - } - - while (i >= 0 && Prompt[i] == '>') - Prompt[i--] = 0; - - strcat(Prompt, "\r\n"); -} - -VOID TidyPrompt(char ** pPrompt) -{ - // Make sure prompt ends > CR LF - - char * Prompt = *pPrompt; - - int i = (int)strlen(Prompt) - 1; - - *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it - - Prompt = *pPrompt; - - while (Prompt[i] == 10 || Prompt[i] == 13) - { - Prompt[i--] = 0; - } - - if (Prompt[i] != '>') - strcat(Prompt, ">"); - - strcat(Prompt, "\r\n"); -} - -VOID TidyPrompts() -{ - TidyPrompt(&Prompt); - TidyPrompt(&NewPrompt); - TidyPrompt(&ExpertPrompt); -} - -BOOL SendARQMail(CIRCUIT * conn) -{ - conn->NextMessagetoForward = FirstMessageIndextoForward; - - // Send Message. There is no mechanism for reverse forwarding - - if (FindMessagestoForward(conn)) - { - struct MsgInfo * Msg; - char MsgHddr[512]; - int HddrLen; - char TimeString[64]; - char * WholeMessage; - - char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); - int MsgLen; - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r"); - conn->FwdMsg->length = (int)strlen(MsgBytes); - } - - Msg = conn->FwdMsg; - WholeMessage = malloc(Msg->length + 512); - - FormatTime(TimeString, (time_t)Msg->datecreated); - -/* -ARQ:FILE::flarqmail-1.eml -ARQ:EMAIL:: -ARQ:SIZE::96 -ARQ::STX -//FLARQ COMPOSER -Date: 16/01/2014 22:26:06 -To: g8bpq -From: -Subject: test message - -Hello -Hello - -ARQ::ETX -*/ - Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); - - HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", - TimeString, Msg->to, Msg->from, Msg->title); - - MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", - BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); - - WholeMessage[MsgLen] = 0; - QueueMsg(conn,WholeMessage, MsgLen); - - free(WholeMessage); - free(MsgBytes); - - // FLARQ doesn't ACK the message, so set flag to look for all acked - - conn->BBSFlags |= ARQMAILACK; - conn->ARQClearCount = 10; // To make sure clear isn't reported too soon - - return TRUE; - } - - // Nothing to send - close - - Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); - - conn->CloseAfterFlush = 20; - return FALSE; -} - -char *stristr (char *ch1, char *ch2) -{ - char *chN1, *chN2; - char *chNdx; - char *chRet = NULL; - - chN1 = _strdup (ch1); - chN2 = _strdup (ch2); - if (chN1 && chN2) - { - chNdx = chN1; - while (*chNdx) - { - *chNdx = (char) tolower (*chNdx); - chNdx ++; - } - chNdx = chN2; - while (*chNdx) - { - *chNdx = (char) tolower (*chNdx); - chNdx ++; - } - - chNdx = strstr (chN1, chN2); - if (chNdx) - chRet = ch1 + (chNdx - chN1); - } - free (chN1); - free (chN2); - return chRet; -} - -#ifdef WIN32 - -void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - - WIN32_FIND_DATA ffd; - - char szDir[MAX_PATH]; - HANDLE hFind = INVALID_HANDLE_VALUE; - - // Prepare string for use with FindFile functions. First, copy the - // string to a buffer, then append '\*' to the directory name. - - strcpy(szDir, GetBPQDirectory()); - strcat(szDir, "\\BPQMailChat\\Files\\*.*"); - - // Find the first file in the directory. - - hFind = FindFirstFile(szDir, &ffd); - - if (INVALID_HANDLE_VALUE == hFind) - { - nodeprintf(conn, "No Files\r"); - return; - } - - // List all the files in the directory with some info about them. - - do - { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - {} - else - { - if (filename == NULL || stristr(ffd.cFileName, filename)) - nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); - } - } - while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); -} - -#else - -#include - -void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - struct dirent **namelist; - int n, i; - struct stat STAT; - time_t now = time(NULL); - int Age = 0, res; - char FN[256]; - - n = scandir("Files", &namelist, NULL, alphasort); - - if (n < 0) - perror("scandir"); - else - { - for (i = 0; i < n; i++) - { - sprintf(FN, "Files/%s", namelist[i]->d_name); - - if (filename == NULL || stristr(namelist[i]->d_name, filename)) - if (FN[6] != '.' && stat(FN, &STAT) == 0) - nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); - - free(namelist[i]); - } - free(namelist); - } - return; -} -#endif - -void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - char * MsgBytes; - - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - struct stat STAT; - - if (filename == NULL) - { - nodeprintf(conn, "Missing Filename\r"); - return; - } - - if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) - { - nodeprintf(conn, "Invalid filename\r"); - return; - } - - if (BaseDir[0]) - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); - else - sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); - - if (stat(MsgFile, &STAT) != -1) - { - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - int Length; - - MsgBytes=malloc(FileSize+1); - fread(MsgBytes, 1, FileSize, hFile); - fclose(hFile); - - MsgBytes[FileSize]=0; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - QueueMsg(conn, MsgBytes, Length); - free(MsgBytes); - - nodeprintf(conn, "\r\r[End of File %s]\r", filename); - return; - } - } - - nodeprintf(conn, "File %s not found\r", filename); -} - -VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) -{ - struct TempUserInfo * Temp = user->Temp; - - Buffer[len] = 0; - - // Command entered during listing pause. May be A R or C (or ) - - if (Buffer[0] == 'A' || Buffer[0] == 'a') - { - // Abort - - Temp->ListActive = Temp->ListSuspended = FALSE; - SendPrompt(conn, user); - return; - } - - if (_memicmp(Buffer, "R ", 2) == 0) - { - // Read Message(es) - - int msgno; - char * ptr; - char * Context; - - ptr = strtok_s(&Buffer[2], " ", &Context); - - while (ptr) - { - msgno = atoi(ptr); - ReadMessage(conn, user, msgno); - - ptr = strtok_s(NULL, " ", &Context); - } - - nodeprintf(conn, "bort, , = Continue..>"); - return; - } - - if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) - { - // Resume Listing from where we left off - - DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); - SendPrompt(conn, user); - return; - } - - nodeprintf(conn, "bort, , = Continue..>"); - -} -/* -CreateMessageWithAttachments() -{ - int i; - char * ptr, * ptr2, * ptr3, * ptr4; - char Boundary[1000]; - BOOL Multipart = FALSE; - BOOL ALT = FALSE; - int Partlen; - char * Save; - BOOL Base64 = FALSE; - BOOL QuotedP = FALSE; - - char FileName[100][250] = {""}; - int FileLen[100]; - char * FileBody[100]; - char * MallocSave[100]; - UCHAR * NewMsg; - - int Files = 0; - - ptr = Msg; - - if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) - { - sockptr->MailBufferSize += 2000; - sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); - - if (sockptr->MailBuffer == NULL) - { - CriticalErrorHandler("Failed to extend Message Buffer"); - shutdown(sockptr->socket, 0); - return FALSE; - } - } - - - NewMsg = sockptr->MailBuffer + 1000; - - NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); - - for (i = 1; i < Files; i++) - { - NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); - } - - NewMsg += sprintf(NewMsg, "\r\n"); - - for (i = 0; i < Files; i++) - { - memcpy(NewMsg, FileBody[i], FileLen[i]); - NewMsg += FileLen[i]; - free(MallocSave[i]); - NewMsg += sprintf(NewMsg, "\r\n"); - } - - *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); - *Body = sockptr->MailBuffer + 1000; - - return TRUE; // B2 Message -} - -*/ -VOID CreateUserReport() -{ - struct UserInfo * User; - int i; - char Line[200]; - int len; - char File[MAX_PATH]; - FILE * hFile; - - sprintf(File, "%s/UserList.csv", BaseDir); - - hFile = fopen(File, "wb"); - - if (hFile == NULL) - { - Debugprintf("Failed to create UserList.csv"); - return; - } - - for (i=1; i <= NumberofUsers; i++) - { - User = UserRecPtr[i]; - - len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", - User->Call, - User->lastmsg, - FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), - User->flags, - User->Name, - User->Address, - User->RMSSSIDBits, - User->HomeBBS, - User->QRA, - User->ZIP -// struct MsgStats Total; -// struct MsgStats Last; - ); - fwrite(Line, 1, len, hFile); - } - - fclose(hFile); -} - -BOOL ProcessYAPPMessage(CIRCUIT * conn) -{ - int Len = conn->InputLen; - UCHAR * Msg = conn->InputBuffer; - int pktLen = Msg[1]; - char Reply[2] = {ACK}; - int NameLen, SizeLen, OptLen; - char * ptr; - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char Mess[255]; - int len; - char * FN = &Msg[2]; - - switch (Msg[0]) - { - case ENQ: // YAPP Send_Init - - // Shouldn't occur in session. Reset state - - Mess[0] = ACK; - Mess[1] = 1; - QueueMsg(conn, Mess, 2); - Flush(conn); - conn->InputLen = 0; - if (conn->MailBuffer) - { - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - } - return TRUE; - - case SOH: - - // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) - - // YAPPC has date/time in dos format - - if (Len < Msg[1] + 1) - return 0; - - NameLen = (int)strlen(FN); - strcpy(conn->ARQFilename, FN); - ptr = &Msg[3 + NameLen]; - SizeLen = (int)strlen(ptr); - FileSize = atoi(ptr); - - // Check file name for unsafe characters (.. / \) - - if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) - { - Mess[0] = NAK; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - - conn->InputLen = 0; - conn->InputMode = 0; - - return FALSE; - } - - OptLen = pktLen - (NameLen + SizeLen + 2); - - conn->YAPPDate = 0; - - if (OptLen >= 8) // We have a Date/Time for YAPPC - { - ptr = ptr + SizeLen + 1; - conn->YAPPDate = strtol(ptr, NULL, 16); - } - - // Check Size - - if (FileSize > MaxRXSize) - { - Mess[0] = NAK; - Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); - QueueMsg(conn, Mess, Mess[1] + 2); - - Flush(conn); - - len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - - conn->InputLen = 0; - conn->InputMode = 0; - - return FALSE; - } - - // Make sure file does not exist - - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - Mess[0] = NAK; - Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; - QueueMsg(conn, Mess, Mess[1] + 2); - - Flush(conn); - - len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - fclose(hFile); - - conn->InputLen = 0; - conn->InputMode = 0; - - return FALSE; - } - - - conn->MailBufferSize = FileSize; - conn->MailBuffer=malloc(FileSize); - conn->YAPPLen = 0; - - if (conn->YAPPDate) // If present use YAPPC - Reply[1] = ACK; //Receive_TPK - else - Reply[1] = 2; //Rcv_File - - QueueMsg(conn, Reply, 2); - - len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); - WriteLogLine(conn, '!', Mess, len, LOG_BBS); - - conn->InputLen = 0; - return FALSE; - - case STX: - - // Data Packet - - // Check we have it all - - if (conn->YAPPDate) // If present use YAPPC so have checksum - { - if (pktLen > (Len - 3)) // -3 for header and checksum - return 0; // Wait for rest - } - else - { - if (pktLen > (Len - 2)) // -2 for header - return 0; // Wait for rest - } - - // Save data and remove from buffer - - // if YAPPC check checksum - - if (conn->YAPPDate) - { - UCHAR Sum = 0; - int i; - UCHAR * uptr = &Msg[2]; - - i = pktLen; - - while(i--) - Sum += *(uptr++); - - if (Sum != *uptr) - { - // Checksum Error - - Mess[0] = CAN; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); - QueueMsg(conn, Mess, len); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - conn->InputLen = 0; - conn->InputMode = 0; - return TRUE; - } - } - - if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) - { - // Too Big ?? - - Mess[0] = CAN; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); - QueueMsg(conn, Mess, len); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - conn->InputLen = 0; - conn->InputMode = 0; - return TRUE; - } - - - memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); - conn->YAPPLen += pktLen; - - if (conn->YAPPDate) - ++pktLen; // Add Checksum - - conn->InputLen -= (pktLen + 2); - memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); - - return TRUE; - - case ETX: - - // End Data - - - - if (conn->YAPPLen == conn->MailBufferSize) - { - // All received - - int ret; - DWORD Written = 0; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); - -#ifdef WIN32 - hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) - { - ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); - - if (conn->YAPPDate) - { - FILETIME FileTime; - struct tm TM; - struct timeval times[2]; - time_t TT; -/* - The MS-DOS date. The date is a packed value with the following format. - - cant use DosDateTimeToFileTime on Linux - - Bits Description - 0-4 Day of the month (1–31) - 5-8 Month (1 = January, 2 = February, and so on) - 9-15 Year offset from 1980 (add 1980 to get actual year) - wFatTime - The MS-DOS time. The time is a packed value with the following format. - Bits Description - 0-4 Second divided by 2 - 5-10 Minute (0–59) - 11-15 Hour (0–23 on a 24-hour clock) -*/ - memset(&TM, 0, sizeof(TM)); - - TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; - TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); - TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); - - TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); - TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; - TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; - - Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); - - TT = mktime(&TM); - times[0].tv_sec = times[1].tv_sec = - times[0].tv_usec = times[1].tv_usec = 0; - - DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); - ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); - ret = GetLastError(); - - } - CloseHandle(hFile); - } -#else - - hFile = fopen(MsgFile, "wb"); - if (hFile) - { - Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); - fclose(hFile); - - if (conn->YAPPDate) - { - struct tm TM; - struct timeval times[2]; -/* - The MS-DOS date. The date is a packed value with the following format. - - cant use DosDateTimeToFileTime on Linux - - Bits Description - 0-4 Day of the month (1–31) - 5-8 Month (1 = January, 2 = February, and so on) - 9-15 Year offset from 1980 (add 1980 to get actual year) - wFatTime - The MS-DOS time. The time is a packed value with the following format. - Bits Description - 0-4 Second divided by 2 - 5-10 Minute (0–59) - 11-15 Hour (0–23 on a 24-hour clock) -*/ - memset(&TM, 0, sizeof(TM)); - - TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; - TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); - TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); - - TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); - TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; - TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; - - Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); - - times[0].tv_sec = times[1].tv_sec = mktime(&TM); - times[0].tv_usec = times[1].tv_usec = 0; - } - } -#endif - - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - - if (Written != conn->YAPPLen) - { - Mess[0] = CAN; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); - QueueMsg(conn, Mess, len); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - conn->InputLen = 0; - conn->InputMode = 0; - } - } - - Reply[1] = 3; //Ack_EOF - QueueMsg(conn, Reply, 2); - Flush(conn); - conn->InputLen = 0; - - return TRUE; - - case EOT: - - // End Session - - Reply[1] = 4; // Ack_EOT - QueueMsg(conn, Reply, 2); - Flush(conn); - conn->InputLen = 0; - conn->InputMode = 0; - - len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - - return TRUE; - - case CAN: - - // Abort - - Mess[0] = ACK; - Mess[1] = 5; // CAN Ack - QueueMsg(conn, Mess, 2); - Flush(conn); - - if (conn->MailBuffer) - { - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - } - - // There may be a reason after the CAN - - len = Msg[1]; - - if (len) - { - char * errormsg = &Msg[2]; - errormsg[len] = 0; - nodeprintf(conn, "File Rejected - %s\r", errormsg); - } - else - - nodeprintf(conn, "File Rejected\r"); - - - len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - - conn->InputLen = 0; - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - - return FALSE; - - case ACK: - - switch (Msg[1]) - { - case 1: // Rcv_Rdy - - // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) - - len = (int)strlen(conn->ARQFilename) + 3; - - strcpy(&Mess[2], conn->ARQFilename); - len += sprintf(&Mess[len], "%d", conn->MailBufferSize); - len++; // include null - Mess[0] = SOH; - Mess[1] = len - 2; - - QueueMsg(conn, Mess, len); - Flush(conn); - conn->InputLen = 0; - - return FALSE; - - case 2: - - // Start sending message - - YAPPSendData(conn); - conn->InputLen = 0; - return FALSE; - - case 3: - - // ACK EOF - Send EOT - - - Mess[0] = EOT; - Mess[1] = 1; - QueueMsg(conn, Mess, 2); - Flush(conn); - - conn->InputLen = 0; - return FALSE; - - case 4: - - // ACK EOT - - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - - conn->InputLen = 0; - return FALSE; - - default: - conn->InputLen = 0; - return FALSE; - - - - } - - case NAK: - - // Either Reject or Restart - - // RE Resume NAK len R NULL (File size in ASCII) NULL - - if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) - { - int posn = atoi(&Msg[4]); - - conn->YAPPLen += posn; - conn->MailBufferSize -= posn; - - YAPPSendData(conn); - conn->InputLen = 0; - return FALSE; - - } - - // There may be a reason after the ack - - len = Msg[1]; - - if (len) - { - char * errormsg = &Msg[2]; - errormsg[len] = 0; - nodeprintf(conn, "File Rejected - %s\r", errormsg); - } - else - - nodeprintf(conn, "File Rejected\r"); - - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - conn->InputLen = 0; - SendPrompt(conn, conn->UserPointer); - return FALSE; - } - - nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); - - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - conn->InputLen = 0; - SendPrompt(conn, conn->UserPointer); - - return FALSE; - -} - -void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - struct stat STAT; - - if (filename == NULL) - { - nodeprintf(conn, "Filename missing\r"); - SendPrompt(conn, user); - return; - } - - if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) - { - nodeprintf(conn, "Invalid filename\r"); - SendPrompt(conn, user); - return; - } - - if (BaseDir[0]) - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); - else - sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); - - if (stat(MsgFile, &STAT) != -1) - { - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - char Mess[255]; - strcpy(conn->ARQFilename, filename); - conn->MailBuffer = malloc(FileSize); - conn->MailBufferSize = FileSize; - conn->YAPPLen = 0; - fread(conn->MailBuffer, 1, FileSize, hFile); - fclose(hFile); - - Mess[0] = ENQ; - Mess[1] = 1; - - QueueMsg(conn, Mess, 2); - Flush(conn); - - conn->InputMode = 'Y'; - - return; - } - } - - nodeprintf(conn, "File %s not found\r", filename); - SendPrompt(conn, user); -} - -void YAPPSendData(ConnectionInfo * conn) -{ - char Mess[258]; - - conn->BBSFlags |= YAPPTX; - - while (TXCount(conn->BPQStream) < 15) - { - int Left = conn->MailBufferSize; - - if (Left == 0) - { - // Finished - send End Data - - Mess[0] = ETX; - Mess[1] = 1; - - QueueMsg(conn, Mess, 2); - Flush(conn); - - conn->BBSFlags &= ~YAPPTX; - break; - } - - if (Left > conn->paclen - 2) // 2 byte header - Left = conn->paclen -2; - - memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); - Mess[0] = STX; - Mess[1] = Left; - - QueueMsg(conn, Mess, Left + 2); - Flush(conn); - - conn->YAPPLen += Left; - conn->MailBufferSize -= Left; - } -} - -char * AddUser(char * Call, char * password, BOOL BBSFlag) -{ - struct UserInfo * USER; - - strlop(Call, '-'); - - if (strlen(Call) > 6) - Call[6] = 0; - - _strupr(Call); - - if (Call[0] == 0 || LookupCall(Call)) - { - return("User already exists\r\n"); - } - - USER = AllocateUserRecord(Call); - USER->Temp = zalloc(sizeof (struct TempUserInfo)); - - if (strlen(password) > 12) - password[12] = 0; - - strcpy(USER->pass, password); - - if (BBSFlag) - { - if(SetupNewBBS(USER)) - USER->flags |= F_BBS; - else - printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); - } - - SaveUserDatabase(); - UpdateWPWithUserInfo(USER); - - return("User added\r\n"); -} - -// Server Support Code - -// For the moment only internal REQDIR and REQFIL. - -// May add WPSERV and user implemented servers -/* -F6FBB BBS > - SP REQDIR @ F6ABJ.FRA.EU - Title of message : - YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU - Text of message : - /EX - - F6FBB BBS > - SP REQFIL @ F6ABJ.FRA.EU - Title of message : - DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU - Text of message : - /EX - - Note Text not used. - -*/ - -VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); - -BOOL ProcessReqDir(struct MsgInfo * Msg) -{ - char * Buffer; - int Len = 0; - char * ptr; - - // Parse title - gives directory and return address - - // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU - - // At the moment we don't allow subdirectories but no harm handling here - - char Pattern[64]; - char * Address; - char * filename = NULL; // ?? Pattern Match ?? - -#ifdef WIN32 - - WIN32_FIND_DATA ffd; - - char szDir[MAX_PATH]; - HANDLE hFind = INVALID_HANDLE_VALUE; - -#else - - #include - - struct dirent **namelist; - int n, i; - struct stat STAT; - int res; - char FN[256]; - -#endif - - strcpy(Pattern, Msg->title); - - ptr = strchr(Pattern, '@'); - - if (ptr == NULL) - - // if we don't have return address no point - // but could we default to sender?? - - return FALSE; - - *ptr++ = 0; // Terminate Path - - strlop(Pattern, ' '); - - while (*ptr == ' ') - ptr++; // accept with or without spaces round @ - - Address = ptr; - - ptr = Buffer = malloc(MaxTXSize); - -#ifdef WIN32 - - // Prepare string for use with FindFile functions. First, copy the - // string to a buffer, then append '\*' to the directory name. - - strcpy(szDir, GetBPQDirectory()); - strcat(szDir, "\\BPQMailChat\\Files\\"); - strcat(szDir, Pattern); - - // Find the first file in the directory. - - hFind = FindFirstFile(szDir, &ffd); - - if (INVALID_HANDLE_VALUE == hFind) - { - Len = sprintf(Buffer, "No Files\r"); - } - else - { - do - { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - {} - else - { - if (filename == NULL || stristr(ffd.cFileName, filename)) - Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); - } - } - while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); - } - -#else - - n = scandir("Files", &namelist, NULL, alphasort); - - if (n < 0) - perror("scandir"); - else - { - for (i = 0; i < n; i++) - { - sprintf(FN, "Files/%s", namelist[i]->d_name); - - if (filename == NULL || stristr(namelist[i]->d_name, filename)) - if (FN[6] != '.' && stat(FN, &STAT) == 0) - Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); - - free(namelist[i]); - } - free(namelist); - } - -#endif - - // Build Message - - SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); - return TRUE; -} - -/* - ' Augment Message ID with the Message Pickup Station we're directing this message to. - ' - Dim strAugmentedMessageID As String - If GetMidRMS(MessageId) <> "" Then - ' The MPS RMS is already set on the message ID - strAugmentedMessageID = MessageId - strMPS = GetMidRMS(MessageId) - ' "@R" at the end of the MID means route message only via radio - If GetMidForwarding(MessageId) = "" And (blnRadioOnly Or UploadThroughInternet()) Then - strAugmentedMessageID &= "@" & strHFOnlyFlag - End If - ElseIf strMPS <> "" Then - ' Add MPS to the message ID - strAugmentedMessageID = MessageId & "@" & strMPS - ' "@R" at the end of the MID means route message only via radio - If blnRadioOnly Or UploadThroughInternet() Then - strAugmentedMessageID &= "@" & strHFOnlyFlag - End If - Else - strAugmentedMessageID = MessageId - End If - -*/ - -void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) -{ - Buffer[len] = 0; - - if (conn->Flags & GETTINGSYNCMESSAGE) - { - // Data - - if ((conn->TempMsg->length + len) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); - - conn->TempMsg->length += len; - - if (conn->TempMsg->length >= conn->SyncCompressedLen) - { - // Complete - decompress it - - conn->BBSFlags |= FBBCompressed; - Decode(conn, 1); - - conn->Flags &= !GETTINGSYNCMESSAGE; - - BBSputs(conn, "OK\r"); - return; - } - return; - } - - if (conn->Flags & PROPOSINGSYNCMSG) - { - // Waiting for response to TR AddMessage - - if (strcmp(Buffer, "OK\r") == 0) - { - char Msg[256]; - int n; - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // Send the message, it has already been built - - conn->Flags &= !PROPOSINGSYNCMSG; - conn->Flags |= SENDINGSYNCMSG; - - n = sprintf_s(Msg, sizeof(Msg), "Sending SYNC message %s", conn->FwdMsg->bid); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - QueueMsg(conn, conn->SyncMessage, conn->SyncCompressedLen); - return; - } - - if (strcmp(Buffer, "NO\r") == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // Message Rejected - ? duplicate - - if (conn->FwdMsg) - { - // Zap the entry - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - conn->UserPointer->ForwardingInfo->MsgCount--; - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - conn->FwdMsg->Locked = 0; // Unlock - } - } - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->Flags &= !PROPOSINGSYNCMSG; - conn->BBSFlags &= ~SYNCMODE; - return; - } - - if (conn->Flags & SENDINGSYNCMSG) - { - if (strcmp(Buffer, "OK\r") == 0) - { - // Message Sent - - conn->Flags &= !SENDINGSYNCMSG; - free(conn->SyncMessage); - - if (conn->FwdMsg) - { - char Msg[256]; - int n; - - n = sprintf_s(Msg, sizeof(Msg), "SYNC message %s Sent", conn->FwdMsg->bid); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - conn->UserPointer->ForwardingInfo->MsgCount--; - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - conn->FwdMsg->Locked = 0; // Unlock - } - - // drop through to send any more - } - else - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - conn->Flags &= !SENDINGSYNCMSG; - free(conn->SyncMessage); - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - - return; - } - } - - if (strcmp(Buffer, "OK\r") == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // Send Message(?s) to RMS Relay SYNC - -/* -OK ->TR AddMessage_V5JLSGH591JR 786 1219 522 True -BYE*/ - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - struct MsgInfo * Msg = conn->FwdMsg; - char Buffer[128]; - char * Message; - - Message = FormatSYNCMessage(conn, Msg); - - // Need to compress it - - conn->SyncMessage = malloc(conn->SyncXMLLen + conn->SyncMsgLen + 4096); - - conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); - - sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", // The True on end indicates compressed - Msg->bid, conn->SyncCompressedLen, conn->SyncXMLLen, conn->SyncMsgLen); - - free(Message); - - conn->Flags |= PROPOSINGSYNCMSG; - - BBSputs(conn, Buffer); - return; - } - - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - return; - } - - if (memcmp(Buffer, "TR ", 2) == 0) - { - // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len - - char * Command; - char * BIDptr; - - BIDRec * BID; - char *ptr1, *ptr2, *context; - - // TR AddMessage_1145_G8BPQ 727 1202 440 True - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - Command = strtok_s(&Buffer[3], "_", &context); - BIDptr = strtok_s(NULL, " ", &context); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncCompressedLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncXMLLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncMsgLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - - // If addmessage need to check bid doesn't exist - - if (strcmp(Command, "AddMessage") == 0) - { - strlop(BIDptr, '@'); // sometimes has @CALL@R - if (strlen(BIDptr) > 12) - BIDptr[12] = 0; - - BID = LookupBID(BIDptr); - - if (BID) - { - BBSputs(conn, "Rejected - Duplicate BID\r"); - return; - } - } - - conn->TempMsg = zalloc(sizeof(struct MsgInfo)); - - conn->Flags |= GETTINGSYNCMESSAGE; - - BBSputs(conn, "OK\r"); - return; - } - - if (memcmp(Buffer, "BYE\r", 4) == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - return; - } - - if (memcmp(Buffer, "BBS\r", 4) == 0) - { - // Out of Sync - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - conn->BBSFlags &= ~SYNCMODE; - return; - } - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - return; -} -BOOL ProcessReqFile(struct MsgInfo * Msg) -{ - char FN[128]; - char * Buffer; - int Len = 0; - char * ptr; - struct stat STAT; - char MsgFile[MAX_PATH]; - FILE * hFile; - int FileSize; - char * MsgBytes; - - // Parse title - gives file and return address - - // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU - - // At the moment we don't allow subdirectories but no harm handling here - - char * Address; - char * filename = NULL; // ?? Pattern Match ?? - - strcpy(FN, Msg->title); - - ptr = strchr(FN, '@'); - - if (ptr == NULL) - - // if we don't have return address no point - // but could we default to sender?? - - return FALSE; - - *ptr++ = 0; // Terminate Path - - strlop(FN, ' '); - - while (*ptr == ' ') - ptr++; // accept with or without spaces round @ - - Address = ptr; - - ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null - - // Build Message - - if (FN == NULL) - { - Len = sprintf(Buffer, "Missing Filename\r"); - } - else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) - { - Len = sprintf(Buffer,"Invalid filename %s\r", FN); - } - else - { - if (BaseDir[0]) - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); - else - sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); - - if (stat(MsgFile, &STAT) != -1) - { - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - int Length; - - if (FileSize > MaxTXSize) - FileSize = MaxTXSize; // Truncate to max size - - MsgBytes=malloc(FileSize+1); - fread(MsgBytes, 1, FileSize, hFile); - fclose(hFile); - - MsgBytes[FileSize]=0; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - Len = sprintf(Buffer, "%s", MsgBytes); - free(MsgBytes); - } - } - else - Len = sprintf(Buffer, "File %s not found\r", FN); - } - - SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); - return TRUE; -} - -BOOL CheckforMessagetoServer(struct MsgInfo * Msg) -{ - if (_stricmp(Msg->to, "REQDIR") == 0) - return ProcessReqDir(Msg); - - if (_stricmp(Msg->to, "REQFIL") == 0) - return ProcessReqFile(Msg); - - return FALSE; -} - -VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) -{ - struct MsgInfo * Msg = AllocateMsgRecord(); - BIDRec * BIDRec; - char * Via; - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - - Msg->length = Length; - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - - FreeSemaphore(&MsgNoSemaphore); - - strcpy(Msg->from, BBSName); - Via = strlop(To, '@'); - - if (Via) - strcpy(Msg->via, Via); - - strcpy(Msg->to, To); - strcpy(Msg->title, Title); - - Msg->type = 'P'; - Msg->status = 'N'; - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - BIDRec = AllocateBIDRecord(); - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - MatchMessagetoBBSList(Msg, NULL); - free(MailBuffer); -} - -void SendRequestSync(CIRCUIT * conn) -{ - // Only need XML Header - - char * Buffer = malloc(4096); - int Len = 0; - - struct tm *tm; - char Date[32]; - char MsgTime[32]; - time_t Time = time(NULL); - - char * Encoded; - - tm = gmtime(&Time); - - sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - Len += sprintf(&Buffer[Len], "\r\n"); - - Len += sprintf(&Buffer[Len], "\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " request_sync\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Date); - Len += sprintf(&Buffer[Len], " %s\r\n", BBSName); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " BBSName\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", conn->SyncHost); - Len += sprintf(&Buffer[Len], " %d\r\n", conn->SyncPort); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], "\r\n"); - -/* - - - - request_sync - 20230205100652 - GI8BPQ - - - GI8BPQ - - 127.0.0.1 - 8780 - - - -*/ - - // Need to compress it - - conn->SyncXMLLen = Len; - conn->SyncMsgLen = 0; - - conn->SyncMessage = malloc(conn->SyncXMLLen + 4096); - - conn->SyncCompressedLen = Encode(Buffer, conn->SyncMessage, conn->SyncXMLLen, 0, 1); - - sprintf(Buffer, "TR RequestSync_%s_%d %d %d 0 True\r", // The True on end indicates compressed - 50, conn->SyncCompressedLen, conn->SyncXMLLen); - - free(Buffer); - - conn->Flags |= REQUESTINGSYNC; - - BBSputs(conn, Buffer); - return; -} - - -void ProcessSyncXML(CIRCUIT * conn, char * XML) -{ - // Process XML from RMS Relay Sync - - // All seem to start - - // - // - // - // - - char * Type = strstr(XML, ""); - - if (Type == NULL) - return; - - Type += strlen(""); - - if (memcmp(Type, "rms_location", 12) == 0) - { - return; - } - - - if (memcmp(Type, "request_sync", 12) == 0) - { - char * Call; - struct UserInfo * BBSREC; - - // This isn't requesting a poll, it is asking to be added as a sync partner - - Call = strstr(Type, ""); - - if (Call == NULL) - return; - - Call += 10; - strlop(Call, '<'); - BBSREC = FindBBS(Call); - - if (BBSREC == NULL) - return; - - if (BBSREC->ForwardingInfo->Forwarding == 0) - StartForwarding(BBSREC->BBSNumber, NULL); - - return; - } - - if (memcmp(Type, "remove_message", 14) == 0) - { - char * MID = strstr(Type, ""); - struct MsgInfo * Msg; - - if (MID == NULL) - return; - - MID += 11; - strlop(MID, '<'); - - strlop(MID, '@'); // sometimes has @CALL@R - if (strlen(MID) > 12) - MID[12] = 0; - - Msg = FindMessageByBID(MID); - - if (Msg == NULL) - return; - - Logprintf(LOG_BBS, conn, '|', "Killing Msg %d %s", Msg->number, Msg->bid); - - FlagAsKilled(Msg, TRUE); - return; - } - - if (memcmp(Type, "delivered", 9) == 0) - { - char * MID = strstr(Type, ""); - struct MsgInfo * Msg; - - if (MID == NULL) - return; - - MID += 11; - strlop(MID, '<'); - - strlop(MID, '@'); // sometimes has @CALL@R - if (strlen(MID) > 12) - MID[12] = 0; - - Msg = FindMessageByBID(MID); - - if (Msg == NULL) - return; - - Logprintf(LOG_BBS, conn, '|', "Message Msg %d %s Delivered", Msg->number, Msg->bid); - return; - } - - Debugprintf(Type); - return; - -/* - - - - request_sync - 20230205100652 - GI8BPQ - - - GI8BPQ - - 127.0.0.1 - 8780 - - - -} - - - - delivered - 20230205093113 - G8BPQ - - - 10845_GM8BPB - G8BPQ - G8BPQ - 3 - - - - Public Enum MessageDeliveryMethod - ' - ' Method used to deliver a message. None if the message hasn't been delivered. - ' - Unspecified = -1 - None = 0 - Telnet = 1 - CMS = 2 - Radio = 3 - Email = 4 -End Enum -*/ -} - -int ReformatSyncMessage(CIRCUIT * conn) -{ - // Message has been decompressed - reformat to look like a WLE message - - char * MsgBit; - char *ptr1, *ptr2; - int linelen; - char FullFrom[80]; - char FullTo[80]; - char BID[80]; - time_t Date; - char Mon[80]; - char Subject[80]; - int i = 0; - char * Boundary; - char * Input; - char * via = NULL; - char * NewMsg = conn->MailBuffer; - char * SaveMsg = NewMsg; - char DateString[80]; - struct tm * tm; - char Type[16] = "Private"; - char * part[100] = {""}; - char * partname[100]; - int partLen[100]; - char xml[4096]; - - // Message has an XML header then the message - - // The XML may have control info, so examine it. - - /* - Date: Mon, 25 Oct 2021 10:22:00 -0000 - From: GM8BPQ - Subject: Test - To: 2E1BGT - Message-ID: ALYJQJRXVQAO - X-Source: GM8BPQ - X-Relay: G8BPQ - MIME-Version: 1.0 - MIME-Version: 1.0 - Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" - - --boundaryBSoxlw== - Content-Type: text/plain; charset="iso-8859-1" - Content-Transfer-Encoding: quoted-printable - - Hello Hello - - --boundaryBSoxlw==-- - */ - - // I think the best way is to reformat as if from Winlink Express, then pass - //through the normal B2 code. - -// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); - - // display the message for testing - - conn->MailBuffer[conn->TempMsg->length] = 0; - -// OutputDebugString(conn->MailBuffer); - memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); - xml[conn->SyncXMLLen] = 0; - - if (conn->SyncMsgLen == 0) - { - // No message, Just xml. Looks like a status report - - ProcessSyncXML(conn, xml); - return 0; - } - - MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; - conn->TempMsg->length -= conn->SyncXMLLen; - - ptr1 = MsgBit; - -Loop: - - ptr2 = strchr(ptr1, '\r'); - - linelen = (int)(ptr2 - ptr1); - - if (_memicmp(ptr1, "From:", 5) == 0) - { - memcpy(FullFrom, &ptr1[6], linelen - 6); - FullFrom[linelen - 6] = 0; - } - - if (_memicmp(ptr1, "To:", 3) == 0) - { - memcpy(FullTo, &ptr1[4], linelen - 4); - FullTo[linelen - 4] = 0; - } - - else if (_memicmp(ptr1, "Subject:", 8) == 0) - { - memcpy(Subject, &ptr1[9], linelen - 9); - Subject[linelen - 9] = 0; - } - - else if (_memicmp(ptr1, "Message-ID", 10) == 0) - { - memcpy(BID, &ptr1[12], linelen - 12); - BID[linelen - 12] = 0; - } - - else if (_memicmp(ptr1, "Date:", 5) == 0) - { - struct tm rtime; - char seps[] = " ,\t\r"; - - memset(&rtime, 0, sizeof(struct tm)); - - // Date: Mon, 25 Oct 2021 10:22:00 -0000 - - sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", - &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - rtime.tm_year -= 1900; - - for (i = 0; i < 12; i++) - { - if (strcmp(Mon, month[i]) == 0) - break; - } - - rtime.tm_mon = i; - - Date = mktime(&rtime) - (time_t)_MYTIMEZONE; - - if (Date == (time_t)-1) - Date = time(NULL); - - } - - if (linelen) // Not Null line - { - ptr1 = ptr2 + 2; // Skip crlf - goto Loop; - } - - // Unpack Body - seems to be multipart even if only one - - // Can't we just send the whole body through ?? - // No, Attachment format is different - - // Mbo: GM8BPQ - // Body: 17 - // File: 1471 leadercoeffs.txt - - Input = MsgBit; - Boundary = initMultipartUnpack(&Input); - - i = 0; - - if (Boundary) - { - // input should be start of part - - // Find End of part - ie -- Boundary + CRLF or -- - - char * ptr, * saveptr; - char * Msgptr; - size_t BLen = strlen(Boundary); - size_t Partlen; - - saveptr = Msgptr = ptr = Input; - - while(ptr) // Just in case we run off end - { - if (*ptr == '-' && *(ptr+1) == '-') - { - if (memcmp(&ptr[2], Boundary, BLen) == 0) - { - // Found Boundary - - char * p1, *p2, *ptr3, *ptr4; - int llen; - int Base64 = 0; - int QuotedP = 0; - char * BoundaryStart = ptr; - - Partlen = ptr - Msgptr; - - ptr += (BLen + 2); // End of Boundary - - if (*ptr == '-') // Terminating Boundary - Input = NULL; - else - Input = ptr + 2; - - // Will check for quoted printable - - p1 = Msgptr; -Loop2: - p2 = strchr(p1, '\r'); - llen = (int)(p2 - p1); - - if (llen) - { - - if (_memicmp(p1, "Content-Transfer-Encoding:", 26) == 0) - { - if (_memicmp(&p1[27], "base64", 6) == 0) - Base64 = TRUE; - else if (_memicmp(&p1[27], "quoted", 6) == 0) - QuotedP = TRUE; - } - else if (_memicmp(p1, "Content-Disposition: ", 21) == 0) - { - ptr3 = strstr(&p1[21], "name"); - - if (ptr3) - { - ptr3 += 5; - if (*ptr3 == '"') ptr3++; - ptr4 = strchr(ptr3, '"'); - if (ptr4) *ptr4 = 0; - - partname[i] = ptr3; - } - } - - if (llen) // Not Null line - { - p1 = p2 + 2; // Skip crlf - goto Loop2; - } - } - - part[i] = strstr(p2, "\r\n"); // Over separator - - if (part[i]) - { - part[i] += 2; - partLen[i] = BoundaryStart - part[i] - 2; - if (QuotedP) - partLen[i] = decode_quoted_printable(part[i], partLen[i]); - else if (Base64) - { - int Len = partLen[i], NewLen; - char * ptr = part[i]; - char * ptr2 = part[i]; - - // WLE sends base64 with embedded crlf, so remove them - - while (Len-- > 0) - { - if ((*ptr) != 10 && (*ptr) != 13) - *(ptr2++) = *(ptr++); - else - ptr ++; - } - - Len = ptr2 - part[i]; - ptr = part[i]; - ptr2 = part[i]; - - while (Len > 0) - { - decodeblock(ptr, ptr2); - ptr += 4; - ptr2 += 3; - Len -= 4; - } - - NewLen = (int)(ptr2 - part[i]); - - if (*(ptr-1) == '=') - NewLen--; - - if (*(ptr-2) == '=') - NewLen--; - - partLen[i] = NewLen; - } - } - Msgptr = ptr = Input; - i++; - continue; } - - // See if more parts - } - ptr++; - } - ptr++; - } - - - // Build the message - - tm = gmtime(&Date); - - sprintf(DateString, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - NewMsg += sprintf(NewMsg, - "MID: %s\r\n" - "Date: %s\r\n" - "Type: %s\r\n" - "From: %s\r\n", - BID, DateString, Type, FullFrom); - -// if (ToCalls) -// { -// int i; - -// for (i = 0; i < Calls; i++) -// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); - -// } -// else - { - NewMsg += sprintf(NewMsg, "To: %s\r\n", - FullTo); - } -// if (WebMail->CC && WebMail->CC[0]) -// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); - - NewMsg += sprintf(NewMsg, - "Subject: %s\r\n" - "Mbo: %s\r\n", - Subject, BBSName); - - // Write the Body: line and any File Lines - - NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); - - i = 1; - - while (part[i]) - { - NewMsg += sprintf(NewMsg, "File: %d %s\r\n", - partLen[i], partname[i]); - - i++; - } - - NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header - - // Now add parts - - i = 0; - - while (part[i]) - { - memmove(NewMsg, part[i], partLen[i]); - NewMsg += partLen[i]; - i++; - NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line between attachments - } - - conn->TempMsg->length = NewMsg - SaveMsg; - conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); - conn->TempMsg->datecreated = Date; - strcpy(conn->TempMsg->bid, BID); - - if (strlen(Subject) > 60) - Subject[60] = 0; - - strcpy(conn->TempMsg->title, Subject); - - return TRUE; -} - -char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) -{ - // First an XML Header - - char * Buffer = malloc(4096 + Msg->length); - int Len = 0; - - struct tm *tm; - char Date[32]; - char MsgTime[32]; - char Separator[33]=""; - time_t Time = time(NULL); - char * MailBuffer; - int BodyLen; - char * Encoded; - - // Get the message - may need length in header - - MailBuffer = ReadMessageFile(Msg->number); - - BodyLen = Msg->length; - - // Remove any B2 Header - - if (Msg->B2Flags & B2Msg) - { - // Remove B2 Headers (up to the File: Line) - - char * ptr; - ptr = strstr(MailBuffer, "Body:"); - if (ptr) - { - BodyLen = atoi(ptr + 5); - ptr = strstr(ptr, "\r\n\r\n"); - } - if (ptr) - { - memcpy(MailBuffer, ptr + 4, BodyLen); - MailBuffer[BodyLen] = 0; - } - } - - // encode body as quoted printable; - - Encoded = malloc(Msg->length * 3); - - BodyLen = encode_quoted_printable(MailBuffer, Encoded, BodyLen); - - // Create multipart Boundary - - CreateOneTimePassword(&Separator[0], "Key", 0); - CreateOneTimePassword(&Separator[16], "Key", 1); - - - tm = gmtime(&Time); - - sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - tm = gmtime((time_t *)&Msg->datecreated); - - sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - Len += sprintf(&Buffer[Len], "\r\n"); - - Len += sprintf(&Buffer[Len], "\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " add_message\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Date); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); - Len += sprintf(&Buffer[Len], " \r\n", MsgTime); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], " 2\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", (Msg->B2Flags & Attachments) ? "true" : "false"); - Len += sprintf(&Buffer[Len], " %d\r\n", BodyLen); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->title); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); - Len += sprintf(&Buffer[Len], " 450443\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->to); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " 0\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " True\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], "\r\n"); - -// Debugprintf(Buffer); - - conn->SyncXMLLen = Len; - - Len += sprintf(&Buffer[Len], "Date: Sat, 04 Feb 2023 11:19:00 +0000\r\n"); - Len += sprintf(&Buffer[Len], "From: %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], "Subject: %s\r\n", Msg->title); - Len += sprintf(&Buffer[Len], "To: %s\r\n", Msg->to); - Len += sprintf(&Buffer[Len], "Message-ID: %s\r\n", Msg->bid); -// Len += sprintf(&Buffer[Len], "X-Source: G8BPQ\r\n"); -// Len += sprintf(&Buffer[Len], "X-Location: 52.979167N, 1.125000W (GRID SQUARE)\r\n"); -// Len += sprintf(&Buffer[Len], "X-RMS-Originator: G8BPQ\r\n"); -// Len += sprintf(&Buffer[Len], "X-RMS-Path: G8BPQ@2023-02-04-11:19:29\r\n"); - Len += sprintf(&Buffer[Len], "X-Relay: %s\r\n", BBSName); - - Len += sprintf(&Buffer[Len], "MIME-Version: 1.0\r\n"); - Len += sprintf(&Buffer[Len], "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", Separator); - - Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before separator - Len += sprintf(&Buffer[Len], "--%s\r\n", Separator); - Len += sprintf(&Buffer[Len], "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); - Len += sprintf(&Buffer[Len], "Content-Transfer-Encoding: quoted-printable\r\n"); - Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before body - - Len += sprintf(&Buffer[Len], "%s\r\n", Encoded); - Len += sprintf(&Buffer[Len], "--%s--\r\n", Separator); - - conn->SyncMsgLen = Len - conn->SyncXMLLen; - - free(Encoded); - free(MailBuffer); - - return Buffer; -} - -int encode_quoted_printable(char *s, char * out, int Len) -{ - int n = 0; - char * start = out; - - while(Len--) - { - if (n >= 73 && *s != 10 && *s != 13) - {strcpy(out, "=\r\n"); n = 0; out +=3;} - if (*s == 10 || *s == 13) {putchar(*s); n = 0;} - else if (*s<32 || *s==61 || *s>126) - out += sprintf(out, "=%02x", (unsigned char)*s); - else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) - {*(out++) = *s; n++;} - else n += printf("=20"); - - s++; - } - *out = 0; - - return out - start; -} - -int decode_quoted_printable(char *ptr, int len) -{ - // overwrite input with decoded version - - char * ptr2 = ptr; - char * End = ptr + len; - char * Start = ptr; - - while (ptr < End) - { - if ((*ptr) == '=') - { - char c = *(++ptr); - char d; - - c = c - 48; - if (c < 0) - { - // = CRLF as a soft break - - ptr += 2; - continue; - } - if (c > 9) c -= 7; - d = *(++ptr); - d = d - 48; - if (d > 9) d -= 7; - - *(ptr2) = c << 4 | d; - ptr2++; - ptr++; - } - else - *ptr2++ = *ptr++; - } - return ptr2 - Start; -} diff --git a/BPQINP3.c b/BPQINP3.c index 8a1860a..e541518 100644 --- a/BPQINP3.c +++ b/BPQINP3.c @@ -35,6 +35,9 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include //#include "vmm.h" +uint64_t timeLoadedMS = 0; + + static VOID SendNetFrame(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Frame) { // INP3 should only ever send over an active link, so just queue the message @@ -320,7 +323,7 @@ VOID ProcessRTTReply(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff) Route->Timeout = 0; // Got Response sscanf(&Buff->L4DATA[6], "%d", &OrigTime); - RTT = GetTickCount() - OrigTime; + RTT = (GetTickCount() - timeLoadedMS) - OrigTime; if (RTT > 60000) return; // Ignore if more than 60 secs @@ -379,6 +382,11 @@ VOID ProcessINP3RIF(struct ROUTE * Route, UCHAR * ptr1, int msglen, int Port) rtt = (*ptr1++ << 8); rtt += *ptr1++; + // rtt is value from remote node. Add our RTT to that node and update hops + + rtt += Route->SRTT; + hops++; + msglen -= 10; while (*ptr1 && msglen > 0) @@ -766,7 +774,7 @@ VOID SendRTTMsg(struct ROUTE * Route) Msg->L4FLAGS = L4INFO; - sprintf(Stamp, "%10d %10d %10d %10d ", GetTickCount(), Route->SRTT/10, Route->RTT/10, 0); + sprintf(Stamp, "%10llu %10d %10d %10d ", (GetTickCount() - timeLoadedMS), Route->SRTT/10, Route->RTT/10, 0); memcpy(RTTMsg.TXTIME, Stamp, 44); memcpy(Msg->L4DATA, &RTTMsg, 236); diff --git a/BPQMail.aps b/BPQMail.aps index 2e2b5feee10127aa668fd7c5087333925df9f17a..694b39f7766dad71d538e1d41d602f2a116248da 100644 GIT binary patch delta 17484 zcmaJ}37AyXmA88=@#;RrT)5tEyMsCAC?vil%{BX-PYx(2XpK5Vgi+ zl1aXC0+|<6Nz6JEwTX#HIxaDSD8{&eYZ4R{A@Q5ZOmv3KBx5Ef)AOHm?yGv$m?^)e z-aFep_uO;OJ@?#OzVnr;UmuyWr^N_=p|#EW+J_Cb;o|k#ZNpo)t-o^gx{>wCk>T*x zjUO9cA0GbLdV6%o)(yk!ui0{AdtiNNbo1uX5e*8au8I$B+_Ylr2KDdGyStm5mAj{p z&)w6u;OtPYKN$;0(#d#0b^CgJs=~SGJN5ZWLq)^!Tq>=4eLlac(yv*552z{nJwoVq zHC4YR_`O3-llLIMcB<+6RnT3kTHX^Gn+dwrj3OXQ0((?`ra)pD(u`oeYL*MiBmwaI z)LAaT%n-n@W(y#0Mj_f`SMvlEjD};;h?R*} z$B8)*P-i=dyBg}|yBrXli3+YmEpTqVt8RIX;Ig)r7G6PjsoEkU=vTcx-Ce5AdHk+M zquzPru7(R51Z&1($$?NZo+64~)hN(lEE!s5LERnKx%uuzH7$bcPxo6fq3=|!&V6^+UwMw^ zN`=jg73w!LaZ?DoRhys)7)r)GQhU@ALZx$YE0Oh6Ll~K*&fI(I@*g5ZJV%zXRR`;O zu7Dx~VFm=$c>;)Awr%!VLe{R933MQBrZU!=4AXX~^EK2=C!&c|R=}O=0xfgX(6;NC zvP&%|P;WdJiY0ApAesm#2N=_>F4Qto{NAHh(0eFH-U8#1lD+C8=dF8cF7*jz*a~L* z2m$}{3n-TC3kF$LcvqkZ%Y;|}9IV~(jV&-b9Bb@6qth7~t2MfuPme7%x}AH+7B1^C z6s9A!n6^Z6uj(}v1~p_R;E^mj;8Pbnr^lL%OPpDIn`3nu?b7i}VIN#mdV)QxB?QJ&t zo!{(TXhg@SfA{-6-5tvV>MUifT8${Pee1@Nzf$LPt?;b~bgT%h_no6=E2CB6L;wD2 zf}ZH}kydB%+i+i}_wBYBJml-IO!g<7)<3ixOU8G99^!WIqe;ulv4DBA@|IX=( z$@_n zip5mL$2ZsMdb=&(Npl9lFQ!zdLin+hF?V7EK)7T=PjvkrDtqN=T!Q#94~Q;k=X5M1 zu?TLS*MQH5sTJn@uR)|^(OWvzKq^bERa#0p zS54I2;Wy+jY1HOB+ySE{La7c#-#H9`erhj1`IYKLgP~l;iY2TJ42U496tx>ogkssS zr9k3+g%s~AM0sC9Y(@%qRvN8^_bQF0`Txh^;ps}<0n?W|A=U?W!s_W2^nlOr5MOe( zl`_+2CYe?~L>jf)&OuQ-noQ)PiAWMk0?1(qPgC(S4}bGj-cSqhP=miZ{4M0J#s_Ht z6*@vJSOP*;3~$UfA5<w?QnwUGkVn!WsP`mAXTMr3|xZ&RMT8+Kd0};-lCt2MZ;6xl37zjI*ssLd#{$0WdYYir>-`6RGVPvw&R0=5uB3-%h2T7a!$Yq$QnPmb>aYy;FI(xtfJyCK6KN86z7zq(lwMg1TI_MAd zxN1;P;Mq^ng&Um#mSuVFpeK$5$bQtLS@=7@Xvb6<>0d6K3?)b|5%DM?FVsPWf(&2= zR%zGv!o&YsY0U@!Dh&=$c}S(yLZjJRCh@~|iEJEgYUc^%63H-Jrc*|myn$mr0In~? zJrb*kLJt@6tU)`O;vA=whKv-gK-WWg5f-56DW(eLkR+nB0wKa#L`YdXc|X8i!Y|-# zCR75kNTN(zq16EEkrqm%)@G~(6xuw9Iq3!hW94a%^06XJh!Lp!O#pkngqRzol!ghR z9nYjxr|UxkOIxemj=2cvm6x8u#-P_;BlkTxN-UNf|$Ee`H~6oSVIa{0)+Vb!=uQptS^*x2DH_N1jtG>YxrMpr#(+=`777h* z+C(tjnd!>e{c;@wAon0Rv~4)NaVz1uS6*RuW-tb~Vw6iBw9PGBSeyx;`sX&|^fyB2cKUgc*!k*(f1&j1oj1UKmwK8GuZU{1iMGTTwHG1vr^NGMk7EYJ^M->MkN{E*?#=q@p*FB*cmU2D}far2`|X>unRLSAH2Zo!KFnWaS`bm5)YrdGzoMdwK{5QsF;c9^y2C+ zS6aZ?ZrOykr4WRf>zH4vU*ypn%oub+>JhAUM!tlloD?VJI5GpP2TKd~$faR+vD_O@ zcOaJFJd{Y~gQiW+F7MPgGm|kx{m2^GGYL)&igYs;&6_l?fwJN2Pg5R9U~S?$wo-o@ zQPZ+1S_R6&cg(ail+5syY}d>C46&@#Ccx6JgIPUwmFi0X!4TNcQ)=ecI;1!4(2~~g zIry8)INDxNfYx}Z>+CMnO6F0b0@f9+HJ`)W)*3jtmmfail}{^UDwVXO8DycPnC>2B zhVh(Dsa_=Ffs<6K{pBgcM*gI`T*G^*3}ti#5}(07A+(Ahy8l9it&4_f&oWb-Ac9VN z2h-64f((gB0oHRhiS2?6P~O*}y`@M^3md2bu7vSg@YjmJb68=rO}RUCXm_Y5%|kKE zOzYOzov@%Ka$2BvkQqfng6Gi^5alh=Ah=w5f-_gpl4#VSfh~$Dque_^Tp}K0O??1N zp|W$O70TGy8W2d&WhR5Y^5lyIEYWFo^#C#<7PF08Vl;jo8r-6SgYaanWoOZ2VT9P| zUnO^J-?C#n$O)k|DYuW!DU4|-77NOJd~{^v_R+0G3zSkc6Ub^%nP7S-MT_B)kA^pH z+b}vZf}O_jM>QV%eX_EM*MrIQ5yE2TcG>~(b zm#*Td5Wj6l!xUk=i&k}^6u-KQ7IdNQ1T7lM`Sw3A2>H5oZzZ0G(Hko5$V$_#6&IY& z`ZGzfNw;>>>CWYMHf&~Xg)WioN3lQ?X=igWoVP;>-3}Wj=l(kzO2IsogX2oL@6lau6eUVF4Lgacq&;(GLWx%H0}6uDuCxJ`}%9)e>!1#Pme_Sar*oEGQE-o>b5DQEdB4Jhaq zQ&bepk$w#HM>FUWR&yA85)NBJ!tk!lx%HKX!80g=u`Dz5=F3f&Merb|7wX&K=R~y} z8K-Sq1}Olh0k!L#ex;tW@N%lYw27PvCOK2JShTM{NQKa#$pXYc4Z3*25@he< zM;-@Gr}^iNUh5E2rk37nyJky8Lzyf}C#VFJX-%q9@)E$7*Q*IFO9UAGe+ShQG9`j#G6am8Auwit^$aa?p zVZgcY8;!1dBrdaY^YB*EffrP;d1!=DFTBjG$3mD1ktaWZRE;R(pn{fL zT@VfsBQ3%~K@ncmjfwTzrh5`E5^J~+LuV%D6)$Qiq2}Tm3Dygt&{95UQv?P=kEf8! zj=}?;;mrQFt_HXiTytkb>Zv0#d38pBDjP|uE0mx@ibWEYUlQTg2t+nzL%h8$A($bL zEjcUe772trm5jx(43ii=)*_z!jMOt1Ly@akK@eP4hSn{8oxyMuaZrE}M--#xJ)lq= zF~{acbsi!z%v9Q@9Fh1v<03~zo+BU5sA*=Mg%*ItwGHc|8@ESCt{nxdq}vqpu&<4$ zs2SwLHpnSECpfheM|wyYJsAW6OHdUVAd01k|B%w&&A|^eIgp22N z;!V|1TzzrFlB}ujIV=!vwl2#MAz%}LwX6~tyBC19LkJidG+U+HcTKmpVG$uxv1OO zn0uMW3vw^?lwc_27jN~Hpaj%6dptnz<(@JyEW;Ok%78EoU-Iz)xk|=s5?}No%#~b+ zic^F7woj3s&ko3W_{NV14FB}r+|dr#;3V&=Z}E_EYo*&if^qV9)voan(bVz^)7~JJ zJ0RVwK+GcRYT81`p#7?xOyC}n_LN!9n|IYyO71T3yk{+47V==qE({6WX}!DlT211n ztr;`Orw8d|O!;nVr(z+pRGep`bQBxJ8O(ry^|4ZJAi6eaqV&VGoY8_7zy#IS7|02& zbNcxOX%_T>7nE2Dstkv7G*6KPAQf}xHHCMCM)we#FnVUscW$|R;bLZ{QeW|4nn?8A zt8@0zV$nZbo-V*iH{BqDPOVAGq}y(c zX#pk=)TAqKMI06}&$TyR89wONn};5=k95+@4z)b#_8Zem(iOI1R>pFEb5C`&2#w>a zS?NZcZn5k}9Ft0y059E&6S(6an~$p$PGsy{nOx1z=va;W_?fXKg#6xE(+Y@2Et8^q z>6#p1XIzuhVt+f<=n6S&?_voXqD0xHIZbWuT|n*JySMo=?!sTZK*#h_PAj`a$M9LW z$#7poYtf}p;tj_vJ;I2O7VTlkt3I?(W@7)oHg?33ed1kzzps_w_wSpJEFNQ#qv@SX zrjP-HUDt|Su-wI9I8QVe&MP2;IYc{W-CwubBaLr8*j6T+5}7>FTwb{x;K6e6DZ>gE z&pl_?{i66+?q76?rwIwWLTFNtT>+#TR-Dve4res1Vvr5;+xr{1<7e$}@XCp&vupuB z(;{K?h;nfO!HHpq_KTcbK3Gnt(CUdQ#&zX%2~NV!OIpsm`?>b!s_)YF3N1&O$v9|9 z9rJS}J_D(H!bWj&Kalu(^A8%=en8K#QDI5p59&5HDvY!~5UD#8VdI|u0hjoo3oW;} z1WUyyVy5Mu;!PWkPYK5fFyq;%MI44r*x+rG*l=*d3~o-q!Xmh6E8q?~>?}5dJrpXR zOfS~Hb6o83@c1HHklY5-Rtjf3LMQ?Kx8t&)&irAco*�PUn+C#@ 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 delta 2554 zcmb7GeQaA-6+h=ae8S)$4T5I>(;b*_Dh`DNjqZuJ%3Pt<=Kv-&WY1DS(+|1 zZPTJjXoR9oFttmXU5UGD^h0A%1fr~RrHCO_MO50DiWo2%ZN#ess}t!HvPyo!z#Z7KF$U8$Gl4zoOzWzRLfgVmDsnRTbNawQ1N%Y*;f`ky!UN z7=awT-d3%i1@pqqm=)-U(0~eTf&lkDif#O+hP{~M?`t?L=iqxhq~Uh1M6rk1HGVQi z?06KDxWa!L#o^W*1TFu~nQS_1-B-WP74NC zfTqo`qr&b3>pD#vydP{P4;$1E_H}C715ju3bUtSU3_|^eP!I_NFxX%!$$V7Lq=XiP z&()-Sh6JA%9;peTybyfQh-~#+&HFcPWK)IS2b+ZuDW^i?sa%XC#RpBM6i%qgL{!Ts zQku{e*z&TYRoaSd3vb44%-02 zloZw=w6JAI>(UN0Ae2mIijhnzE4(1InrfKB4`|eKAm9b3sTD(dI-Suog*^2a2>75) z=qmk`L^@9;3dwv9`n=*6xlBh>6ZvA+&=hDlwL&72Nk8b}&y%`}R)LiR*DK zX-50O&8Av*wC`LWlT|}`EfNpqQz0{85W0|sF19XpiwM1(($WQ=C`7lS#~h@ma^XTU zsU~BF?t@2#o+^vU%R#@<=Ppx<7F9z~;4xE3(S(I!niy4`uV(c;1NZhux%3 zQ$jH>iFCGL>VD{5Pda~S@u(3x0DDX+lFa0^Vj`_(iiR-=d)G5(4IY5c3mz%wLSq_D zoM zi@|>AXYN*qlcLjIm2j7se}ZI)4;c^6mBep%L)F*vzTL)2HT+&qCLL$Lmpi z9>2fj?V`O5cZ!`&eBQ4LBrbY}p@+WXP=qjjSF67eGI*iBPBM_t%&WdG0z*)O45UE= z6?90z7@-p3C5>Q-Hq#6g$cPwn7_u-5G|}Wl$Vt&l4F0UB6dZy%n1@qv1Uzt*O8+lq zH>y7Kf0y-88PV1Pe2HEQaGcs&fHOt|GvtiH4B!EH(B?r80gh^ss;R!eXZ=Skq;|>D*vE1wmu;z)XV{( zCFGmenEP-ebI;7xQccK}w`U}*(X%ppW7fesXZ4zLK`4b<=1c6x+%`6L^m@&{DYMAj zZ)@zX%nHZ)YV3D%<*j48EG#=~WAef`YVw@LsPX2m8mf z+oX*{T6)dOZl9i#pqXEqz?Q~OFQy>>*#vf5#esO0{rPU2El#K(`8#QR(soA%_&)Mm zlh`Z0ZsmWU!~tpE#u|R3*iKMbkDVVZqawX)=gVarlAf>Q@077adRWIUzvr>NVx>$i zykiQLu8&-`)AVf`Nb9*toT=t@fl&T2_OE|>_|+-2NER=9^WRQ>o3QNN2mGxmY?p5n z_$vYFYL0hg(Zwe_ajQ+C23_O)RyTU=*Xj8<@f7fdLlizT34BF>>|x+P7Qj6X{8s|F zW`KVzzz4IyHy(yVM<_+nZQZ2c@pDx22=0~x=Yh9PQ?7T< z^M-@C%hp8h$V>d`gE(Xp|2Uq1iF-=u;U7%XMBFimi%T=;kiNMr92XxTB0pTFMrY6` z#aH<85{(sm!+8Tg&hM3Qt9>5{Z>&(%)7Waervo%ySY7Vu75hum{m commands -// Experimental support for B1 and B2 forwarding -// Experimental UI System -// Fix extracting QTH from WP updates - -// Version 1.0.0.26 - -// Add YN etc responses for FBB B1/B2 - -// Version 1.0.0.27 - -// Fix crash if NULL received as start of a packet. -// Add Save WP command -// Make B2 flag BBS-specific. -// Implement B2 Send - -// Version 1.0.0.28 - -// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net -// Flag messages as Held if smtp server rejects from or to addresses -// Fix Kill to (K> Call) -// Edit Message dialog shows latest first -// Add chat debug window to try to track down occasional chat connection problems - -// Version 1.0.0.29 - -// Add loads of try/excspt - -// Version 1.0.0.30 - -// Writes Debug output to LOG_DEBUG_X and Monitor Window - -// Version 1.0.0.32 - -// Allow use of GoogleMail for ISP functions -// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc -// Email Housekeeping Results to SYSOP - -// Version 1.0.0.33 - -// Housekeeping now runs at Maintenance Time. Maintenance Interval removed. -// Allow multiple numbers on R and K commands -// Fix L command with single number -// Log if Forward count is out of step with messages to forward. -// UI Processing improved and F< command implemented - -// Version 1.0.0.34 - -// Semaphore Chat Messages -// Display Semaphore Clashes -// More Program Error Traps -// Kill Messages more than BIDLifetime old - -// Version 1.0.0.35 - -// Test for Mike - Remove B1 check from Parse_SID - -// Version 1.0.0.36 - -// Fix calculation of Housekeeping Time. -// Set dialog box background explicitly. -// Remove tray entry for chat debug window. -// Add date to log file name. -// Add Actions Menu option to disable logging. -// Fix size of main window when it changes between versions. - -// Version 1.0.0.37 - -// Implement Paging. -// Fix L< command (was giving no messages). -// Implement LR LR mmm-nnn LR nnn- (and L nnn-) -// KM should no longer kill SYSOP bulls. -// ISP interfaces allows SMTP Auth to be configured -// SMTP Client would fail to send any more messages if a connection failed - -// Version 1.0.0.38 - -// Don't include killed messages in L commands (except LK!) -// Implement l@ -// Add forwarding timebands -// Allow resizing of main window. -// Add Ver command. - -// Version 1.0.1.1 - -// First Public Beta - -// Fix part line handling in Console -// Maintenance deletes old log files. -// Add option to delete files to the recycle bin. - -// Version 1.0.2.1 - -// Allow all Node SYSOP commands in connect scripts. -// Implement FBB B1 Protocol with Resume -// Make FBB Max Block size settable for each BBS. -// Add extra logging when Chat Sessions refused. -// Fix Crash on invalid housekeeping override. -// Add Hold Messages option. -// Trap CRT Errors -// Sort Actions/Start Forwarding List - -// Version 1.0.2.2 - -// Fill in gaps in BBS Number sequence -// Fix PE if ctext contains } -// Run Houskeeping at startup if previous Housekeeping was missed - -// Version 1.0.2.3 - -// Add configured nodes to /p listing - -// Version 1.0.2.4 - -// Fix RMS (it wanted B2 not B12) -// Send messages if available after rejecting all proposals -// Dont try to send msg back to originator. - -// Version 1.0.2.5 - -// Fix timeband processing when none specified. -// Improved Chat Help display. -// Add helpful responses to /n /q and /t - -// Version 1.0.2.6 - -// Kill Personal WP messages after processing -// Make sure a node doesnt try to "join" or "leave" a node as a user. -// More tracing to try to track down lost topic links. -// Add command recall to Console -// Show users in new topic when changing topic -// Add Send From Clipboard" Action - -// Version 1.0.2.7 - -// Hold messages from the future, or with invalid dates. -// Add KH (kill held) command. -// Send Message to SYSOP when a new user connects. - -// Version 1.0.2.8 - -// Don't reject personal message on Dup BID unless we already have an unforwarded copy. -// Hold Looping messages. -// Warn SYSOP of held messages. - -// Version 1.0.2.9 - -// Close connecton on receipt of *** DONE (MBL style forwarding). -// Improved validation in link_drop (Chat Node) -// Change to welcome prompt and Msg Header for Outpost. -// Fix Connect Script processing for KA Nodes - -// Version 1.0.3.1 - -// Fix incorrect sending of NO - BID. -// Fix problems caused by a user being connected to more than one chat node. -// Show idle time on Chat /u display. -// Rewrite forwarding by HA. -// Add "Bad Words" Test. -// Add reason for holding to SYSOP "Message Held" Message. -// Make topics case-insensitive. -// Allow SR for smtp mail. -// Try to fix some user's "Add User" problem. - - -// Version 1.0.3.2 - -// Fix program error when prcessing - response in FBB forwarding. -// Fix code to flag messages as sent. - - -// Version 1.0.3.3 - -// Attempt to fix message loop on topic_change -// Fix loop if compressed size is greater than 32K when receiving with B1 protocol. -// Fix selection of B1 - -// Version 1.0.3.4 - -// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above) -// Add Basic NNTP Interface -// Fix possible loop in lzhuf encode - -// Version 1.0.3.5 - -// Fix forwarding of Held Messages -// More attempts to fix Chat crashes. -// Limit join/leave problem with mismatched nodes. -// Add Chat Node Monitoring System. -// Change order of elements in nntp addresses (now to.at, was at.to) - -// Version 1.0.3.6 - -// Restart and Exit if too many errors -// Fix forwarding of killed messages. -// Fix Forwarding to PaKet. -// Fix problem if BBS signon contains words from the "Fail" list - -// Version 1.0.3.7 - -// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4 -// Add last message to edit users -// Change Console and Monitor Buffer sizes -// Don't flag msg as 'Y' on read if it was Held or Killed - -// Version 1.0.3.8 - -// Don't connect if all messages for a BBS are held. -// Hold message if From or To are missing. -// Fix parsing of /n and /q commands -// fix possible loop on changing name or qth - -// Version 1.0.3.9 - -// More Chat fixes and monitoring -// Added additional console for chat - -// Version 1.0.3.10 - -// Fix for corruption of CIrcuit-Node chain. - -// Version 1.0.3.11 - -// Fix flow control for SMTP and NNTP - -// Version 1.0.3.12 - -// Fix crash in SendChatStatus if no Chat Links Defined. -// Disable Chat Mode if there is no ApplCall for ChatApplNum, -// Add Edit Message to Manage Messages Dialog -// NNTP needs authentication - - -// Version 1.0.3.13 - -// Fix Chat ApplCall warning when ChatAppl = 0 -// Add NNTP NEWGROUPS Command -// Fix MBL Forwarding (remove extra > prompt after SP) - -// Version 1.0.3.14 - -// Fix topic switch code. -// Send SYSOP messages on POP3 interface if User SYSOP flag is set. -// NNTP only needs Authentication for posting, not reading. - -// Version 1.0.3.15 - -// Fix reset of First to Forward after househeeping - -// Version 1.0.3.16 - -// Fix check of HA for terminating WW -// MBL Mode remove extra > prompts -// Fix program error if WP record has unexpected format -// Connect Script changes for WINMOR -// Fix typo in unconfigured node has connected message - -// Version 1.0.3.17 - -// Fix forwarding of Personals - -// Version 1.0.3.18 - -// Fix detection of misconfigured nodes to work with new nodes. -// Limit connection attempt rate when a chat node is unavailable. -// Fix Program Error on long input lines (> ~250 chars). - -// Version 1.0.3.19 - -// Fix Restart of B2 mode transfers. -// Fix error if other end offers B1 and you are configured for B2 only. - - -// Version 1.0.3.20 - -// Fix Paging in Chat Mode. -// Report Node Versions. - -// Version 1.0.3.21 - -// Check node is not already known when processing OK -// Add option to suppress emailing of housekeeping results - -// Version 1.0.3.22 - -// Correct Version processing when user connects via the network -// Add time controlled forwarding scripts - -// Version 1.0.3.23 - -// Changes to RMS forwarding - -// Version 1.0.3.24 - -// Fix RMS: from SMTP interface -// Accept RMS/ instead of RMS: for Thunderbird - -// Version 1.0.3.25 - -// Accept smtp: addresses from smtp client, and route to ISP gateway. -// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS. - -// Version 1.0.3.26 - -// Improve display of rms and smtp messages in message lists and message display. - -// Version 1.0.3.27 - -// Correct code that prevents mail being retured to originating BBS. -// Tidy stuck Nodes and Topics when all links close -// Fix B2 handling of @ to TO Address. - -// Version 1.0.3.28 - -// Ensure user Record for the BBS Call has BBS bit set. -// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set. -// Add user configurable welcome messages. - -// Version 1.0.3.29 - -// Add AUTH feature to Rig Control - -// Version 1.0.3.30 - -// Process Paclink Header (;FW:) - -// Version 1.0.3.31 - -// Process Messages with attachments. -// Add inactivity timeout to Chat Console sessions. - -// Version 1.0.3.32 - -// Fix for Paclink > BBS Addresses - -// Version 1.0.3.33 - -// Fix multiple transfers per session for B2. -// Kill messages eent to paclink. -// Add option to forward messages on arrival. - -// Version 1.0.3.34 - -// Fix bbs addresses to winlink. -// Fix adding @winlink.org to imcoming paclink msgs - -// Version 1.0.3.35 - -// Fix bbs addresses to winlink. (Again) - -// Version 1.0.3.36 - -// Restart changes for RMS/paclink - -// Version 1.0.3.37 - -// Fix for RMS Express forwarding - -// Version 1.0.3.38 - -// Fixes for smtp and lower case packet addresses from Airmail -// Fix missing > afer NO - Bid in MBL mode - -// Version 1.0.3.39 - -// Use ;FW: for RMS polling. - -// Version 1.0.3.40 - -// Add ELSE Option to connect scripts. - -// Version 1.0.3.41 - -// Improved handling of Multiple Addresses -// Add user colours to chat. - -// Version 1.0.3.42 - -// Poll multiple SSID's for RMS -// Colour support for BPQTEerminal -// New /C chat command to toggle colour on or off. - -// Version 1.0.3.43 - -// Add SKIPPROMPT command to forward scripts - -// Version 1.0.4.1 - -// Non - Beta Release -// Fix possible crash/corruption with long B2 messages - -// Version 1.0.4.2 - -// Add @winlink.org to the B2 From addresss if it is just a callsign -// Route Flood Bulls on TO as well as @ - -// Version 1.0.4.3 - -// Handle Packet Addresses from RMS Express -// Fix for Housekeeping B$ messages - -// Version 1.0.4.4 - -// Remove B2 header and all but the Body part from messages forwared using MBL -// Fix handling of ;FW: from RMS Express - -// Version 1.0.4.5 - -// Disable Paging on forwarding sessions. -// Kill Msgs sent to RMS Exxpress -// Add Name to Chat *** Joined msg - -// Version 1.0.4.6 - -// Pass smtp:winlink.org messages from Airmail to local user check -// Only apply local user check to RMS: messages @winlink.org -// Check locally input smtp: messages for local winlink.org users -// Provide facility to allow only one connect on a port - -// Version 1.0.4.8 - -// Only reset last listed on L or LR commands. - -// Version 1.0.4.9 - -// Fix error in handling smtp: messages to winlink.org addresses from Airmail - -// Version 1.0.4.10 - -// Fix Badwords processing -// Add Connect Script PAUSE command - -// Version 1.0.4.11 - -// Suppress display and listing of held messages -// Add option to exclude SYSOP messages from LM, KM, etc -// Fix crash whan receiving messages with long lines via plain text forwarding - -// Version 1.0.4.12 Jul 2010 - -// Route P messages on AT -// Allow Applications above 8 - -// Version 1.0.4.13 Aug 2010 - -// Fix TidyString for addresses of form John Wiseman -// Add Try/Except around socket routines - -// Version 1.0.4.14 Aug 2010 - -// Trap "Error - TNC Not Ready" in forward script response -// Fix restart after program error -// Add INFO command -// Add SYSOP-configurable HELP Text. - -// Version 1.0.4.15 Aug 2010 - -// Semaphore Connect/Disconnect -// Semaphore RemoveTempBIDS - -// Version 1.0.4.16 Aug 2010 - -// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS) - -// Version 1.0.4.17 Aug 2010 - -// Fix receiving multiple messages in FBB Uncompressed Mode -// Try to trap phantom chat node connections -// Add delay to close - - -// Version 1.0.4.18 Aug 2010 - -// Add "Send SYSTEM messages to SYSOP Call" Option -// set fwd bit on local winlink.org msgs if user is a BBS -// add winlink.org to from address of messages from WL2K that don't already have an @ - -// Version 1.0.4.19 Sept 2010 - -// Build a B2 From: address if possible, so RMS Express can reply to packet messages. -// Fix handling of addresses from WL2K with SSID's -// L@ now only matches up to length of input string. -// Remove "Type H for help" from login prompt. - -// Version 1.0.4.20 Sept 2010 - -// Process FBB 'E' response -// Handle FROM addresses with an @BBS -// Fix FROM addresses with @ on end. -// Extend delay before close after sending FQ on winmor/pactor sessions. - -// Version 1.0.4.21 Sept 2010 - -// Fix handling B2 From: with an HA -// Add "Expert User" welcome message. - -// Version 1.0.4.22 Sept 2010 - -// Version 1.0.4.23 Oct 2010 - -// Add Dup message supression -// Dont change B2 from if going to RMS - -// Version 1.0.4.24 Oct 2010 - -// Add "Save Registry Config" command -// Add forwarding on wildcarded TO for NTS -// Add option to force text mode forwarding -// Define new users as a temporaty BBS if SID received in reply to Name prompt -// Reduce delay before sending close after sending FQ on pactor sessions -// Fix processing of MIME boundary from GMail - -// Send /ex instead of ctrl/z for text mode forwarding -// Send [WL2K-BPQ... SID if user flagged as RMS Express -// Fix Chat Map reporting when more than one AXIP port -// Add Message State D for NTS Messages -// Forward messages in priority order - T, P, B -// Add Reject and Hold Filters -// Fix holding messages to local RMS users when received as part of a multiple addressee message - -// Version 1.0.4.25 Nov 2010 - -// Renumbered for release -// Add option to save Registry Config during Housekeeping - -// Version 1.0.4.26 Nov 2010 - -// Fix F> loop when doing MBL forwarding between BPQ BBSes -// Allow multiple To: addresses, separated by ; -// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages. -// Set Unforwarded Bulls to status '$' -// Accept MARS and USA as continent codes for MARS Packet Addresses -// Add option to send Non-delivery notifications. - -// Version 1.0.4.27 Dec 2010 - -// Add MSGTYPES fwd file option - -// Version 1.0.4.28 Dec 2010 - -// Renumbered to for release - -// Version 1.0.4.30 Dec 2010 - -// Fix rescan requeuing where bull was rejected by a BBS -// Fiz flagging bulls received by NNTP with $ if they need to be forwarded. -// Add Chat Keepalive option. -// Fix bug in non-delivery notification. - -// Version 1.0.4.32 Jan 2011 - -// Allow "Send from Clipboard" to send to rms: or smtp: -// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS) -// Fix corruption of messages converted to B2 if body contains binary data -// Fix occasional program error when forwarding B2 messages -// Limit FBB protocol data blocks to 250 to try to fix restart problem. -// Add F2 to F5 to open windows. - -// Version 1.0.4.33 Jan 2011 - -// Fix holding old bulls with forwarding info. - -// Version 1.0.4.33 Jan 2011 - -// Prevent transfer restarting after a program error. -// Allow Housekeeping to kill held messages. - -// Version 1.0.4.35 Jan 2011 - -// Add Size limits for P and T messages to MSGTYPES command -// Fix Error in MBL processing when blank lines received (introduced in .33) -// Trap possible PE in Send_MON_Datagram -// Don't use paging on chat sessions - -// Version 1.0.4.36 Jan 2011 - -// Fix error after handling first FBB block. -// Add $X and $x welcome message options. - -// Version 1.0.4.37 Jan 2011 - -// Change L command not to list the last message if no new ones are available -// Add LC I I@ IH IZ commands -// Add option to send warning to sysop if forwarded P or T message has nowhere to go -// Fixes for Winpack Compressed Download -// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set. -// Add console copy/paste. -// Add "No Bulls" Option. -// Add "Mail For" Beacon. -// Tidied up Tab order in config dialogs to help text-to-speech programs. -// Limit MaxMsgno to 99000. - -// Version 1.0.4.38 Feb 2011 - -// Renumbered for release - -// Version 1.0.4.40 April 2011 - -// Add POLLRMS command - -// Changes for Vista/Win7 (registry key change) -// Workaround for changes to RMS Express -// Fix AUTH bug in SMTP server -// Add filter to Edit Messages dialog - -// Version 1.0.4.41 April 2011 - -// Extend B2 proposals to other BPQMail systems so Reject Filter will work. -// Add Edit User Command -// Use internal Registry Save routine instead of Regedit -// Fix Start Forward/All -// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag) -// Add FWD SYSOP command -// Fix security on POLLRMS command -// Add AUTH command -// Leave selection in same place after Delete User -// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k -// Add option to show name as well as call on Chat messages -// Fix program error if you try to define more than 80 BBS's - -// Version 1.0.4.45 October 2011 - -// Changes to program error reporting. -// BBS "Returh to Node" command added -// Move config to "Standard" location (BPQ Directory/BPQMailChat) . -// Fix crash if "Edit Message" clicked with no message selected. - -// Version 1.0.4.46 October 2011 - -// Fix BaseDir test when BaseDir ends with \ or / -// Fix long BaseDir values (>50 chars) - -// Version 1.4.47.1 January 2012 - -// Call CloseBPQ32 on exit -// Add option to flash window instead of sounding bell on Chat Connects -// Add ShowRMS SYSOP command -// Update WP with I records from R: lines -// Send WP Updates -// Fix Paclen on Pactor-like sessions -// Fix SID and Prompt when RMS Express User is set -// Try to stop loop in Program Error/Restarting code -// Trap "UNABLE TO CONNECT" response in connect script -// Add facility to print messages or save them to a text file - -// Version 1.4.48.1 January 2012 - -// Add Send Message (as well as Send from Clipboard) -// Fix Email From: Address when forwaring using B2 -// Send WP from BBSCALL not SYSOPCALL -// Send Chat Map reports via BPQ32.dll - - -// Version 1.4.49.1 February 2012 - - -// Fix Setting Paclink mode on SNOS connects -// Remove creation of debugging file for each message -// Add Message Export and Import functions -// All printing of more than one message at a time -// Add command to toggle "Expert" status - -// Version 1.4.50.1 February 2012 - -// Fix forwarding to RMS Express users -// Route messages received via B2 to an Internet email address to RMS -// Add Reverse Poll interval -// Add full FROM address to POP3 messages -// Include HOMEBBS command in Help - - -// Version 1.4.51.1 June 2012 - -// Allow bulls to be sent from RMS Express. -// Handle BASE64 and Quoted-printable encoding of single part messages -// Work round for RMS Express "All proposals rejected" Bug. - -// Version 1.4.52.1 August 2012 - -// Fix size limit on B2 To List when sending to multiple dests -// Fix initialisation of DIRMES.SYS control record -// Allow use of Tracker and UZ7HO ports for UI messages - -// Version 1.4.53.1 September 2012 - -// Fix crash if R: line with out a CR found. - -// Version 1.4.54.1 ?? 2012 - -// Add configurable prompts -// Fix KISS-Only Test -// Send EHLO instead of HELO when Authentication is needed on SMTP session -// Add option to use local tome for bbs forwarding config -// Allow comment lines (; or @) or single space in fwd scripts -// Fix loss of forwarding info if SAVE is clicked before selecting a call - -// Version 1.4.55.1 June 2013 - -// Add option to remove users that have not connected for a long time. -// Add l@ smtp: -// Fix From: sent to POP3 Client when meaages is from RMS -// Display Email From on Manage Messages - -// Version 1.4.56.1 July 2013 - -// Add timeout -// Verify prompts -// Add IDLETIME command - - - -// Version 1.4.57.1 - -// Change default IDLETIME -// Fix display of BBS's in Web "Manage Messages" -// Add separate househeeping lifetines for T messages -// Don't change flag on forwarded or delivered messages if they sre subsequently read -// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet -// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured -// Fix receiving NTS messages via B2 -// Add option to send "Mail For", but not FBB Headers -// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems -// Fix Endian bug in FBB Compression code - - -// Version 1.4.58.1 - -// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag -// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink -// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink -// Collect stats by message type -// Fix Non-Delivery notifications to SMTP messages -// Add Message Type Stats to BBS Trafic Report -// Add "Batch forward to email" -// Add EXPORT command -// Allow more BBS records -// Allow lower case connect scripts -// Fix POP3 LIST command -// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding -// Fix duplicates of SP SYSOP@WW Messages -// Add command line option (tidymail) to delete redundant Mail files -// Add command line option (nohomebbs) to suppress HomeBBS prompt - -// 59 April 2014 - -// Add FLARQ Mail Mode -// Fix possible crash saving restart data -// Add script command ADDLF for connect scripts over Telnet -// Add recogniton of URONODE connected message -// Add option to stop Name prompt -// Add new RMS Express users with "RMS Express User" flag set -// Validate HTML Pages -// Add NTS swap file -// Add basic File list and read functions -// Fix Traffic report - -// 60 - -// Fix security hole in readfile - -// 61 August 2014 -// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS -// Dont treat "Attempting downlink" as a failure -// Add option to read messages during a list -// Fix crash during message renumber on MAC -// Timeout response to SID to try to avoid hang on an incomplete connection. -// Save config in file instead of registry -// Fix Manage Messages "EXPORT" option and check filename on EXPORT command -// Fix reverse forward prompt in MBL mode. -// Fix From address in POP3 messages where path is @winlink.org -// Fix possible program error in T message procesing -// Add MaxAge param (for incoming Bulls) - - -//62 November 2014 -// Add ZIP and Permit Bulls flag to Manage Users -// Allow users to kill their own B and anyone to kill T messages -// Improve saving of "Last Listed" -// Fix LL when paging -// Send Date received in R: Line (should fix B2 message restarts) -// Fix occasional crash in terminal part line processing -// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT -// Fix possible retry loop when message is deferred (FBB '=' response); -// Don't remove Attachments from received bulls. - -//63 Feb 2015 - -// Fix creating Bulls from RMS Express messages. -// Fix PE if message with no To: received. -// Fix setting "RMS Express User" flag on new connects from RMS Express -// Fix deleting 'T' messages downloaded by RMS Express -// Include MPS messages in count of messages to forward. -// Add new Welcome Message variable $F for messages to forward -// Fix setting Type in B2 header when usong NTS: or BULL: -// Remove trailing spaces from BID when Creating Message from Clipboard. -// Improved handling of FBB B1/B2 Restarts. - -//64 September 2015 - -// Fix Message Type in msgs from RMS Express to Internet -// Reopen Monitor window if open when program list closed -// Only apply NTS alias file to NTS Messages -// Fix failure to store some encrypted ISP passwords -// Allow EDITUSER to change "RMS Express User" flag -// Fix reporting of Config File errors -// Fix Finding MPS Messages (First to Forward was being used incorrectly) -// Add "Save Attachment" to Web Mgmt Interface -// Support Secure Signon on Forwarding sessions to CMS -// Save Forwarding config when BBS flag on user is cleared -// Pass internally generated SYSOP messages through routing process -// Add POP3 TOP command. -// Don't set 'T' messages to 'Y' when read. -// Add optional temporary connect script on "FWD NOW" command -// Add automatic import facility -// Accept RMS mail to BBS Call even if "Poll RMS" not set. - -// 65 November 2015 - -// Fix loading Housekeeping value for forwarded bulls. -// Fix re-using Fwd script override in timer driven forwarding. -// Add ampr.org handling -// Add "Dont forward" match on TO address for NTS -// Allow listing a combinatiom of state and type, such as LNT or LPF -// Fix handling ISP messages from gmail without a '+' -// Add basic WebMail support - -// 66 - -// Autoimport messages as Dummy Call, not SYSOP Call -// Add "My Messages" display option to WebMail -// Create .csv extract of User List during hourekeeping. -// Fix processing of NTS Alising of @ Addresses -// Don't reroute Delivered NTS Messages -// Add option to stop users killing T messages -// Add multicast Receive -// Fix initialising new message database format field -// Fix "Forward Messages to BBS Call" option. -// Add Filter WP Bulls option and allow multiple WP "TO" addresses -// Fix deleting P WP messages for other stations -// Fix saving blank lines in forwarding config -// Fix paging on L@ and l< -// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script -// Run DeleteRedundantMessages before renumbering messages -// Connect script now tries ELSE lines if prompt not received from remote BBS -// Send connecting call instead of BBS Name when connecting to CMS server. -// Add BID filter to Manage Messages -// Fix handling of over long suject lines in IMPORT -// Allow comments before ELSE in connect script -// Add Copy and Clear to Multicast Window -// Fix possible duplicate messages with MBL forwarding -// Set "Permit EMail" on IMPORT dummy User. -// Fix repeated running of housekeeping if clock is stepped forward. -// Fix corruption of CMS Pass field by Web interface -// Kill B2 WP bulls if FilterWPBulls set -// Include Message Type in BPQ B2 proposal extensions - -// 6.0.14.1 July 2017 - -// Fix corruption of BBSNumber if RMS Ex User and BBS both checked -// Tread B messages without an AT as Flood. -// Make sure Message headers are always saved to disk when a message status changes -// Reject message instead of failing session if TO address too long in FBB forwarding -// Fix error when FBB restart data exactly fills a packet. -// Fix possible generation of msg number zero in send nondlivery notification -// Fix problem with Web "Manage Messages" when stray message number zero appears -// Fix Crash in AMPR forward when host missing from VIA -// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS -// Fix test for Status "D" in forward check. -// Don't cancel AUTH on SMTP RSET -// Fix "nowhere to go" message on some messages sent to smtp addresses -// Add @ from Home BBS or WP is not spcified in "Send from Clipboard" - -// 6.0.15.1 Feb 2018 - -// Fix PE if Filename missing from FILE connect script command -// Suppress reporting errors after receiving FQ -// Fix problem caused by trailing spaces on callsign in WP database -// Support mixed case WINLINK Passwords - -// 6.0.16.1 March 2018 - -// Make sure messages sent to WL2K don;'t have @ on from: address -// If message to saildocs add R: line as an X header instead of to body -// Close session if more than 4 Invalid Commmad responses sent -// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client - -// 6.0.17.1 November 2018 - -// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq -// Accept an internet email address without rms: or smtp: -// Fix "Forward messages for BBS Call" when TO isn't BBS Call -// Accept NNTP commands in either case -// Add NNTP BODY command -// Timeout POP or SMTP TCP connections that are open too long -// Add YAPP support -// Fix connect script when Node CTEXT contains "} BBS " -// Fix handling null H Route -// Detect and correct duplicate BBS Numbers -// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B) -// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages -// Add support for Winlink HTML Forms to WebMail interface -// Update B2 header when using NTS alias file with B2 messages - -// 6.0.18.1 January 2019 - -// Ensure callsigns in WP database are upper case. -// Various fixes for Webmail -// Fix sending direct to ampr.org addresses -// Use SYSOP Call as default for Webmail if set -// Preparations for 64 bit version - - -// 6.0.19.1 September 2019 - -// Trap missing HTML reply Template or HTML files -// Fix case problems in HTML Templates -// Fix setting To call on reply to HTML messages -// More preparations for 64 bit including saving WP info as a text file. -// Set "RMS Express User" when a new user connects using PAT -// Increace maximum length on Forwarding Alias string in Web interface -// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS -// Fix program error if READ used without a filename -// Trap reject messages from Winlink CMS -// Fix "delete to recycle bin" on Linux -// Handle Radio Only Messages (-T or -R suffix on calling station) -// Fix program error on saving empty Alias list on Web Forwarding page -// Add REQDIR and REQFIL -// Experimental Blocked Uncompressed forwarding -// Security fix for YAPP -// Fix WebMail Cancel Send Message -// Fix processing Hold Message response from Winlink Express - -// 6.0.20.1 April 2020 - -// Improvments to YAPP -// Add Copy forwarding config -// Add Next and Previous buttons to Webmail message read screen -// Move HTML templates from HTMLPages to inline code. -// Fix Paclen on YAPP send -// Fix bug in handling "RMS Express User" -// Fix WINPACK compressed forwarding -// Add option to send P messages to more than one BBS -// Add "Default to Don't Add WINLINK.ORG" Config option -// Re-read Badwords.sys during Housekeeping -// Add BID Hold and Reject Filters -// On SMTP Send try HELO if EHLO rejected -// Allow SID response timeout to be configured per BBS -// Fix sending bulls with PAT -// Set "Forward Messages to BBS Call" when routing Bulls on TO -// Add option to send Mail For Message to APRS -// Fix WP update -// Fix Holding messages from Webmail Interface -// Add RMR command -// Add REROUTEMSGS BBS SYSOP command -// Disable null passwords and check Exclude flag in Webmail Signin -// Add basic Webmail logging - -// 6.0.21.1 December 2020 - -// Remove nulls from displayed messages. -// Fix Holding messages from SMTP and POP3 Interfaces -// Various fixes for handling messages to/from Internet email addresses -// Fix saving Email From field in Manage Messages -// Fix sending WL2K traffic reports via TriMode. -// Fix removing successive CR from Webmail Message display -// Fix Wildcarded @ forwarding -// Fix message type when receiving NTS Msgs form Airmail -// Fix address on SERVICE messages from Winlink -// Add multiple TO processing to Webmail non-template messages -// Don't backup config file if reading it fails -// Include Port and Freq on Connected log record -// Make sure welcome mesages don't end in > -// Allow flagging unread T messages as Delivered -// Replace \ with # in forward script so commands starting with # can be sent -// Fix forwarding NTS on TO field -// Fix possible crash in text mode forwarding -// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured -// Add DOHOUSEKEEPING sysop command -// Add MARS continent code -// Try to trap 'zombie' BBS Sessions -// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory. -// Fix corruption of message length when reading R2 message via Read command -// Fix paging on List command and add new combinations of List options -// Fix NNTP list and LC command when bulls are killed - -// 6.0.22.1 August 2021 - -// Fix flagging messages with attachments as read. -// Fix possible corruption of WP database and subsequent crash on reloading. -// Fix format of Web Manage Messages display -// Include SETNEXTMESSAGENUMBER in SYSOP Help Message -// Fix occasional "Incoming Connect from SWITCH" -// Fix L> with numeric dests -// Improved diagnostic for MailTCP select() error. -// Clear "RMS Express User" if user is changed to a BBS -// Fix saving Window positions on exit -// Fix parsing ReplyTemplate name in Webmail -// Handle multiple addressees for WebMail Forms messages to packet stations -// Add option to allow only known users to connect -// Add basic callsign validation to From address -// Add option to forward a user's messages to Winlink -// Move User config to main config file. -// Update message status whne reading a Forms Webmail message -// Speed up killing multiple messages -// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command - -// 6.0.23.1 June 2022 - -// Fix crash when ; added to call in send commands -// Allow smtp/ override for messages from RMS Express to send via ISP gateway -// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured -// Recompiled for Web Interface changes in Node -// Add RMS Relay SYNC Mode (.17) -// Add Protocol changes for Relay RO forwarding -// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay -// Fix B2 Header Date in Webmail message with sttachments. -// Fix bug when using YAPP with VARA (.27) -// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command -// Add mechsnism to send bbs log records to qttermtcp. (32) -// Add MFJ forwarding Mode (No @BBS on send) -// Fix handling CR/LF split over packet boundaries -// Add Header and Footers for Webmail Send (42) -// Fix Maintenance Interval in LinBPQ (53) -// Add RMS: to valid from addresses (.56) -// Fix Web management on Android deviced (.58) -// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70) -// Check Badword and Reject filters before processing WP Messages - -// 6.0.24.1 ?? 2022 - -// Fix ' in Webmail subject (8) -// Change web buttons to white on black when pressed (10) -// 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 inm 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) - - -#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; - -BOOL WINE = FALSE; - -INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - -VOID SetupNTSAliases(char * FN); - -HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default -char * REGTREETEXT = "HKEY_LOCAL_MACHINE"; - -// Global Variables: -HINSTANCE hInst; // current instance -TCHAR szTitle[MAX_LOADSTRING]; // The title bar text -TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name - -extern int LastVer[4]; // In case we need to do somthing the first time a version is run - -UINT BPQMsg; - -HWND MainWnd; -HWND hWndSess; -RECT MainRect; -HMENU hActionMenu; -static HMENU hMenu; -HMENU hDisMenu; // Disconnect Menu Handle -HMENU hFWDMenu; // Forward Menu Handle - -int SessX, SessY, SessWidth; // Params for Session Window - -char szBuff[80]; - -#define MaxSockets 64 - -int _MYTIMEZONE = 0; - -ConnectionInfo Connections[MaxSockets+1]; - -//struct SEM AllocSemaphore = {0, 0}; -//struct SEM ConSemaphore = {0, 0}; -//struct SEM OutputSEM = {0, 0}; - -//struct UserInfo ** UserRecPtr=NULL; -//int NumberofUsers=0; - -//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes - -//struct MsgInfo ** MsgHddrPtr=NULL; -//int NumberofMessages=0; - -//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search - -//BIDRec ** BIDRecPtr=NULL; -//int NumberofBIDs=0; - -extern BIDRec ** TempBIDRecPtr; -//int NumberofTempBIDs=0; - -//WPRec ** WPRecPtr=NULL; -//int NumberofWPrecs=0; - -extern char ** BadWords; -//int NumberofBadWords=0; -extern char * BadFile; - -//int LatestMsg = 0; -//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg -//int HighestBBSNumber = 0; - -//int MaxMsgno = 60000; -//int BidLifetime = 60; -//int MaintInterval = 24; -//int MaintTime = 0; -//int UserLifetime = 0; - - -BOOL cfgMinToTray; - -BOOL DisconnectOnClose; - -extern char PasswordMsg[100]; - -char cfgHOSTPROMPT[100]; - -char cfgCTEXT[100]; - -char cfgLOCALECHO[100]; - -char AttemptsMsg[]; -char disMsg[]; - -char LoginMsg[]; - -char BlankCall[]; - - -ULONG BBSApplMask; -ULONG ChatApplMask; - -int BBSApplNum; - -//int StartStream=0; -int NumberofStreams; -int MaxStreams; - -extern char BBSSID[]; -extern char ChatSID[]; - -extern char NewUserPrompt[100]; - -extern char * WelcomeMsg; -extern char * NewWelcomeMsg; -extern char * ExpertWelcomeMsg; - -extern char * Prompt; -extern char * NewPrompt; -extern char * ExpertPrompt; - -extern BOOL DontNeedHomeBBS; - -char BBSName[100]; -char MailForText[100]; - -char SignoffMsg[100]; - -char AbortedMsg[100]; - -extern char UserDatabaseName[MAX_PATH]; -extern char UserDatabasePath[MAX_PATH]; - -extern char MsgDatabasePath[MAX_PATH]; -extern char MsgDatabaseName[MAX_PATH]; - -extern char BIDDatabasePath[MAX_PATH]; -extern char BIDDatabaseName[MAX_PATH]; - -extern char WPDatabasePath[MAX_PATH]; -extern char WPDatabaseName[MAX_PATH]; - -extern char BadWordsPath[MAX_PATH]; -extern char BadWordsName[MAX_PATH]; - -char NTSAliasesPath[MAX_PATH]; -extern char NTSAliasesName[MAX_PATH]; - -char BaseDir[MAX_PATH]; -char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% - -char MailDir[MAX_PATH]; - -char RlineVer[50]; - -extern BOOL KISSOnly; - -extern BOOL OpenMon; - -extern struct ALIAS ** NTSAliases; - -extern int EnableUI; -extern int RefuseBulls; -extern int SendSYStoSYSOPCall; -extern int SendBBStoSYSOPCall; -extern int DontHoldNewUsers; -extern int ForwardToMe; - -extern int MailForInterval; - -char zeros[NBMASK]; // For forward bitmask tests - -time_t MaintClock; // Time to run housekeeping - -struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. - -// Filter Params - -char ** RejFrom; // Reject on FROM Call -char ** RejTo; // Reject on TO Call -char ** RejAt; // Reject on AT Call -char ** RejBID; // Reject on BID - -char ** HoldFrom; // Hold on FROM Call -char ** HoldTo; // Hold on TO Call -char ** HoldAt; // Hold on AT Call -char ** HoldBID; // Hold on BID - - -// Send WP Params - -BOOL SendWP; -char SendWPVIA[81]; -char SendWPTO[11]; -int SendWPType; - - -int ProgramErrors = 0; - -UCHAR BPQDirectory[260] = ""; - - -// Forward declarations of functions included in this code module: -ATOM MyRegisterClass(HINSTANCE hInstance); -ATOM RegisterMainWindowClass(HINSTANCE hInstance); -BOOL InitInstance(HINSTANCE, int); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); -INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - -unsigned long _beginthread( void( *start_address )(VOID * DParam), - unsigned stack_size, VOID * DParam); - -VOID SendMailForThread(VOID * Param); -BOOL CreatePipeThread(); -int DeleteRedundantMessages(); -VOID BBSSlowTimer(); -VOID CopyConfigFile(char * ConfigName); -BOOL CreateMulticastConsole(); -char * CheckToAddress(CIRCUIT * conn, char * Addr); -BOOL CheckifPacket(char * Via); -int GetHTMLForms(); - -struct _EXCEPTION_POINTERS exinfox; - -CONTEXT ContextRecord; -EXCEPTION_RECORD ExceptionRecord; - -DWORD Stack[16]; - -BOOL Restarting = FALSE; - -Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg) -{ - unsigned int SPPtr; - unsigned int SPVal; - - memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord)); - memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord)); - - SPPtr = ContextRecord.Esp; - - Debugprintf("BPQMail *** Program Error %x at %x in %s", - ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg); - - - __asm{ - - mov eax, SPPtr - mov SPVal,eax - lea edi,Stack - mov esi,eax - mov ecx,64 - rep movsb - - } - - Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", - ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx, - ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal); - - Debugprintf("Stack:"); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); - -} - - - -void myInvalidParameterHandler(const wchar_t* expression, - const wchar_t* function, - const wchar_t* file, - unsigned int line, - uintptr_t pReserved) -{ - Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); - - if (expression && function && file) - { - Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression); - Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function); - Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line); - } -} - -// If program gets too many program errors, it will restart itself and shut down - -VOID CheckProgramErrors() -{ - STARTUPINFO SInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION - char ProgName[256]; - - if (Restarting) - exit(0); // Make sure can't loop in restarting - - ProgramErrors++; - - if (ProgramErrors > 25) - { - Restarting = TRUE; - - Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); - - if (cfgMinToTray) - { - DeleteTrayMenuItem(MainWnd); - if (ConsHeader[0]->hConsole) - DeleteTrayMenuItem(ConsHeader[0]->hConsole); - if (ConsHeader[1]->hConsole) - DeleteTrayMenuItem(ConsHeader[1]->hConsole); - if (hMonitor) - DeleteTrayMenuItem(hMonitor); - } - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - GetModuleFileName(NULL, ProgName, 256); - - Debugprintf("Attempting to Restart %s", ProgName); - - CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); - - exit(0); - } -} - - -VOID WriteMiniDump() -{ -#ifdef WIN32 - - HANDLE hFile; - BOOL ret; - char FN[256]; - - sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); - - hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) - { - // Create the minidump - - ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), - hFile, MiniDumpNormal, 0, 0, 0 ); - - if(!ret) - Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); - else - Debugprintf("Minidump %s created.", FN); - CloseHandle(hFile); - } -#endif -} - - -void GetSemaphore(struct SEM * Semaphore, int ID) -{ - // - // Wait for it to be free - // -#ifdef WIN32 - if (Semaphore->Flag != 0) - { - Semaphore->Clashes++; - } -loop1: - - while (Semaphore->Flag != 0) - { - Sleep(10); - } - - // - // try to get semaphore - // - - _asm{ - - mov eax,1 - mov ebx, Semaphore - xchg [ebx],eax // this instruction is locked - - cmp eax,0 - jne loop1 // someone else got it - try again -; -; ok, weve got the semaphore -; - } -#else - - while (Semaphore->Flag) - usleep(10000); - - Semaphore->Flag = 1; - -#endif - return; -} - -void FreeSemaphore(struct SEM * Semaphore) -{ - Semaphore->Flag = 0; - - return; -} - -char * CmdLine; - -extern int configSaved; - -int APIENTRY WinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPTSTR lpCmdLine, - int nCmdShow) -{ - MSG msg; - HACCEL hAccelTable; - int BPQStream, n; - struct UserInfo * user; - struct _EXCEPTION_POINTERS exinfo; - _invalid_parameter_handler oldHandler, newHandler; - char Msg[100]; - int i = 60; - struct NNTPRec * NNTPREC; - struct NNTPRec * SaveNNTPREC; - - CmdLine = _strdup(lpCmdLine); - _strlwr(CmdLine); - - if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs - { - hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0, - CW_USEDEFAULT, 100, 550, 70, - NULL, NULL, hInstance, NULL); - - ShowWindow(hWnd, nCmdShow); - - while (i-- > 0) - { - sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i); - SetWindowText(hWnd, Msg); - - Sleep(1000); - } - - DestroyWindow(hWnd); - } - - __try { - - // Trap CRT Errors - - newHandler = myInvalidParameterHandler; - oldHandler = _set_invalid_parameter_handler(newHandler); - - // Initialize global strings - LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); - LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING); - MyRegisterClass(hInstance); - - // Perform application initialization: - - if (!InitInstance (hInstance, nCmdShow)) - { - return FALSE; - } - - hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat)); - - // Main message loop: - - Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting"); - Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting"); - Debugprintf("BPQMail Starting"); - - if (pDllBPQTRACE == 0) - Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable"); - - - } My__except_Routine("Init"); - - while (GetMessage(&msg, NULL, 0, 0)) - { - __try - { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - #define EXCEPTMSG "GetMessageLoop" - #include "StdExcept.c" - - CheckProgramErrors(); - } - } - - __try - { - for (n = 0; n < NumberofStreams; n++) - { - BPQStream=Connections[n].BPQStream; - - if (BPQStream) - { - SetAppl(BPQStream, 0, 0); - Disconnect(BPQStream); - DeallocateStream(BPQStream); - } - } - - - hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0, - 150, 200, 350, 40, NULL, NULL, hInstance, NULL); - - ShowWindow(hWnd, nCmdShow); - - Sleep(1000); // A bit of time for links to close - - DestroyWindow(hWnd); - - if (ConsHeader[0]->hConsole) - DestroyWindow(ConsHeader[0]->hConsole); - if (ConsHeader[1]->hConsole) - DestroyWindow(ConsHeader[1]->hConsole); - if (hMonitor) - { - DestroyWindow(hMonitor); - hMonitor = (HWND)1; // For status Save - } - - -// SaveUserDatabase(); - SaveMessageDatabase(); - SaveBIDDatabase(); - - configSaved = 1; - SaveConfig(ConfigName); - - if (cfgMinToTray) - { - DeleteTrayMenuItem(MainWnd); - if (ConsHeader[0]->hConsole) - DeleteTrayMenuItem(ConsHeader[0]->hConsole); - if (ConsHeader[1]->hConsole) - DeleteTrayMenuItem(ConsHeader[1]->hConsole); - if (hMonitor) - DeleteTrayMenuItem(hMonitor); - } - - // Free all allocated memory - - for (n = 0; n <= NumberofUsers; n++) - { - user = UserRecPtr[n]; - - if (user->ForwardingInfo) - { - FreeForwardingStruct(user); - free(user->ForwardingInfo); - } - - free(user->Temp); - - free(user); - } - - free(UserRecPtr); - - for (n = 0; n <= NumberofMessages; n++) - free(MsgHddrPtr[n]); - - free(MsgHddrPtr); - - for (n = 0; n <= NumberofWPrecs; n++) - free(WPRecPtr[n]); - - free(WPRecPtr); - - for (n = 0; n <= NumberofBIDs; n++) - free(BIDRecPtr[n]); - - free(BIDRecPtr); - - if (TempBIDRecPtr) - free(TempBIDRecPtr); - - NNTPREC = FirstNNTPRec; - - while (NNTPREC) - { - SaveNNTPREC = NNTPREC->Next; - free(NNTPREC); - NNTPREC = SaveNNTPREC; - } - - if (BadWords) free(BadWords); - if (BadFile) free(BadFile); - - n = 0; - - if (Aliases) - { - while(Aliases[n]) - { - free(Aliases[n]->Dest); - free(Aliases[n]); - n++; - } - - free(Aliases); - FreeList(AliasText); - } - - n = 0; - - if (NTSAliases) - { - while(NTSAliases[n]) - { - free(NTSAliases[n]->Dest); - free(NTSAliases[n]); - n++; - } - - free(NTSAliases); - } - - FreeOverrides(); - - FreeList(RejFrom); - FreeList(RejTo); - FreeList(RejAt); - FreeList(RejBID); - FreeList(HoldFrom); - FreeList(HoldTo); - FreeList(HoldAt); - FreeList(HoldBID); - FreeList(SendWPAddrs); - - Free_UI(); - - for (n=1; n<20; n++) - { - if (MyElements[n]) free(MyElements[n]); - } - - free(WelcomeMsg); - free(NewWelcomeMsg); - free(ExpertWelcomeMsg); - - free(Prompt); - free(NewPrompt); - free(ExpertPrompt); - - FreeWebMailMallocs(); - - free(CmdLine); - - _CrtDumpMemoryLeaks(); - - } - My__except_Routine("Close Processing"); - - CloseBPQ32(); // Close Ext Drivers if last bpq32 process - - return (int) msg.wParam; -} - - - -// -// FUNCTION: MyRegisterClass() -// -// PURPOSE: Registers the window class. -// -// COMMENTS: -// -// This function and its usage are only necessary if you want this code -// to be compatible with Win32 systems prior to the 'RegisterClassEx' -// function that was added to Windows 95. It is important to call this function -// so that the application will get 'well formed' small icons associated -// with it. -// -// -#define BGCOLOUR RGB(236,233,216) -//#define BGCOLOUR RGB(245,245,245) - -HBRUSH bgBrush; - -ATOM MyRegisterClass(HINSTANCE hInstance) -{ - WNDCLASSEX wcex; - - bgBrush = CreateSolidBrush(BGCOLOUR); - - wcex.cbSize = sizeof(WNDCLASSEX); - - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = DLGWINDOWEXTRA; - wcex.hInstance = hInstance; - wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON)); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = bgBrush; - wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat); - wcex.lpszClassName = szWindowClass; - wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON)); - - return RegisterClassEx(&wcex); -} - - -// -// FUNCTION: InitInstance(HINSTANCE, int) -// -// PURPOSE: Saves instance handle and creates main window -// -// COMMENTS: -// -// In this function, we save the instance handle in a global variable and -// create and display the main program window. -// - -HWND hWnd; - -int AXIPPort = 0; - -char LOC[7] = ""; - -BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) -{ - char Title[80]; - WSADATA WsaData; - HMENU hTopMenu; // handle of menu - HKEY hKey=0; - int retCode; - RECT InitRect; - RECT SessRect; - struct _EXCEPTION_POINTERS exinfo; - - HMODULE ExtDriver = LoadLibrary("bpq32.dll"); - - if (ExtDriver) - { - pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8"); - pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0"); - pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0"); - pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8"); - - if (pGetLOC) - { - char * pLOC = (char *)pGetLOC(); - memcpy(LOC, pLOC, 6); - } - } - - // See if running under WINE - - retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); - - if (retCode == ERROR_SUCCESS) - { - RegCloseKey(hKey); - WINE =TRUE; - Debugprintf("Running under WINE"); - } - - - REGTREE = GetRegistryKey(); - REGTREETEXT = GetRegistryKeyText(); - - Sleep(1000); - - { - int n; - struct _EXTPORTDATA * PORTVEC; - - KISSOnly = TRUE; - - for (n=1; n <= GetNumberofPorts(); n++) - { - PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n); - - if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL - { - if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0) - KISSOnly = FALSE; - - if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR - KISSOnly = FALSE; - - if (AXIPPort == 0) - { - if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0) - { - AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER; - KISSOnly = FALSE; - } - } - } - } - } - - hInst = hInstance; - - hWnd=CreateDialog(hInst,szWindowClass,0,NULL); - - if (!hWnd) - { - return FALSE; - } - - MainWnd = hWnd; - - GetVersionInfo(NULL); - - sprintf(Title,"G8BPQ Mail Server Version %s", VersionString); - - sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]); - - SetWindowText(hWnd,Title); - - hWndSess = GetDlgItem(hWnd, 100); - - GetWindowRect(hWnd, &InitRect); - GetWindowRect(hWndSess, &SessRect); - - SessX = SessRect.left - InitRect.left ; - SessY = SessRect.top -InitRect.top; - SessWidth = SessRect.right - SessRect.left; - - // Get handles for updating menu items - - hTopMenu=GetMenu(MainWnd); - hActionMenu=GetSubMenu(hTopMenu,0); - - hFWDMenu=GetSubMenu(hActionMenu,0); - hMenu=GetSubMenu(hActionMenu,1); - hDisMenu=GetSubMenu(hActionMenu,2); - - CheckTimer(); - - cfgMinToTray = GetMinimizetoTrayFlag(); - - if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) - if (cfgMinToTray) - { - ShowWindow(hWnd, SW_HIDE); - } - else - { - ShowWindow(hWnd, nCmdShow); - } - else - ShowWindow(hWnd, nCmdShow); - - UpdateWindow(hWnd); - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - __try { - - return Initialise(); - - }My__except_Routine("Initialise"); - - return FALSE; -} - -// -// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) -// -// PURPOSE: Processes messages for the main window. -// -// - - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - int state,change; - ConnectionInfo * conn; - struct _EXCEPTION_POINTERS exinfo; - - - if (message == BPQMsg) - { - if (lParam & BPQMonitorAvail) - { - __try - { - DoBBSMonitorData(wParam); - } - My__except_Routine("DoMonitorData"); - - return 0; - - } - if (lParam & BPQDataAvail) - { - // Dont trap error at this level - let Node error handler pick it up -// __try -// { - DoReceivedData(wParam); -// } -// My__except_Routine("DoReceivedData") - return 0; - } - if (lParam & BPQStateChange) - { - // Get current Session State. Any state changed is ACK'ed - // automatically. See BPQHOST functions 4 and 5. - - __try - { - SessionState(wParam, &state, &change); - - if (change == 1) - { - if (state == 1) // Connected - { - GetSemaphore(&ConSemaphore, 0); - __try {Connected(wParam);} - My__except_Routine("Connected"); - FreeSemaphore(&ConSemaphore); - } - else - { - GetSemaphore(&ConSemaphore, 0); - __try{Disconnected(wParam);} - My__except_Routine("Disconnected"); - FreeSemaphore(&ConSemaphore); - } - } - } - My__except_Routine("DoStateChange"); - - } - - return 0; - } - - - switch (message) - { - - case WM_KEYUP: - - switch (wParam) - { - case VK_F2: - CreateConsole(-1); - return 0; - - case VK_F3: - CreateMulticastConsole(); - return 0; - - case VK_F4: - CreateMonitor(); - return 0; - - case VK_TAB: - return TRUE; - - break; - - - - } - return 0; - - case WM_TIMER: - - if (wParam == 1) // Slow = 10 secs - { - __try - { - time_t NOW = time(NULL); - struct tm * tm; - RefreshMainWindow(); - CheckTimer(); - TCPTimer(); - BBSSlowTimer(); - FWDTimerProc(); - if (MaintClock < NOW) - { - while (MaintClock < NOW) // in case large time step - MaintClock += MaintInterval * 3600; - - Debugprintf("|Enter HouseKeeping"); - DoHouseKeeping(FALSE); - } - tm = gmtime(&NOW); - - if (tm->tm_wday == 0) // Sunday - { - if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) - { - CreateBBSTrafficReport(); - LastTrafficTime = NOW; - } - } - } - My__except_Routine("Slow Timer"); - } - else - __try - { - TrytoSend(); - TCPFastTimer(); - } - My__except_Routine("TrytoSend"); - - return (0); - - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - case WM_CTLCOLORSTATIC: - { - HDC hdcStatic = (HDC)wParam; - SetTextColor(hdcStatic, RGB(0, 0, 0)); - SetBkMode(hdcStatic, TRANSPARENT); - return (LONG)bgBrush; - } - - case WM_INITMENUPOPUP: - - if (wParam == (WPARAM)hActionMenu) - { - if (IsClipboardFormatAvailable(CF_TEXT)) - EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED); - else - EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED ); - - return TRUE; - } - - if (wParam == (WPARAM)hFWDMenu) - { - // Set up Forward Menu - - struct UserInfo * user; - char MenuLine[30]; - - for (user = BBSChain; user; user = user->BBSNext) - { - sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user)); - - if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, - MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0) - - AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine); - } - return TRUE; - } - - if (wParam == (WPARAM)hDisMenu) - { - // Set up Disconnect Menu - - CIRCUIT * conn; - char MenuLine[30]; - int n; - - for (n = 0; n <= NumberofStreams-1; n++) - { - conn=&Connections[n]; - - RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND); - - if (conn->Active) - { - sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); - AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine); - } - } - return TRUE; - } - break; - - - case WM_COMMAND: - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - // Parse the menu selections: - - if (wmEvent == LBN_DBLCLK) - - break; - - if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) - { - // disconnect user - - conn=&Connections[wmId-IDM_DISCONNECT]; - - if (conn->Active) - { - Disconnect(conn->BPQStream); - } - } - - if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100) - { - StartForwarding(wmId - IDM_FORWARD_ALL, NULL); - return 0; - } - - switch (wmId) - { - case IDM_LOGBBS: - - ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS); - break; - - case IDM_LOGCHAT: - - ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); - break; - - case IDM_LOGTCP: - - ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP); - break; - - case IDM_HOUSEKEEPING: - - DoHouseKeeping(TRUE); - - break; - - case IDM_CONSOLE: - - CreateConsole(-1); - break; - - case IDM_MCMONITOR: - - CreateMulticastConsole(); - break; - - case IDM_MONITOR: - - CreateMonitor(); - break; - - case RESCANMSGS: - - ReRouteMessages(); - break; - - case IDM_IMPORT: - - ImportMessages(NULL, "", FALSE); - break; - - case IDM_ABOUT: - DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); - break; - - case ID_HELP_ONLINEHELP: - - ShellExecute(hWnd,"open", - "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html", - "", NULL, SW_SHOWNORMAL); - - break; - - case IDM_CONFIG: - DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc); - break; - - case IDM_USERS: - DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc); - break; - - case IDM_FWD: - DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc); - break; - - case IDM_MESSAGES: - DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc); - break; - - case IDM_WP: - DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc); - break; - - case ID_ACTIONS_SENDMSGFROMCLIPBOARD: - DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc); - break; - - case ID_ACTIONS_SENDMESSAGE: - DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc); - break; - - case ID_MULTICAST: - - MulticastRX = !MulticastRX; - CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED); - break; - - case IDM_EXIT: - DestroyWindow(hWnd); - break; - - - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - break; - - case WM_SIZE: - - if (wParam == SIZE_MINIMIZED) - if (cfgMinToTray) - return ShowWindow(hWnd, SW_HIDE); - - return (0); - - - case WM_SIZING: - { - LPRECT lprc = (LPRECT) lParam; - int Height = lprc->bottom-lprc->top; - int Width = lprc->right-lprc->left; - - MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE); - - return TRUE; - } - - - case WM_PAINT: - hdc = BeginPaint(hWnd, &ps); - // TODO: Add any drawing code here... - EndPaint(hWnd, &ps); - break; - - case WM_DESTROY: - - GetWindowRect(MainWnd, &MainRect); // For save soutine - if (ConsHeader[0]->hConsole) - GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine - if (ConsHeader[1]->hConsole) - GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine - if (hMonitor) - GetWindowRect(hMonitor, &MonitorRect); // For save soutine - - KillTimer(hWnd,1); - KillTimer(hWnd,2); - PostQuitMessage(0); - break; - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - return 0; -} - -INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_INITDIALOG: - - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); - - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); - - return TRUE; - - case WM_SIZING: - { - HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1); - - LPRECT lprc = (LPRECT) lParam; - int Height = lprc->bottom-lprc->top; - int Width = lprc->right-lprc->left; - - MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE); - - return TRUE; - } - - case WM_COMMAND: - - if (LOWORD(wParam) == IDSEND) - { - char status [3]; - struct MsgInfo * Msg; - char * via = NULL; - char BID[13]; - char FileList[32768]; - BIDRec * BIDRec; - int MsgLen; - char * MailBuffer; - char MsgFile[MAX_PATH]; - HANDLE hFile = INVALID_HANDLE_VALUE; - int WriteLen=0; - char HDest[61]; - char Destcopy[61]; - char * Vptr; - char * FileName[100]; - int FileLen[100]; - char * FileBody[100]; - int n, Files = 0; - int TotalFileSize = 0; - char * NewMsg; - - GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60); - strcpy(Destcopy, HDest); - - GetDlgItemText(hDlg, IDC_MSGBID, BID, 13); - strlop(BID, ' '); - - GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767); - - // if there are attachments, check that they can be opened ane read - - n = 0; - - if (FileList[0]) - { - FILE * Handle; - struct stat STAT; - char * ptr1 = FileList, * ptr2; - - while(ptr1 && ptr1[0]) - { - ptr2 = strchr(ptr1, ';'); - - if (ptr2) - *(ptr2++) = 0; - - FileName[n++] = ptr1; - - ptr1 = ptr2; - } - - FileName[n] = 0; - - // read the files - - Files = n; - n = 0; - - while (FileName[n]) - { - if (stat(FileName[n], &STAT) == -1) - { - char ErrorMessage[512]; - sprintf(ErrorMessage,"Can't find file %s", FileName[n]); - MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); - return TRUE; - } - - FileLen[n] = STAT.st_size; - - Handle = fopen(FileName[n], "rb"); - - if (Handle == NULL) - { - char ErrorMessage[512]; - sprintf(ErrorMessage,"Can't open file %s", FileName[n]); - MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); - return TRUE; - } - - FileBody[n] = malloc(FileLen[n]+1); - - fread(FileBody[n], 1, FileLen[n], Handle); - - fclose(Handle); - - TotalFileSize += FileLen[n]; - n++; - } - } - - if (strlen(HDest) == 0) - { - MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR); - return TRUE; - } - - if (strlen(BID)) - { - if (LookupBID(BID)) - { - // Duplicate bid - - MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR); - return TRUE; - } - } - - Msg = AllocateMsgRecord(); - - // Set number here so they remain in sequence - - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - - strcpy(Msg->from, SYSOPCall); - - Vptr = strlop(Destcopy, '@'); - - if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @ - { - Vptr = strchr(Destcopy, '!'); - strcpy(Msg->via, Vptr); - strlop(Destcopy, '!'); - - if (strlen(Destcopy) > 6) - memcpy(Msg->to, Destcopy, 6); - else - strcpy(Msg->to, Destcopy); - goto gotAddr; - } - - if (strlen(Destcopy) > 6) - memcpy(Msg->to, Destcopy, 6); - else - strcpy(Msg->to, Destcopy); - - _strupr(Msg->to); - - if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) - { - Vptr = HDest; - memmove(HDest, &HDest[4], strlen(HDest)); - strcpy(Msg->to, "RMS"); - - } - else if (_memicmp(HDest, "smtp:", 5) == 0) - { - if (ISP_Gateway_Enabled) - { - Vptr = HDest; - memmove(HDest, &HDest[5], strlen(HDest)); - Msg->to[0] = 0; - } - } - else if (Vptr) - { - // If looks like a valid email address, treat as such - - int tolen = (Vptr - Destcopy) - 1; - - if (tolen > 6 || !CheckifPacket(Vptr)) - { - // Assume Email address - - Vptr = HDest; - - if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route - strcpy(Msg->to, "RMS"); - else if (ISP_Gateway_Enabled) - Msg->to[0] = 0; - else - { - MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR); - return TRUE; - } - } - } - if (Vptr) - { - if (strlen(Vptr) > 40) - Vptr[40] = 0; - - strcpy(Msg->via, Vptr); - } -gotAddr: - GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61); - GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); - Msg->type = status[0]; - Msg->status = 'N'; - - if (strlen(BID) == 0) - sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); - - strcpy(Msg->bid, BID); - - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0); - - MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments - - if (Files) - { - char DateString[80]; - struct tm * tm; - - char Type[16] = "Private"; - - // Get Type - - if (Msg->type == 'B') - strcpy(Type, "Bulletin"); - else if (Msg->type == 'T') - strcpy(Type, "Traffic"); - - // Create a B2 Message - - // B2 Header - - NewMsg = MailBuffer + 1000; - - tm = gmtime((time_t *)&Msg->datecreated); - - sprintf(DateString, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - // Remove last Source Route - - if (strchr(HDest, '!')) - { - char * bang = HDest + strlen(HDest); - - while (*(--bang) != '!'); // Find last ! - - *(bang) = 0; // remove it; - } - - NewMsg += sprintf(NewMsg, - "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", - Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName); - - - NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen); - - for (n = 0; n < Files; n++) - { - char * p = FileName[n], * q; - - // Remove any path - - q = strchr(p, '\\'); - - while (q) - { - if (q) - *q++ = 0; - p = q; - q = strchr(p, '\\'); - } - - NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p); - } - - NewMsg += sprintf(NewMsg, "\r\n"); - GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1); - NewMsg += MsgLen; - NewMsg += sprintf(NewMsg, "\r\n"); - - for (n = 0; n < Files; n++) - { - memcpy(NewMsg, FileBody[n], FileLen[n]); - NewMsg += FileLen[n]; - free(FileBody[n]); - NewMsg += sprintf(NewMsg, "\r\n"); - } - - Msg->length = NewMsg - (MailBuffer + 1000); - NewMsg = MailBuffer + 1000; - Msg->B2Flags = B2Msg | Attachments; - } - - else - { - GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1); - Msg->length = MsgLen; - NewMsg = MailBuffer; - } - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = CreateFile(MsgFile, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hFile != INVALID_HANDLE_VALUE) - { - WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL); - CloseHandle(hFile); - } - - free(MailBuffer); - - MatchMessagetoBBSList(Msg, 0); - - BuildNNTPList(Msg); // Build NNTP Groups list - - SaveMessageDatabase(); - SaveBIDDatabase(); - - EndDialog(hDlg, LOWORD(wParam)); - - return TRUE; - } - - - if (LOWORD(wParam) == IDSelectFiles) - { - char FileNames[2048]; - char FullFileNames[32768]; - OPENFILENAME Ofn; - int err; - - FileNames[0] = 0; - - memset(&Ofn, 0, sizeof(Ofn)); - - Ofn.lStructSize = sizeof(OPENFILENAME); - Ofn.hInstance = hInst; - Ofn.hwndOwner = hDlg; - Ofn.lpstrFilter = NULL; - Ofn.lpstrFile= FileNames; - Ofn.nMaxFile = 2048; - Ofn.lpstrFileTitle = NULL; - Ofn.nMaxFileTitle = 0; - Ofn.lpstrInitialDir = (LPSTR)NULL; - Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; - Ofn.lpstrTitle = NULL;//; - - if (GetOpenFileName(&Ofn)) - { - // if one is selected, a single string is returned, if more than one, a single - // path, followed by all the strings, duuble null terminated. - - char * Names[101]; // Allow up to 100 names - int n = 0; - char * ptr = FileNames; - - while (*ptr) - { - Names[n++] = ptr; - ptr += strlen(ptr); - ptr++; - } - - GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768); - - if (strlen(FullFileNames)) - strcat(FullFileNames, ";"); - - if (n == 1) - { - // Single Select - - strcat(FullFileNames, FileNames); - } - else - { - int i = 1; - - while(i < n) - { - strcat(FullFileNames, Names[0]); - strcat(FullFileNames, "\\"); - strcat(FullFileNames, Names[i]); - i++; - if (i < n) - strcat(FullFileNames, ";"); - } - } - SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames); - } - else - err = GetLastError(); - return (INT_PTR)TRUE; - } - - - if (LOWORD(wParam) == IDCANCEL) - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - - return (INT_PTR)TRUE; - - break; - } - return (INT_PTR)FALSE; -} - -INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - HGLOBAL hglb; - LPTSTR lptstr; - - switch (message) - { - case WM_INITDIALOG: - - SetWindowText(hDlg, "Send Message from Clipboard"); - - if (!IsClipboardFormatAvailable(CF_TEXT)) - break; - - if (!OpenClipboard(hDlg)) - break; - - hglb = GetClipboardData(CF_TEXT); - - if (hglb != NULL) - { - lptstr = GlobalLock(hglb); - - if (lptstr != NULL) - { - SetDlgItemText(hDlg, IDC_EDIT1, lptstr); - GlobalUnlock(hglb); - } - } - CloseClipboard(); - } - - return SendMsgDialogProc(hDlg, message, wParam, lParam); - -} - -// Message handler for about box. -INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - UNREFERENCED_PARAMETER(lParam); - switch (message) - { - case WM_INITDIALOG: - return (INT_PTR)TRUE; - - case WM_COMMAND: - if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - return (INT_PTR)TRUE; - - break; - } - return (INT_PTR)FALSE; -} - -SMTPMsgs = 0; - -int RefreshMainWindow() -{ - char msg[80]; - CIRCUIT * conn; - int i,n, SYSOPMsgs = 0, HeldMsgs = 0; - time_t now; - struct tm * tm; - char tim[20]; - - SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); - - SMTPMsgs = 0; - - for (n = 0; n < NumberofStreams; n++) - { - conn=&Connections[n]; - - if (!conn->Active) - { - strcpy(msg,"Idle"); - } - else - { - { - if (conn->UserPointer == 0) - strcpy(msg,"Logging in"); - else - { - i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", - conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, - "BBS", conn->OutputQueueLength - conn->OutputGetPointer); - } - } - } - SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); - } - - SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); - - n = 0; - - for (i=1; i <= NumberofMessages; i++) - { - if (MsgHddrPtr[i]->status == 'N') - { - if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) - SYSOPMsgs++; - else - if (MsgHddrPtr[i]->to[0] == 0) - SMTPMsgs++; - } - else - { - if (MsgHddrPtr[i]->status == 'H') - HeldMsgs++; - } - } - - SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); - SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); - SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); - - SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE); - SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE); - SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE); - - now = time(NULL); - - tm = gmtime(&now); - sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); - SetDlgItemText(hWnd, IDC_UTC, tim); - - tm = localtime(&now); - sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); - SetDlgItemText(hWnd, IDC_LOCAL, tim); - - - return 0; -} - -#define MAX_PENDING_CONNECTS 4 - -#define VERSION_MAJOR 2 -#define VERSION_MINOR 0 - -SOCKADDR_IN local_sin; /* Local socket - internet style */ - -PSOCKADDR_IN psin; - -SOCKET sock; - - - -BOOL Initialise() -{ - int i, len; - ConnectionInfo * conn; - struct UserInfo * user = NULL; - HKEY hKey=0; - char * ptr1; - int Attrs, ret; - char msg[500]; - TIME_ZONE_INFORMATION TimeZoneInformation; - struct stat STAT; - - GetTimeZoneInformation(&TimeZoneInformation); - - _tzset(); - _MYTIMEZONE = timezone; - _MYTIMEZONE = TimeZoneInformation.Bias * 60; - - // Register message for posting by BPQDLL - - BPQMsg = RegisterWindowMessage(BPQWinMsg); - - // See if we need to warn of possible problem with BaseDir moved by installer - - strcpy(BPQDirectory, GetBPQDirectory()); - - sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory); - - len = strlen(BaseDir); - ptr1 = BaseDir; - - while (*ptr1) - { - if (*(ptr1) == '/') *(ptr1) = '\\'; - ptr1++; - } - - // Make Sure BASEDIR Exists - - Attrs = GetFileAttributes(BaseDir); - - if (Attrs == -1) - { - sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir); - ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO); - - if (ret == IDYES) - { - ret = CreateDirectory(BaseDir, NULL); - if (ret == 0) - { - MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP); - return FALSE; - } - } - else - { - MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP); - return FALSE; - } - } - else - { - if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY)) - { - sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir); - ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP); - - return FALSE; - } - } - - initUTF8(); - - // Set up file and directory names - - strcpy(UserDatabasePath, BaseDir); - strcat(UserDatabasePath, "\\"); - strcat(UserDatabasePath, UserDatabaseName); - - strcpy(MsgDatabasePath, BaseDir); - strcat(MsgDatabasePath, "\\"); - strcat(MsgDatabasePath, MsgDatabaseName); - - strcpy(BIDDatabasePath, BaseDir); - strcat(BIDDatabasePath, "\\"); - strcat(BIDDatabasePath, BIDDatabaseName); - - strcpy(WPDatabasePath, BaseDir); - strcat(WPDatabasePath, "\\"); - strcat(WPDatabasePath, WPDatabaseName); - - strcpy(BadWordsPath, BaseDir); - strcat(BadWordsPath, "\\"); - strcat(BadWordsPath, BadWordsName); - - strcpy(NTSAliasesPath, BaseDir); - strcat(NTSAliasesPath, "/"); - strcat(NTSAliasesPath, NTSAliasesName); - - strcpy(MailDir, BaseDir); - strcat(MailDir, "\\"); - strcat(MailDir, "Mail"); - - CreateDirectory(MailDir, NULL); // Just in case - - strcpy(ConfigName, BaseDir); - strcat(ConfigName, "\\"); - strcat(ConfigName, "BPQMail.cfg"); - - UsingingRegConfig = FALSE; - - // if config file exists use it else try to get from Registry - - if (stat(ConfigName, &STAT) == -1) - { - UsingingRegConfig = TRUE; - - if (GetConfigFromRegistry()) - { - SaveConfig(ConfigName); - } - else - { - int retCode; - - strcpy(BBSName, GetNodeCall()); - strlop(BBSName, '-'); - strlop(BBSName, ' '); - - sprintf(msg, "No configuration found - Dummy Config created"); - - retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL); - - if (retCode == IDCANCEL) - return FALSE; - - SaveConfig(ConfigName); - } - } - - if (GetConfig(ConfigName) == EXIT_FAILURE) - { - ret = MessageBox(NULL, - "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP); - return FALSE; - } - - // Got a Config File - - if (MainRect.right < 100 || MainRect.bottom < 100) - { - GetWindowRect(MainWnd, &MainRect); - } - - MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); - - if (OpenMon) - CreateMonitor(); - - BBSApplMask = 1<<(BBSApplNum-1); - - ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE); - ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE); - ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE); - - // Make backup copies of Databases - - CopyBIDDatabase(); - CopyMessageDatabase(); - CopyUserDatabase(); - CopyWPDatabase(); - - SetupMyHA(); - SetupFwdAliases(); - SetupNTSAliases(NTSAliasesPath); - - GetWPDatabase(); - GetMessageDatabase(); - GetUserDatabase(); - GetBIDDatabase(); - GetBadWordFile(); - GetHTMLForms(); - - UsingingRegConfig = FALSE; - - // Make sure SYSOPCALL is set - - if (SYSOPCall[0] == 0) - strcpy(SYSOPCall, BBSName); - - // Make sure there is a user record for the BBS, with BBS bit set. - - user = LookupCall(BBSName); - - if (user == NULL) - { - user = AllocateUserRecord(BBSName); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - } - - if ((user->flags & F_BBS) == 0) - { - // Not Defined as a BBS - - if (SetupNewBBS(user)) - user->flags |= F_BBS; - } - - // if forwarding AMPR mail make sure User/BBS AMPR exists - - if (SendAMPRDirect) - { - BOOL NeedSave = FALSE; - - user = LookupCall("AMPR"); - - if (user == NULL) - { - user = AllocateUserRecord("AMPR"); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - NeedSave = TRUE; - } - - if ((user->flags & F_BBS) == 0) - { - // Not Defined as a BBS - - if (SetupNewBBS(user)) - user->flags |= F_BBS; - NeedSave = TRUE; - } - - if (NeedSave) - SaveUserDatabase(); - } - - // Allocate Streams - - for (i=0; i < MaxStreams; i++) - { - conn = &Connections[i]; - conn->BPQStream = FindFreeStream(); - - if (conn->BPQStream == 255) break; - - NumberofStreams++; - - BPQSetHandle(conn->BPQStream, hWnd); - - SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask); - Disconnect(conn->BPQStream); - } - - InitialiseTCP(); - - InitialiseNNTP(); - - SetupListenSet(); // Master set of listening sockets - - if (BBSApplNum) - { - SetupUIInterface(); - if (MailForInterval) - _beginthread(SendMailForThread, 0, 0); - } - - if (cfgMinToTray) - { - AddTrayMenuItem(MainWnd, "Mail Server"); - } - - SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) - SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms) - - // Calulate time to run Housekeeping - { - struct tm *tm; - time_t now; - - now = time(NULL); - - tm = gmtime(&now); - - tm->tm_hour = MaintTime / 100; - tm->tm_min = MaintTime % 100; - tm->tm_sec = 0; - - MaintClock = _mkgmtime(tm); - - while (MaintClock < now) - MaintClock += MaintInterval * 3600; - - Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); - - if (LastHouseKeepingTime) - { - if ((now - LastHouseKeepingTime) > MaintInterval * 3600) - { - DoHouseKeeping(FALSE); - } - } - } - - if (strstr(CmdLine, "tidymail")) - DeleteRedundantMessages(); - - if (strstr(CmdLine, "nohomebbs")) - DontNeedHomeBBS = TRUE; - - if (strstr(CmdLine, "DontCheckFromCall")) - DontCheckFromCall = TRUE; - - CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); - - RefreshMainWindow(); - -// CreateWPReport(); - - CreatePipeThread(); - - return TRUE; -} - -int ConnectState(Stream) -{ - int state; - - SessionStateNoAck(Stream, &state); - return state; -} -UCHAR * EncodeCall(UCHAR * Call) -{ - static char axcall[10]; - - ConvToAX25(Call, axcall); - return &axcall[0]; - -} - -/* -VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo) -{ - struct UserInfo * user; - - int i = FWDInfo->UserIndex; - - if (i == -1) - { - FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users - } - - for (i++; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - if (user->flags & F_POLLRMS) - { - FWDInfo->UserIndex = i; - strcpy(FWDInfo->UserCall, user->Call); - FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20; - return ; - } - } - - // Finished Scan - - FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0; -} -*/ - -#ifndef NEWROUTING - -VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) -{ -} -VOID SetupMyHA() -{ -} -VOID SetupFwdAliases() -{ -} - -int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) -{ - struct UserInfo * bbs; - struct BBSForwardingInfo * ForwardingInfo; - char ATBBS[41]; - char * HRoute; - int Count =0; - - strcpy(ATBBS, Msg->via); - HRoute = strlop(ATBBS, '.'); - - if (Msg->type == 'P') - { - // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckBBSToList(Msg, bbs, ForwardingInfo)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - return 1; - } - } - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - return 1; - } - } - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - return 1; - } - } - - return FALSE; - } - - // Bulls go to all matching BBSs, so the order of checking doesn't matter - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - Count++; - } - } - - return Count; -} -BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) -{ - char ** Calls; - char ** HRoutes; - int i, j; - - if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS - return TRUE; - - // Check TO distributions - - if (ForwardingInfo->TOCalls) - { - Calls = ForwardingInfo->TOCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], Msg->to) == 0) - return TRUE; - - Calls++; - } - } - - // Check AT distributions - - if (ForwardingInfo->ATCalls) - { - Calls = ForwardingInfo->ATCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - if ((HRoute) && (ForwardingInfo->Haddresses)) - { - // Match on Routes - - HRoutes = ForwardingInfo->Haddresses; - - while(HRoutes[0]) - { - i = strlen(HRoutes[0]) - 1; - j = strlen(HRoute) - 1; - - while ((i >= 0) && (j >= 0)) // Until one string rus out - { - if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards - goto next; - } - - return TRUE; - next: - HRoutes++; - } - } - - - return FALSE; - -} - -BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) -{ - char ** Calls; - - // Check TO distributions - - if (ForwardingInfo->TOCalls) - { - Calls = ForwardingInfo->TOCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], Msg->to) == 0) - return TRUE; - - Calls++; - } - } - return FALSE; -} - -BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) -{ - char ** Calls; - - // Check AT distributions - - if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS - return TRUE; - - if (ForwardingInfo->ATCalls) - { - Calls = ForwardingInfo->ATCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - return FALSE; -} - -BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) -{ - char ** HRoutes; - int i, j; - - if ((HRoute) && (ForwardingInfo->Haddresses)) - { - // Match on Routes - - HRoutes = ForwardingInfo->Haddresses; - - while(HRoutes[0]) - { - i = strlen(HRoutes[0]) - 1; - j = strlen(HRoute) - 1; - - while ((i >= 0) && (j >= 0)) // Until one string rus out - { - if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards - goto next; - } - - return TRUE; - next: - HRoutes++; - } - } - return FALSE; -} - -#endif - -char * strlop(char * buf, char delim) -{ - // Terminate buf at delim, and return rest of string - - char * ptr; - - if (buf == NULL) return NULL; // Protect - - ptr = strchr(buf, delim); - - if (ptr == NULL) return NULL; - - *(ptr)++=0; - - return ptr; -} diff --git a/BPQMail.rc b/BPQMail.rc index 24bda1f..75ae17f 100644 --- a/BPQMail.rc +++ b/BPQMail.rc @@ -254,7 +254,7 @@ END IDD_USEREDIT DIALOGEX 20, 20, 293, 281 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Edit User" -FONT 8, "System" +FONT 8, "System", 0, 0, 0x1 BEGIN COMBOBOX 5000,7,10,57,123,CBS_SIMPLE | CBS_SORT | CBS_UPPERCASE | WS_VSCROLL | WS_TABSTOP @@ -1072,39 +1072,45 @@ BEGIN ES_AUTOHSCROLL END -FILTERS DIALOG DISCARDABLE 26, 5, 382, 287 +FILTERS DIALOG DISCARDABLE 26, 5, 382, 371 STYLE WS_CHILD | WS_VISIBLE FONT 8, "System" BEGIN - LTEXT "Reject Messages:",IDC_STATIC,162,29,70,10 - LTEXT "From",IDC_STATIC,83,155,28,10 - EDITTEXT IDC_HOLDFROM,58,167,64,83,ES_MULTILINE | ES_UPPERCASE | + LTEXT "Reject Messages:",IDC_STATIC,162,26,70,10 + LTEXT "From",IDC_STATIC,83,137,28,10 + EDITTEXT IDC_HOLDFROM,58,149,64,83,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN - LTEXT "To",IDC_STATIC,152,155,27,10 - EDITTEXT IDC_HOLDTO,126,167,64,83,ES_MULTILINE | ES_UPPERCASE | + LTEXT "To",IDC_STATIC,152,137,27,10 + EDITTEXT IDC_HOLDTO,126,149,64,83,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN - LTEXT "At",IDC_STATIC,223,155,15,10 - EDITTEXT IDC_HOLDAT,194,167,64,83,ES_MULTILINE | ES_UPPERCASE | + LTEXT "At",IDC_STATIC,223,137,15,10 + EDITTEXT IDC_HOLDAT,194,149,64,83,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN - DEFPUSHBUTTON "Save",IDC_FILTERSAVE,171,266,50,14,BS_CENTER | + DEFPUSHBUTTON "Save",IDC_FILTERSAVE,171,341,50,14,BS_CENTER | BS_VCENTER LTEXT "From",IDC_STATIC,83,40,28,10 - EDITTEXT IDC_REJFROM,58,52,64,83,ES_MULTILINE | ES_UPPERCASE | + EDITTEXT IDC_REJFROM,58,52,64,67,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN LTEXT "To",IDC_STATIC,154,40,27,10 - EDITTEXT IDC_REJTO,126,52,64,83,ES_MULTILINE | ES_UPPERCASE | + EDITTEXT IDC_REJTO,126,52,64,68,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN LTEXT "At",IDC_STATIC,223,40,15,10 - EDITTEXT IDC_REJAT,194,52,64,83,ES_MULTILINE | ES_UPPERCASE | + EDITTEXT IDC_REJAT,194,52,64,68,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN LTEXT "Message Filtering Setup.",IDC_STATIC,152,10,95,15 - LTEXT "Hold Messages:",IDC_STATIC,166,143,60,9 - EDITTEXT IDC_REJBID,262,52,64,83,ES_MULTILINE | ES_UPPERCASE | + LTEXT "Hold Messages:",IDC_STATIC,166,128,60,9 + EDITTEXT IDC_REJBID,262,52,64,68,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN - EDITTEXT IDC_HOLDBID,262,167,64,83,ES_MULTILINE | ES_UPPERCASE | + EDITTEXT IDC_HOLDBID,262,149,64,83,ES_MULTILINE | ES_UPPERCASE | ES_AUTOVSCROLL | ES_WANTRETURN - LTEXT "BID",IDC_STATIC,289,155,15,10 + LTEXT "BID",IDC_STATIC,289,137,15,10 LTEXT "BID",IDC_STATIC,289,41,15,10 + EDITTEXT IDC_REJSYS,58,265,270,66,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Composite Rules (like fbb reject.sys)",IDC_STATIC,152, + 236,134,9 + LTEXT "Action, Type, from, @BBS, to, BID, maximum size", + IDC_STATIC,59,251,247,9 END WPUPDATE DIALOG DISCARDABLE 26, 5, 382, 287 @@ -1258,6 +1264,7 @@ BEGIN "FILTERS", DIALOG BEGIN RIGHTMARGIN, 377 + BOTTOMMARGIN, 355 END IDD_RMSBULLDLG, DIALOG @@ -1444,6 +1451,11 @@ BEGIN 0x0000 END +FILTERS AFX_DIALOG_LAYOUT MOVEABLE PURE +BEGIN + 0x0000 +END + ///////////////////////////////////////////////////////////////////////////// // diff --git a/BPQMail.vcproj b/BPQMail.vcproj index 3c7ef20..3bfb0b5 100644 --- a/BPQMail.vcproj +++ b/BPQMail.vcproj @@ -68,7 +68,7 @@ + + + + + + + + + + diff --git a/BPQMail.vcxproj b/BPQMail.vcxproj index 2ebc492..5aa7387 100644 --- a/BPQMail.vcxproj +++ b/BPQMail.vcxproj @@ -5,23 +5,16 @@ Debug Win32 - - Debug - x64 - Release Win32 - - Release - x64 - {3766AA10-C777-4ED8-A83D-F1452DE9B665} TelnetServer Win32Proj + 10.0.17763.0 @@ -30,39 +23,21 @@ NotSet true - - Application - v141 - NotSet - true - Application v141 false NotSet - - Application - v141 - false - NotSet - - - - - - - <_ProjectFileVersion>15.0.28307.799 @@ -72,17 +47,11 @@ C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ true - - true - C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ false - - false - @@ -117,40 +86,6 @@ MachineX86 - - - - - - - Disabled - ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - true - - - Level3 - ProgramDatabase - CompileAsC - - - ..\Include;%(AdditionalIncludeDirectories) - - - ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) - c:\DevProgs\bpq32\BPQMail.exe - false - LIBCMT;%(IgnoreSpecificDefaultLibraries) - true - $(IntDir)$(TargetName).pdb - true - $(IntDir)BBSListings\bpqmail.map - true - Windows - - @@ -186,124 +121,58 @@ MachineX86 - - - - - - - - - - - MaxSpeed - false - ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - CompileAsC - - - ..\Include;%(AdditionalIncludeDirectories) - - - ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) - c:\DevProgs\bpq32\BPQMail.exe - true - c:\DevProgs\bpq32\BPQMail.pdb - true - c:\DevProgs\bpq32\BPQMail.map - Windows - true - true - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc All - All $(IntDir) - $(IntDir) $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc All - All $(IntDir) - $(IntDir) $(IntDir) - $(IntDir) $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc @@ -312,25 +181,17 @@ $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc diff --git a/BPQMailrc.h b/BPQMailrc.h index e13267c..750d898 100644 --- a/BPQMailrc.h +++ b/BPQMailrc.h @@ -255,6 +255,8 @@ #define IDC_REJFROM 7077 #define IDC_REJTO 7078 #define IDC_REJAT 7079 +#define IDC_HOLDFROM2 7080 +#define IDC_REJSYS 7080 #define IDM_HOUSEKEEPING 9000 #define IDM_PR 9001 #define IDM_PUR 9002 @@ -322,7 +324,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 30012 +#define _APS_NEXT_RESOURCE_VALUE 30013 #define _APS_NEXT_COMMAND_VALUE 40027 #define _APS_NEXT_CONTROL_VALUE 1093 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/Bpq32.c b/Bpq32.c index 5ee2c61..c2a80c3 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1183,6 +1183,17 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Version 6.0.25.? // Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers +// Add Chat PACLEN config (5) +// Fix NC to Application Call (6) +// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) +// Get Beacon config from config file on Windows (9) +// fix processing DED TNC Emulator M command with space between M and params (10) +// Fix sending UI frames on SCSPACTOR (11) +// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) +// Add SDRAngel rig control (11) +// Add option to specify config and data directories on linbpq (12) +// Allow zero resptime (send RR immediately) (13) +// Fix corruptions in Webmail on 64 bit builds, eg in displaying 7+ files (15) #define CKernel @@ -1408,6 +1419,10 @@ extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:L extern char LOC[7]; // Maidenhead Locator for Reporting extern char ReportDest[7]; +extern UCHAR ConfigDirectory[260]; + +extern uint64_t timeLoadedMS; + VOID __cdecl Debugprintf(const char * format, ...); VOID __cdecl Consoleprintf(const char * format, ...); @@ -2290,6 +2305,9 @@ FirstInit() GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); } + + timeLoadedMS = GetTickCount(); + INITIALISEPORTS(); OpenReportingSockets(); @@ -3269,6 +3287,8 @@ if (_winver < 0x0600) RegCloseKey(hKey); } + strcpy(ConfigDirectory, BPQDirectory); + if (LogDirectory[0] == 0) strcpy(LogDirectory, BPQDirectory); diff --git a/CBPQ32.suo b/CBPQ32.suo deleted file mode 100644 index b8ffa3c02960d9c230e0173679cbef6d767078e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5632 zcmeH~Ymkp+7{(uj*suyk#TFKe#kR<15sMtwZjO~*tgN+8>)eWrrY6cU%+O>rR7?&v zl+;Wo)2Gxpt&nNTis?hg4-L}?w(GLLHE(uzCZC!b_s%`f_5Z#1{XWn89PansqemK^ z-MY6~Nx05>g&N^XX`N6j!mCN!uUc#f)g}DT<9NWs+|)SYQ_J#HPMbKpP6g5p^EgSI}dIiaQJk! z8oK^jy|SQ8^;i(5hC%9mvQ+<2SVeZ_xJjs9&HcL;xve~^B31diY8hd^o_c!7SO50B zFhaj`&kbX#QdzfTMk@hJ{ zjj*B8J^|@4pP)Et+we%-2-s+tPv%(k1X}loM-Rn z&jMLmge`)th_Ln2k1K8?%oSuMalgQPpKN_%_~edL>-)RH{7T$Ln5)AkY1?L)W4B2A zIkv%Em5PYl3lj>-Dq&udUTU3ZTPE#2vLV7Y!uChl8R>Y%orR5*-bWuR^-#W_$G6H4 zC_4tTJ`O96uo7wadj;mI`Ls)`Y>8Um&%R39)o?Y;YjcA8ZHKvv?vS<}hI#&upo?i; zVNapY&^qT^XN>E*Chuy$grVe4RR#MZ+$!JPXFtO(Xe>^0aMFrn}!Y&$GfYzORPSTC_pU?*WI zVy9p~z~aQt!@B8}=(+NkyZiMh>k0GtQeh9n62f}vx!z`@ zXVN;ifViCz786@mv+Jw%`21qpMch!BbJL}588DA;w6wo+#=v~6)=B#w8(^+4nbNj_ zD#Gr=_J|9zY!J*LN2Oh3kHK6c1`{`5#oxL1B5Bu|EfMy*bb{FS2-_{~TDK>{K9zR; zI0%yrUr48^Jp^;k0%_Y}n1^u$b{^(h{fD%l@ghtzT+-*G>qc!2vix~tr2RbgU>VXE zxL-W1lUN&RTXKYTh4p~-*7X$h5ZYw1bZOf-nDuzr1Xz+-4lFkkHyM@(^SVxfJp=Q+ zEP=fW>nXMwwiPB6w!wD8d<;I4cFp@7=05fiR}AxfohI%Zn78 zmb?{VXQaJf>gzE3Ieeyz#B8xJ*Sd!2R -//#include "vmm.h" -//#include "SHELLAPI.H" - -#include "CHeaders.h" -#include "bpqaprs.h" - -#pragma pack() - -#include "tncinfo.h" -#include "telnetserver.h" - -//#include "GetVersion.h" - -//#define DllImport __declspec( dllimport ) -//#define DllExport __declspec( dllexport ) - -BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR *AXCalls); -VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); -int APIENTRY ClearNodes(); -VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); -VOID SendHTTPRequest(SOCKET sock, char * Host, int Port, char * Request, char * Params, int Len, char * Return); -SOCKET OpenWL2KHTTPSock(); -VOID FormatTime3(char * Time, time_t cTime); -VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6); -VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst); -VOID FindLostBuffers(); -BOOL CheckCMS(struct TNCINFO * TNC); -VOID L2SENDXID(struct _LINKTABLE * LINK); -int CountBits(unsigned long in); -VOID SaveMH(); -BOOL RestartTNC(struct TNCINFO * TNC); -void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID WriteMiniDump(); - -char COMMANDBUFFER[81] = ""; // Command Hander input buffer -char OrigCmdBuffer[81] = ""; // Command Hander input buffer - -struct DATAMESSAGE * REPLYBUFFER = NULL; -UINT APPLMASK = 0; -UCHAR SAVEDAPPLFLAGS = 0; - -UCHAR ALIASINVOKED = 0; - -extern struct TNCINFO * TNCInfo[41]; - -VOID * CMDPTR = 0; - -short CMDPACLEN = 0; - -char OKMSG[] = "Ok\r"; - -char CMDERRMSG[] = "Invalid command - Enter ? for command list\r"; -#define CMDERRLEN sizeof(CMDERRMSG) - 1 - -char PASSWORDMSG[] = "Command requires SYSOP status - enter password\r"; -#define LPASSMSG sizeof(PASSWORDMSG) - 1 - -char CMDLIST[] = "CONNECT BYE INFO NODES PORTS ROUTES USERS MHEARD"; - -#define CMDLISTLEN sizeof(CMDLIST) - 1 - -char BADMSG[] = "Bad Parameter\r"; -char BADPORT[] = "Invalid Port Number\r"; -char NOTEXTPORT[] = "Only valid on EXT ports\r"; -char NOVALCALLS[] = "No Valid Calls defined on this port\r"; - -char BADVALUEMSG[] = "Invalid parameter\r"; - -char BADCONFIGMSG[] = "Configuration File check falled - will continue with old config\r"; -#ifdef LINBPQ -char REBOOTOK[] = "Rebooting\r"; -#else -char REBOOTOK[] = "Rebooting in 20 secs\r"; -#endif -char REBOOTFAILED[] = "Shutdown failed\r"; - -char RESTARTOK[] = "Restarting\r"; -char RESTARTFAILED[] = "Restart failed\r"; - -UCHAR ARDOP[7] = {'A'+'A','R'+'R','D'+'D','O'+'O','P'+'P',' '+' '}; // ARDOP IN AX25 -UCHAR VARA[7] = {'V'+'V','A'+'A','R'+'R','A'+'A',' '+' ',' '+' '}; // VARA IN AX25 - -int STATSTIME = 0; -int MAXBUFFS = 0; -int QCOUNT = 0; -int MINBUFFCOUNT = 65535; -int NOBUFFCOUNT = 0; -int BUFFERWAITS = 0; -int MAXDESTS = 0; -int NUMBEROFNODES = 0; -int L4CONNECTSOUT = 0; -int L4CONNECTSIN = 0; -int L4FRAMESTX = 0; -int L4FRAMESRX = 0; -int L4FRAMESRETRIED = 0; -int OLDFRAMES = 0; -int L3FRAMES = 0; - -VOID SENDSABM(); -VOID RESET2(); - -int APPL1 = 0; -int PASSCMD = 0; - -#pragma pack(1) - -struct _EXTPORTDATA DP; // Only way I can think of to get offets to port data into cmd table - -char CMDALIAS[ALIASLEN][NumberofAppls] = {0}; -char * ALIASPTR = &CMDALIAS[0][0]; - -extern int RigReconfigFlag; - -CMDX COMMANDS[]; - -int CMDXLEN = sizeof (CMDX); - -VOID SENDNODESMSG(); -VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); -void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); - - - -char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...) -{ - // Send Command response checking PACLEN - - char Mess[4096]; - va_list(arglist); - int OldLen; - int MsgLen; - struct DATAMESSAGE * Buffer; - char * Messptr = Mess; - int Paclen = Session->SESSPACLEN; - - if (Paclen == 0) - Paclen = 255; - - va_start(arglist, format); - - MsgLen = vsprintf(Mess, format, arglist); - - OldLen = (int)(Bufferptr - (char *)REPLYBUFFER->L2DATA); - - while ((OldLen + MsgLen) > Paclen) - { - // Have to send Paclen then get a new buffer - - int ThisBit = Paclen - OldLen; // What we can send this time - - if (ThisBit < 0) - ThisBit = 0; // How can this happen?? - - memcpy(Bufferptr, Messptr, ThisBit); - Messptr += ThisBit; - MsgLen -= ThisBit; - - // QUEUE IT AND GET ANOTHER BUFFER - - Buffer = (struct DATAMESSAGE *)GetBuff(); - - if (Buffer == NULL) - - // No buffers, so just reuse the old one (better than crashing !!) - - Buffer = REPLYBUFFER; - else - SendCommandReply(Session, REPLYBUFFER, Paclen + (4 + sizeof(void *))); - - - REPLYBUFFER = Buffer; - Buffer->PID = 0xf0; - - Bufferptr = &Buffer->L2DATA[0]; - OldLen = 0; - } - - // Add last bit to buffer - - memcpy(Bufferptr, Messptr, MsgLen); - - return Bufferptr + MsgLen; -} - - -VOID SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - SENDNODESMSG(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID SAVEMHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - SaveMH(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID SAVENODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - SaveNodes(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID DUMPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - WriteMiniDump(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID RIGRECONFIG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (!ProcessConfig()) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check falled - will continue with old config"); - } - else - { - RigReconfigFlag = TRUE; - Bufferptr = Cmdprintf(Session, Bufferptr, "Rigcontrol Reconfig requested"); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID REBOOT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (Reboot()) - { - strcpy(Bufferptr, REBOOTOK); - Bufferptr += (int)strlen(REBOOTOK); - } - else - { - strcpy(Bufferptr, REBOOTFAILED); - Bufferptr += (int)strlen(REBOOTFAILED); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID RESTART(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (Restart()) - { - strcpy(Bufferptr, RESTARTOK); - Bufferptr += (int)strlen(RESTARTOK); - } - else - { - strcpy(Bufferptr, RESTARTFAILED); - Bufferptr += (int)strlen(RESTARTFAILED); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID RESTARTTNC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char * ptr, *Context; - int portno; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno && portno < 33) - { - struct TNCINFO * TNC = TNCInfo[portno]; - - if (TNC == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - } - else - { - if (TNC->ProgramPath) - { - if (RestartTNC(TNC)) - Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Ok\r", TNC->ProgramPath); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Failed\r", TNC->ProgramPath); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "PATH not defined so can't restart TNC\r"); - } - } - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -UCHAR VALNODESFLAG = 0, EXTONLY = 0; - -VOID PORTVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); - -VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - VALNODESFLAG = 1; - PORTVAL(Session, Bufferptr, CmdTail, CMD); -} - -VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - EXTONLY = 1; - PORTVAL(Session, Bufferptr, CmdTail, CMD); -} -VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS PORT VALUE COMMANDS - - char * ptr, *Context, * ptr1; - int portno; - UCHAR oldvalue, newvalue; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - UCHAR * valueptr; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - if (VALNODESFLAG) - { - char * VNPtr = PORT->PERMITTEDCALLS; - char Normcall[10]; - - VALNODESFLAG = 0; - - if (VNPtr) - { - while (VNPtr[0]) - { - Normcall[ConvFromAX25(VNPtr, Normcall)] = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); - VNPtr += 7; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", NOVALCALLS); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - - } - - if (EXTONLY) - { - // Make sure an Extenal Port - - EXTONLY = 0; - - if (PORT->PORTTYPE != 0x10) - { - strcpy(Bufferptr, NOTEXTPORT); - Bufferptr += (int)strlen(NOTEXTPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - valueptr = (UCHAR *)PORT + CMD->CMDFLAG; - oldvalue = *valueptr; - - // Display Param Namee - - ptr1 = &CMD->String[0]; - n = 12; - - while (*(ptr1) != ' ' && n--) - *(Bufferptr++) = *(ptr1++); - - // See if another param - if not, just display current value - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - *valueptr = newvalue; - - Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); - } - - else - Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - -VOID SWITCHVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Update switch 8 bit value - - char * ptr, *Context, * ptr1; - UCHAR oldvalue, newvalue; - int n; - UCHAR * valueptr; - - valueptr = (UCHAR *)CMD->CMDFLAG; - - oldvalue = *valueptr; - - // Display Param Name - - ptr1 = &CMD->String[0]; - n = 12; - - while (*(ptr1) != ' ' && n--) - *(Bufferptr++) = *(ptr1++); - - // See if a param - if not, just display current value - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - *valueptr = newvalue; - - Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); - - if (memcmp(CMD->String, "NODESINT ", 8) == 0) - L3TIMER = L3INTERVAL; - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - -VOID SWITCHVALW (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Update switch 16 bit value - - char * ptr, *Context, * ptr1; - USHORT oldvalue, newvalue; - int n; - USHORT * valueptr; - - valueptr = (USHORT *)CMD->CMDFLAG; - - oldvalue = (USHORT)*valueptr; - - // Display Param Name - - ptr1 = &CMD->String[0]; - n = 12; - - while (*(ptr1) != ' ' && n--) - *(Bufferptr++) = *(ptr1++); - - // See if a param - if not, just display current value - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - *valueptr = newvalue; - - Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - -TRANSPORTENTRY * SetupSessionFromSession(TRANSPORTENTRY * Session, PBPQVECSTRUC HOSTSESS, UINT APPLMASK) -{ - // Create a Transport (L4) session linked to an incoming Session - - TRANSPORTENTRY * NewSess = L4TABLE; - int Index = 0; - - while (Index < MAXCIRCUITS) - { - if (NewSess->L4USER[0] == 0) - { - // Got One - - UCHAR * ourcall = &MYCALL[0]; - - Session->L4CROSSLINK = NewSess; - NewSess->L4CROSSLINK = Session; - - if (APPLMASK) - { - // Circuit for APPL - look for an APPLCALL - - APPLCALLS * APPL = APPLCALLTABLE; - - while ((APPLMASK & 1) == 0) - { - APPLMASK >>= 1; - APPL++; - } - if (APPL->APPLCALL[0] > 0x40) // We have an applcall - ourcall = &APPL->APPLCALL[0]; - } - - memcpy(NewSess->L4USER, ourcall, 7); - memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); - - NewSess->CIRCUITINDEX = Index; //OUR INDEX - NewSess->CIRCUITID = NEXTID; - - NEXTID++; - if (NEXTID == 0) - NEXTID++; // kEEP nON-ZERO - - NewSess->SESSIONT1 = Session->SESSIONT1; - NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; - NewSess->SESSPACLEN = PACLEN; // Default; - - NewSess->L4TARGET.HOST = HOSTSESS; - NewSess->L4STATE = 5; - return NewSess; - } - Index++; - NewSess++; - } - return NULL; -} - -extern int GETCONNECTIONINFO(); - - -BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions) -{ - PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; - TRANSPORTENTRY * NewSess; - int ApplNum; - int n = BPQHOSTSTREAMS; - int ConfigedPorts = 0; - - // LOOK FOR A FREE HOST SESSION - - while (n--) - { - if (HOSTSESS->HOSTAPPLMASK & Mask) - { - // Right appl - - ConfigedPorts++; - - if (HOSTSESS->HOSTSESSION == NULL && (HOSTSESS->HOSTFLAGS & 3) == 0) // Not attached and no report outstanding - { - // WEVE GOT A FREE BPQ HOST PORT - USE IT - - NewSess = SetupSessionFromSession(Session, HOSTSESS, Mask); - - if (NewSess == NULL) - return FALSE; // Appl not available - - HOSTSESS->HOSTSESSION = NewSess; - - // Convert APPLMASK to APPLNUM - - ApplNum = 1; - - while (APPLMASK && (APPLMASK & 1) == 0) - { - ApplNum++; - APPLMASK >>= 1; - } - - HOSTSESS->HOSTAPPLNUM = ApplNum; - - HOSTSESS->HOSTFLAGS |= 2; // Indicate State Change - - NewSess->L4CIRCUITTYPE = BPQHOST | DOWNLINK; - - PostStateChange(NewSess); - - NewSess->SESS_APPLFLAGS = HOSTSESS->HOSTAPPLFLAGS; - - NewSess->SESSPACLEN = Paclen; - - return TRUE; - } - } - HOSTSESS++; - } - - *AnySessions = ConfigedPorts; // to distinguish between none and all in use - return FALSE; -} - -VOID APPLCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - BOOL CONFAILED = 0; - UINT CONERROR ; - char APPName[13]; - char * ptr1, *ptr2; - int n = 12; - BOOL Stay = FALSE; - - // Copy Appl and Null Terminate - - ptr1 = &CMD->String[0]; - ptr2 = APPName; - - while (*(ptr1) != ' ' && n--) - *(ptr2++) = *(ptr1++); - - *(ptr2) = 0; - - if (Session->LISTEN) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Can't use %s while listening\r", APPName); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - if (CmdTail[0] == 'S') - Stay = TRUE; - - Session->STAYFLAG = Stay; - - memcpy(Session->APPL, CMD->String, 12); - - // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND - - if (ALIASPTR[0] > ' ') - { - // COPY ALIAS TO COMMAND BUFFER, THEN REENTER COMMAND HANDLER - - int SaveSecure = Session->Secure_Session; - - memcpy(COMMANDBUFFER, ALIASPTR, ALIASLEN); - _strupr(COMMANDBUFFER); - memcpy(OrigCmdBuffer, ALIASPTR, ALIASLEN); // In case original case version needed - - ALIASINVOKED = 1; // To prevent Alias Loops - - // Set secure session for application alias in case telnet outward connect - - Session->Secure_Session = 1; - DoTheCommand(Session); - Session->Secure_Session = SaveSecure; - - return; - } - - if (cATTACHTOBBS(Session, APPLMASK, CMDPACLEN, &CONERROR) == 0) - { - // No Streams - - if (CONERROR) - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, All %s Ports are in use - Please try later\r", APPName); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Application %s is not running - Please try later\r", APPName); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // IF CMD_TO_APPL SET IN APPLFLAGS, SEND INPUT MSG TO APPL - - if (Session->L4CROSSLINK->SESS_APPLFLAGS & CMD_TO_APPL) - { - struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff(); - TRANSPORTENTRY * XSession = Session->L4CROSSLINK; - - if (Msg) - { - COMMANDBUFFER[72] = 13; - memcpy(Msg->L2DATA, COMMANDBUFFER, 73); - Msg->LENGTH = 73 + 4 + sizeof(void *); - Msg->PID = 0xf0; - - C_Q_ADD(&XSession->L4TX_Q, (UINT *)Msg); - PostDataAvailable(XSession); - } - } - - if (Stay) - Session->L4CROSSLINK->L4TARGET.HOST->HOSTFLAGS |= 0x20; - - // IF MSG_TO_USER SET, SEND 'CONNECTED' MESSAGE TO USER - - Session->SESS_APPLFLAGS = Session->L4CROSSLINK->SESS_APPLFLAGS; - - if (Session->L4CROSSLINK->SESS_APPLFLAGS & MSG_TO_USER) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Connected to %s\r", APPName); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - // DONT NEED BUFFER ANY MORE - - ReleaseBuffer((UINT *)REPLYBUFFER); - return; -} - - -VOID CMDI00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (sizeof(void *) == 4) - Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s\r", VersionString); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s (64 bit)\r", VersionString); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID BYECMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link - ReleaseBuffer((UINT *)REPLYBUFFER); - return; -} - -VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // SET PACLEN FOR THIS SESSION - - char * ptr, *Context; - int newvalue; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - if (newvalue > 29 && newvalue < 256) - Session->SESSPACLEN = newvalue & 0xff; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "PACLEN - %d\r", Session->SESSPACLEN); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDIDLE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // SET IDLETIME FOR THIS SESSION - - char * ptr, *Context; - int newvalue; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - if (newvalue > 59 && newvalue < 901) - Session->L4LIMIT = newvalue; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "IDLETIME - %d\r", Session->L4LIMIT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - -} -VOID CMDT00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // SET L4 TIMEOUT FOR CONNECTS ON THIS SESSION - - char * ptr, *Context; - int newvalue; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - if (newvalue > 20) - Session->SESSIONT1 = newvalue; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "L4TIMEOUT - %d\r", Session->SESSIONT1); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -UCHAR PWLen; -char PWTEXT[80]; - -VOID PWDCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char * ptr, *Context; - USHORT pwsum = 0; - int n = 5, p1, p2, p3, p4, p5; - - if (Session->Secure_Session) // HOST - SET AUTHORISED REGARDLESS - { - Session->PASSWORD = 0xFFFF; // SET AUTHORISED - Session->Secure_Session = 1; - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Check Password - - n = 5; - - while (n--) - pwsum += *(ptr++); - - if (Session->PASSWORD == pwsum) - { - Session->PASSWORD = 0xFFFF; // SET AUTHORISED - Session->Secure_Session = 1; - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - ReleaseBuffer((UINT *)REPLYBUFFER); - return; - } - - // SEND PASSWORD PROMPT - - if (PWLen == 0) - PWLen = 1; - - p1 = rand() % PWLen; - pwsum += PWTEXT[p1++]; - - p2 = rand() % PWLen; - pwsum += PWTEXT[p2++]; - - p3 = rand() % PWLen; - pwsum += PWTEXT[p3++]; - - p4 = rand() % PWLen; - pwsum += PWTEXT[p4++]; - - p5 = rand() % PWLen; - pwsum += PWTEXT[p5++]; - - Session->PASSWORD = pwsum; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%d %d %d %d %d\r", p1, p2, p3, p4, p5); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char * ptr, *Context; - int Port = 0, cols = NUMBEROFPORTS, i; - char * uptime; - struct PORTCONTROL * PORT = PORTTABLE; - struct PORTCONTROL * STARTPORT; - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - // SEE IF ANY PARAM - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - Port = atoi(ptr); - - // IF ASKING FOR PORT STATS, DONT DO SYSTEM ONES - - if (Port == 0) - { - uptime = FormatUptime(STATSTIME); - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", uptime); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Semaphore Get-Rel/Clashes %9d%9d\r", - Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Buffers:Max/Cur/Min/Out/Wait%9d%9d%9d%9d%9d\r", - MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Known Nodes/Max Nodes %9d%9d\r", - NUMBEROFNODES, MAXDESTS); - - Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Connects Sent/Rxed %9d%9d\r", - L4CONNECTSOUT, L4CONNECTSIN); - - Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Frames TX/RX/Resent/Reseq%9d%9d%9d%9d\r", - L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); - - Bufferptr = Cmdprintf(Session, Bufferptr, "L3 Frames Relayed %9d\r", L3FRAMES); - - if (ptr && ptr[0] == 'S') - { - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - // POSITION TO REQUESTED PORT - - if (Port) - { - while (PORT && PORT->PORTNUMBER != Port) - { - PORT = PORT->PORTPOINTER; - cols--; - } - } - - if (PORT == NULL) // REQUESTED PORT NOT FOUND - { - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - STARTPORT = PORT; - - if (cols > 7) - cols = 7; - - Bufferptr = Cmdprintf(Session, Bufferptr, " "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Port %02d ", PORT->PORTNUMBER); - PORT = PORT->PORTPOINTER; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Digied"); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2DIGIED); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Heard "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMES); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Rxed "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESFORUS); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Sent "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESSENT); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Timeouts "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2TIMEOUTS); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "REJ Frames Rxed "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2REJCOUNT); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "RX out of Seq "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2OUTOFSEQ); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Resequenced "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2RESEQ); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Undrun/Poll T/o "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2URUNC); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "RX Overruns "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2ORUNC); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "RX CRC Errors "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->RXERRORS); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Sent "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRTX); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Received "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRRX); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Frames abandoned"); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L1DISCARD); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d", PORT->AVSENDING, PORT->AVACTIVE); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS 'LINKS' MESSAGE - - struct _LINKTABLE * LINK = LINKS; - int n = MAXLINKS; - int len; - char Normcall[11] = ""; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r"); - - while (n--) - { - if (LINK->LINKCALL[0]) - { - len = ConvFromAX25(LINK->LINKCALL, Normcall); - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); - - len = ConvFromAX25(LINK->OURCALL, Normcall); - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); - - if (LINK->Ver2point2) - Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=2.2\r", - LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE); - else - Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r", - LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG); - } - LINK++; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - -VOID CMDS00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS 'USERS' - - int n = MAXCIRCUITS; - TRANSPORTENTRY * L4 = L4TABLE; - TRANSPORTENTRY * Partner; - int MaxLinks = MAXLINKS; - char State[12] = "", Type[12] = "Uplink"; - char LHS[50] = "", MID[10] = "", RHS[50] = ""; - char Line[100]; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%d)\r", SESSIONHDDR, QCOUNT); - - while (n--) - { - if (L4->L4USER[0]) - { - RHS[0] = MID[0] = 0; - - if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES - { - // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK - - if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END - { - // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION - // DISPLAY TO THE RIGHT FOR NOW - - strcpy(LHS, "(Closing) "); - DISPLAYCIRCUIT(L4, RHS); - goto CMDS50; - } - else - goto CMDS60; // WILL PROCESS FROM OTHER END - } - - if (L4->L4CROSSLINK == 0) - { - // Single Entry - - DISPLAYCIRCUIT(L4, LHS); - } - else - { - DISPLAYCIRCUIT(L4, LHS); - - Partner = L4->L4CROSSLINK; - - if (Partner->L4STATE == 5) - strcpy(MID, "<-->"); - else - strcpy(MID, "<~~>"); - - DISPLAYCIRCUIT(Partner, RHS); - } -CMDS50: - memset(Line, 32, 100); - memcpy(Line, LHS, (int)strlen(LHS)); - memcpy(&Line[35], MID, (int)strlen(MID)); - strcpy(&Line[40], RHS); - strcat(&Line[40], "\r"); - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Line); - } -CMDS60: - L4++; - } - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Process PORTS Message - - struct PORTCONTROL * PORT = PORTTABLE; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r"); - - while (PORT) - { - if (PORT->Hide == 0) - Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION); - - PORT = PORT->PORTPOINTER; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -char * DisplayRoute(TRANSPORTENTRY * Session, char * Bufferptr, struct ROUTE * Routes, char Verbose) -{ - char Normcall[10]; - char locked[] = " ! "; - int NodeCount; - int Percent = 0; - char PercentString[20]; - int Iframes, Retries; - char Active[10]; - int Queued; - - int Port = 0; - - int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); - - Normcall[9]=0; - - if ((Routes->NEIGHBOUR_FLAG & 1) == 1) - strcpy(locked, "!"); - else - strcpy(locked, " "); - - NodeCount = COUNTNODES(Routes); - - if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) - strcpy(Active, ">"); - else - strcpy(Active, " "); - - if (Verbose) - { - if (Routes->NEIGHBOUR_LINK) - Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); // SEE HOW MANY QUEUED - else - Queued = 0; - - Iframes = Routes->NBOUR_IFRAMES; - Retries = Routes->NBOUR_RETRIES; - - if (Iframes) - { - Percent = (Retries * 100) / Iframes; - sprintf(PercentString, "%3d%%", Percent); - } - else - strcpy(PercentString, " "); - - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%2d %s %3d %3d%s%4d %4d %s %d %d %02d:%02d %d %d", - Active, Routes->NEIGHBOUR_PORT, Normcall, - Routes->NEIGHBOUR_QUAL, NodeCount, locked, Iframes, Retries, PercentString, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, - Routes->NEIGHBOUR_TIME >> 8, (Routes->NEIGHBOUR_TIME) & 0xff, Queued, Routes->OtherendsRouteQual); - - // IF INP3 DISPLAY SRTT - - if (Routes->INP3Node) // INP3 Enabled? - { - double srtt = Routes->SRTT/1000.0; - double nsrtt = Routes->NeighbourSRTT/1000.0; - - Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt); - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s %d %s %d %d%s\r", - Active, Routes->NEIGHBOUR_PORT, Normcall, Routes->NEIGHBOUR_QUAL, NodeCount, locked); - } - - return Bufferptr; -} - - -VOID CMDR00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - struct ROUTE * Routes = NEIGHBOURS; - int MaxRoutes = MAXNEIGHBOURS; - char locked[] = " ! "; - int Percent = 0; - char * ptr, * Context; - char Verbose = 0; - int Port = 0; - char AXCALL[7]; - BOOL Found; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && (int)strlen(ptr) > 1) - { - // Route Update - - goto ROUTEUPDATE; - } - - if (ptr) - { - Verbose = ptr[0]; - ptr = strtok_s(NULL, " ", &Context); - if (ptr) - Port = atoi(ptr); - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Routes\r"); - - while (MaxRoutes--) - { - if (Routes->NEIGHBOUR_CALL[0] != 0) - if (Port == 0 || Port == Routes->NEIGHBOUR_PORT) - Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose); - - Routes++; - } - goto SendReply; - -ROUTEUPDATE: - - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - goto SendReply; - } - - // Line is - - // ROUTES G8BPQ-2 2 100 - Set quality to 100 - // ROUTES G8BPQ-2 2 ! - Toggle 'Locked Route' flag - // ROUTES G8BPQ-2 2 100 ! - Set quality and toggle 'locked' flag - - - ConvToAX25(ptr, AXCALL); - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number Missing \r"); - goto SendReply; - } - - Found = FindNeighbour(AXCALL, Port, &Routes); - - if (Context && Context[0] > 32) - { - // More Params - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - { - // Adding - - memcpy(Routes->NEIGHBOUR_CALL, AXCALL, 7); // In case Add - Routes->NEIGHBOUR_PORT = Port; - Found = TRUE; - } - - if (strcmp(ptr, "!") == 0) - { - // Toggle Lock - - Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT - goto Displayit; - } - - if (strcmp(ptr, "Z") == 0) - { - // Clear Counts - - Routes->NBOUR_IFRAMES = 0; - Routes->NBOUR_RETRIES = 0; - goto Displayit; - } - - Routes->NEIGHBOUR_QUAL = atoi(ptr); - - if (Context && Context[0] == '!') - { - // Toggle Lock - - Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT - goto Displayit; - } - } - -Displayit: - - // Just display - - if (Found) - Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); - - - -/* MOV ROUTEDISP,1 - - CMP BYTE PTR [ESI],20H - JE SHORT JUSTDISPLAY - - MOV ZAPFLAG,0 - - CMP BYTE PTR [ESI],'Z' - JNE SHORT NOTZAP - - MOV ZAPFLAG,1 - JMP SHORT JUSTDISPLAY - - PUBLIC NOTZAP -NOTZAP: - - MOV ROUTEDISP,2 ; LOCK UPDATE - - CMP BYTE PTR [ESI],'!' - JE SHORT JUSTDISPLAY -; -; LOOK FOR V FOR ADDING A DIGI -; - CMP WORD PTR [ESI],' V' ; V [SPACE] - JE ADDDIGI - - CALL GETVALUE ; GET NUMBER, UP TO SPACE , CR OR OFFH - JC SHORT BADROUTECMD ; INVALID DIGITS - - MOV NEWROUTEVAL,AL - - MOV ROUTEDISP,0 - - CALL SCAN ; SEE IF ! - MOV AH,[ESI] - - - PUBLIC JUSTDISPLAY -JUSTDISPLAY: - - - MOV ESI,OFFSET32 AX25CALL - CALL _FINDNEIGHBOUR - JZ SHORT FOUNDROUTE ; IN LIST - OK - - CMP EBX,0 - JE SHORT BADROUTECMD ; TABLE FULL?? - - MOV ECX,7 - MOV EDI,EBX - REP MOVSB ; PUT IN CALL - - MOV AL,SAVEPORT - MOV NEIGHBOUR_PORT[EBX],AL - - JMP SHORT FOUNDROUTE - - - PUBLIC BADROUTECMD -BADROUTECMD: - - POP EDI - - JMP PBADVALUE - - PUBLIC FOUNDROUTE -FOUNDROUTE: - - CMP ZAPFLAG,1 - JNE SHORT NOTCLEARCOUNTS - - XOR AX,AX - MOV ES:WORD PTR NBOUR_IFRAMES[EDI],AX - MOV ES:WORD PTR NBOUR_IFRAMES+2[EDI],AX - MOV ES:WORD PTR NBOUR_RETRIES[EDI],AX - MOV ES:WORD PTR NBOUR_RETRIES+2[EDI],AX - - JMP SHORT NOUPDATE - - PUBLIC NOTCLEARCOUNTS -NOTCLEARCOUNTS: - - CMP ROUTEDISP,1 - JE SHORT NOUPDATE - - CMP ROUTEDISP,2 - JE SHORT LOCKUPDATE - - MOV AL,NEWROUTEVAL - MOV NEIGHBOUR_QUAL[EBX],AL - - CMP AH,'!' - JNE SHORT NOUPDATE - - PUBLIC LOCKUPDATE -LOCKUPDATE: - - XOR NEIGHBOUR_FLAG[EBX],1 ; FLIP LOCKED BIT - - PUBLIC NOUPDATE -NOUPDATE: - - MOV ESI,EBX - POP EDI - - POP EBX - CALL DISPLAYROUTE - - JMP SENDCOMMANDREPLY - - PUBLIC ADDDIGI -ADDDIGI: - - ADD ESI,2 - PUSH ESI ; SAVE INPUT BUFFER - - MOV ESI,OFFSET32 AX25CALL - CALL _FINDNEIGHBOUR - - POP ESI - - JZ SHORT ADD_FOUND ; IN LIST - OK - - JMP BADROUTECMD - - PUBLIC ADD_FOUND -ADD_FOUND: - - CALL CONVTOAX25 ; GET DIGI CALLSIGN - - PUSH ESI - - MOV ESI,OFFSET32 AX25CALL - LEA EDI,NEIGHBOUR_DIGI[EBX] - MOV ECX,7 - REP MOVSB - - POP ESI ; MSG BUFFER -; -; SEE IF ANOTHER DIGI -; - CMP BYTE PTR [ESI],20H - JE SHORT NOMORE - - CALL CONVTOAX25 ; GET DIGI CALLSIGN - MOV ESI,OFFSET32 AX25CALL - LEA EDI,NEIGHBOUR_DIGI+7[EBX] - MOV ECX,7 - REP MOVSB - - PUBLIC NOMORE -NOMORE: - - JMP NOUPDATE - - - -*/ - -SendReply: - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - -VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS LISTEN COMMAND - - // for monitoring a remote ax.25 port - - int Port = 0, index =0; - unsigned int ListenMask = 0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - char ListenPortList[128] = ""; - - ptr = strtok_s(CmdTail, " ,", &Context); - - // Now accepts a list of ports - - if (ptr == 0 || memcmp(ptr, "OFF", 3) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Listening disabled\r"); - Session->LISTEN = 0; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - while (ptr) - { - Port = atoi(ptr); - - if (Port == 0 && NUMBEROFPORTS == 1) - Port = 1; - - ptr = strtok_s(NULL, ", ", &Context); // Get Unproto String - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port %d\r", Port); - continue; - } - - if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is not an ax.25 port\r", Port); - continue; - } - - if (PORT->PORTL3FLAG) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is for internode traffic only\r", Port); - continue; - } - - if (Session->L4CIRCUITTYPE == L2LINK + UPLINK) - { - if (Session->L4TARGET.LINK->LINKPORT->PORTNUMBER == Port) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "You can't Listen to the port you are connected on\r"); - continue; - } - } - - sprintf(ListenPortList, "%s %d", ListenPortList, Port); - - - ListenMask |= (1 << (Port - 1)); - } - - Session->LISTEN = ListenMask; - - if (ListenMask) - { - if (CountBits(ListenMask) == 1) - Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on port%s. Use CQ to send a beacon, LIS to disable\r", ListenPortList); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on ports%s. Use LIS to disable\r", ListenPortList); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID UNPROTOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS UNPROTO COMMAND - - int Port = 0, index =0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - UCHAR axcalls[64]; - BOOL Stay, Spy; - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port == 0 && NUMBEROFPORTS == 1) - Port = 1; - else - ptr = strtok_s(NULL, " ", &Context); // Get Unproto String - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Destination missing\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - ptr[strlen(ptr)] = ' '; // Put param back together - - if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PORTL3FLAG) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Copy Address Info to Session Record - - Session->UNPROTO = Port; - Session->UAddrLen = (int)strlen(axcalls); - memcpy(Session->UADDRESS, axcalls, 63); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Unproto Mode - enter ctrl/z or /ex to exit\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -VOID CALCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS CAL COMMAND - - int Port = 0, index = 0, Count = 0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port == 0 && NUMBEROFPORTS == 1) - Port = 1; - else - ptr = strtok_s(NULL, " ", &Context); // Get Unproto String - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Count Missing\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Count = atoi(ptr); - - ptr = strtok_s(NULL, " ", &Context); // Get Unproto String - - Bufferptr = Cmdprintf(Session, Bufferptr, "Ok\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - - -VOID CQCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Send a CQ Beacon on a radio port. Must be in LISTEN state - - DIGIMESSAGE Msg; - int Port = 0; - int OneBits = 0; - unsigned int MaskCopy = Session->LISTEN; - int Len; - UCHAR CQCALL[7]; - char Empty[] = ""; - char * ptr1 = &OrigCmdBuffer[3]; - UCHAR * axptr = &Msg.DIGIS[0][0]; - char * ptr2, *Context; - - while (MaskCopy) - { - if (MaskCopy & 1) - OneBits++; - - Port++; - MaskCopy = MaskCopy >> 1; - } - - if (OneBits == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "You must enter LISTEN before calling CQ\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (OneBits > 1) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "You can't call CQ if LISTENing on more than one port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - Len = (int)strlen(OrigCmdBuffer) - 3; - - if (Len < 0) - Len = 0; - - memset(&Msg, 0, sizeof(Msg)); - - Msg.PORT = Port; - Msg.CTL = 3; // UI - - // see if a Via specified - - if (_memicmp(ptr1, "via ", 4) == 0) - { - ptr2 = strtok_s(ptr1 + 4, ",", &Context); - - while (ptr2) - { - if (ConvToAX25(ptr2, axptr) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid via string\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - axptr += 7; - - if (axptr == &Msg.DIGIS[7][0]) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Too many digis\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - ptr1 = ptr2; - ptr2 = strtok_s(NULL, ",", &Context); - } - - // ptr1 is start of last digi call. We need to position to data - - ptr1 = strchr(ptr1, ' '); - - if (ptr1 == NULL) - ptr1 = Empty; - else - ptr1++ ; // to message - - Len = (int)strlen(ptr1); - - } - - ConvToAX25("CQ", CQCALL); - memcpy(Msg.DEST, CQCALL, 7); - memcpy(Msg.ORIGIN, Session->L4USER, 7); - Msg.ORIGIN[6] ^= 0x1e; // Flip SSID - Msg.PID = 0xf0; // Data PID - memcpy(&Msg.L2DATA, ptr1, Len); - - Send_AX_Datagram(&Msg, Len + 2, Port); // Len is Payload ie CTL, PID and Data - - Bufferptr = Cmdprintf(Session, Bufferptr, "CQ sent\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - - -TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr) -{ - TRANSPORTENTRY * NewSess = L4TABLE; - int Index = 0; - - while (Index < MAXCIRCUITS) - { - if (NewSess->L4USER[0] == 0) - { - // Got One - - Session->L4CROSSLINK = NewSess; - NewSess->L4CROSSLINK = Session; - - memcpy(NewSess->L4USER, Session->L4USER, 7); - memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); - - - NewSess->CIRCUITINDEX = Index; //OUR INDEX - NewSess->CIRCUITID = NEXTID; - - NEXTID++; - if (NEXTID == 0) - NEXTID++; // kEEP nON-ZERO - - NewSess->SESSIONT1 = Session->SESSIONT1; - NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; - - return NewSess; - } - Index++; - NewSess++; - } - - if (Bufferptr) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - } - - return NULL; -} - - -VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy) -{ - TRANSPORTENTRY * NewSess; - - NewSess = SetupNewSession(Session, Bufferptr); - - if (NewSess == NULL) - return; // Tables Full - - NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK; - - NewSess->L4TARGET.DEST = Dest; - NewSess->L4STATE = 2; // CONNECTING - - NewSess->SPYFLAG = Spy; - - ReleaseBuffer((UINT *)REPLYBUFFER); - - SENDL4CONNECT(NewSess); - - L4CONNECTSOUT++; - - return; -} - -BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK) -{ - struct _LINKTABLE * LINK = LINKS; - struct _LINKTABLE * FIRSTSPARE = NULL; - int n = MAXLINKS; - - while (n--) - { - if (LINK->LINKCALL[0] == 0) // Spare - { - if (FIRSTSPARE == NULL) - FIRSTSPARE = LINK; - - LINK++; - continue; - } - - if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall)) - { - *REQLINK = LINK; - return TRUE; - } - - LINK++; - } - // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL - - *REQLINK = FIRSTSPARE; - return FALSE; -} - -VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); - -VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS CONNECT COMMAND - - TRANSPORTENTRY * NewSess; - - int CONNECTPORT, Port; - BOOL CallEvenIfInNodes = FALSE; - char * ptr, *Context; - UCHAR axcalls[64]; - UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted - int ret; - struct PORTCONTROL * PORT = PORTTABLE; - struct _LINKTABLE * LINK; - int CQFLAG = 0; // NOT CQ CALL - BOOL Stay, Spy; - int n; - char TextCall[10]; - int TextCallLen; - char PortString[10]; - char cmdCopy[256]; - struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;; - - -#ifdef EXCLUDEBITS - - if (CheckExcludeList(Session->L4USER) == FALSE) - { - // CONNECTS FROM THIS STATION ARE NOT ALLOWED - - ReleaseBuffer((UINT *)REPLYBUFFER); - return; - } - -#endif - - if (Session->LISTEN) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Can't connect while listening\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - CONNECTPORT = 0; // NO PORT SPECIFIED - - ptr = strtok_s(CmdTail, " ", &Context); - - strcpy(cmdCopy, Context); // Save in case Telnet Connect - - if (ptr == 0) - { - // No param - - if (CFLAG) // C Command Disabled ? - { - // Convert to HOST (appl 32) command - - //MOV _CMDPTR,OFFSET32 _HOSTCMD - //MOV _ALIASPTR,OFFSET32 _HOSTCMD + 32 * 31 - - //MOV _APPLMASK, 80000000H ; Internal Term - - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Port = atoi(ptr); - - if (Port) - { - // IF THERE IS NOTHING FOLLOWING THE NUMBER, ASSUME IT IS A - // NUMERIC ALIAS INSTEAD OF A PORT - - sprintf(PortString, "%d", Port); - - if (strlen(PortString) < (int)strlen(ptr)) - goto NoPort; - - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - EXTPORT = (struct _EXTPORTDATA *)PORT; - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr == 0) - { - // No param - - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - CONNECTPORT = Port; - - if (strcmp(ptr, "CMS") == 0 || strcmp(ptr, "HOST") == 0) // In case someeone has CMS or HOST as an alias - goto Downlink; - - } - -NoPort: - - ptr[strlen(ptr)] = ' '; // Put param back together - - if (ptr[0] == '!') - { - CallEvenIfInNodes = TRUE; - ptr++; - } - - if (memcmp(ptr, "RELAY ", 5) == 0 || memcmp(ptr, "SYNC ", 5) == 0) - { - // c p relay with extra parms - - goto Downlink; - } - - // Skip call validation if using a ptc to allow 1:call, 2:call format - - if (Port && PORT->PROTOCOL == 10 && memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) - { - char * p; - - if (p = strstr(cmdCopy, " S ")) - { - Stay = TRUE; - p++; - *p = ' '; - } - - if (p = strstr(cmdCopy, " Z ")) - { - Spy = TRUE; - p++; - *p = ' '; - } - - goto Downlink; - } - else - { - if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - Session->STAYFLAG = Stay; - - TextCallLen = ConvFromAX25(axcalls, TextCall); - - if (CallEvenIfInNodes) - goto Downlink; - - // SEE IF CALL TO ANY OF OUR HOST SESSIONS - UNLESS DIGIS SPECIFIED - - if (axcalls[7] == 0) - { - // If this connect is as a result of a command alias, don't check appls or we will loop - - if (ALIASINVOKED == 0) - { - APPLCALLS * APPL = APPLCALLTABLE; - int n = NumberofAppls; - APPLMASK = 1; - - while (n--) - { - if (memcmp(axcalls, APPL->APPLALIAS, 6) == 0 || CompareCalls(axcalls, APPL->APPLCALL)) - { - // Call to an appl - - // Convert to an APPL command, so any alias is actioned - - // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND - - if (APPL->APPLHASALIAS && APPL->APPLALIASVAL[0] != 0x20) - { - // COPY ALIAS TO COMMAND _BUFFER, THEN REENTER COMMAND HANDLER - - memcpy(COMMANDBUFFER, APPL->APPLALIASVAL, ALIASLEN); - COMMANDBUFFER[80] = 0; - _strupr(COMMANDBUFFER); - memcpy(OrigCmdBuffer, APPL->APPLALIASVAL, ALIASLEN); // In case original case version needed - - ALIASINVOKED = TRUE; // To prevent Alias Loops - } - else - { - - // Copy Appl Command to Command Buffer. Ensure doesn't contain old command - - memset(COMMANDBUFFER, ' ', 72); - memcpy(COMMANDBUFFER, APPL->APPLCMD, 12); - } - DoTheCommand(Session); - return; - } - APPL++; - APPLMASK <<= 1; - } - } - } - - if (axcalls[7] == 0) - { - // SEE IF CALL TO ANOTHER NODE - - struct DEST_LIST * Dest = DESTS; - int n = MAXDESTS; - - if (axcalls[6] == 0x60) // if SSID, dont check aliases - { - while (n--) - { - if (memcmp(Dest->DEST_ALIAS, TextCall, 6) == 0) - { - DoNetromConnect(Session, Bufferptr, Dest, Spy); - return; - } - Dest++; - } - } - - Dest = DESTS; - n = MAXDESTS; - - while (n--) - { - if (CompareCalls(Dest->DEST_CALL, axcalls)) - { - DoNetromConnect(Session, Bufferptr, Dest, Spy); - return; - } - Dest++; - } - } - - // Must be Downlink Connect - -Downlink: - - if (CONNECTPORT == 0 && NUMBEROFPORTS > 1) - { - // L2 NEEDS PORT NUMBER - - Bufferptr = Cmdprintf(Session, Bufferptr, "Downlink connect needs port number - C P CALLSIGN\r"); - - // Send Port List - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // ENSURE PORT IS AVAILABLE FOR L2 USE - - if (PORT->PROTOCOL >= 10) // Pactor=-style port? - { - int count; - - // if Via PACTOR ARDOP WINMOR or VARA, convert to attach and call = Digi's are in AX25STRING (+7) - - if (memcmp(&axcalls[7], &WINMOR[0], 6) == 0 || - memcmp(&axcalls[7], &ARDOP[0], 6) == 0 || - memcmp(&axcalls[7], &VARA[0], 6) == 0 || - memcmp(&axcalls[7], &PACTORCALL[0], 6) == 0) - { - char newcmd[80]; - - TextCall[TextCallLen] = 0; - sprintf(newcmd, "%s %s", CmdTail, TextCall); - - ATTACHCMD(Session, Bufferptr, newcmd, NULL); - return; - } - - // If on a KAM or SCS with ax.25 on port 2, do an Attach command, then pass on connect - - if (EXTPORT->MAXHOSTMODESESSIONS <= 1) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set - - if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - count = EXTPORT->MAXHOSTMODESESSIONS; - count--; // First is Pactor Stream, count is now last ax.25 session - - while (count) - { - if (EXTPORT->ATTACHEDSESSIONS[count] == 0) - { - int Paclen, PortPaclen; - struct DATAMESSAGE * Buffer; - struct DATAMESSAGE Message = {0}; - char Callstring[80]; - int len; - - // Found a free one - use it - - // See if TNC is OK - - Message.PORT = count; - - ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); - - if ((ret & 0xff00) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK - - NewSess = SetupNewSession(Session, Bufferptr); - if (NewSess == NULL) - return; - - // if a UZ7HO port, and the uplink is L2 or Uz7HO invert SSID bits - - // We only get here if multisession - - if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip; - - if ((Session->L4CIRCUITTYPE & BPQHOST))// host - goto noFlip; - - if ((Session->L4CIRCUITTYPE & PACTOR)) - { - // incoming is Pactorlike - see if UZ7HO - - if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip; - else - NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip - } - else - - // Must be L2 uplink - flip - - NewSess->L4USER[6] ^= 0x1e; // Flip SSID -noFlip: - EXTPORT->ATTACHEDSESSIONS[count] = NewSess; - - NewSess->KAMSESSION = count; - - // Set paclen to lower of incoming and outgoing - - Paclen = Session->SESSPACLEN; // Incoming PACLEN - - if (Paclen == 0) - Paclen = 256; // 0 = 256 - - PortPaclen = PORT->PORTPACLEN; - - if (PortPaclen == 0) - PortPaclen = 256; // 0 = 256 - - if (PortPaclen < Paclen) - Paclen = PortPaclen; - - NewSess->SESSPACLEN = Paclen; - Session->SESSPACLEN = Paclen; - - NewSess->L4STATE = 5; - NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; - NewSess->L4TARGET.PORT = PORT; - - // Send the connect command to the TNC - - Buffer = REPLYBUFFER; - - Buffer->PORT = count; - Buffer->PID = 0xf0; - - // if on Telnet Port convert use original cmd tail - - // Why just on telnet - what not all ports?? - - if (memcmp(EXTPORT->PORT_DLL_NAME, "TELNET", 6) == 0 || memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) - { - NewSess->Secure_Session = Session->Secure_Session; - len = sprintf(Callstring,"C %s", cmdCopy); - } - else - { - TextCall[TextCallLen] = 0; - - len = sprintf(Callstring,"C %s", TextCall); - - if (axcalls[7]) - { - int digi = 7; - - // we have digis - - len += sprintf(&Callstring[len], " via"); - - while (axcalls[digi]) - { - TextCall[ConvFromAX25(&axcalls[digi], TextCall)] = 0; - len += sprintf(&Callstring[len], " %s", TextCall); - digi += 7; - } - } - } - Callstring[len++] = 13; - Callstring[len] = 0; - - Buffer->LENGTH = len + MSGHDDRLEN + 1; - memcpy(Buffer->L2DATA, Callstring, len); - C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); - - return; - } - count--; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if ((Session->L4CIRCUITTYPE & BPQHOST) == 0 && PORT->PORTL3FLAG) - { - //Port only for L3 - - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PortUIONLY) - { - //Port only for UI - - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for UI traffic only\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - Can't make ax.25 calls with SSID of T or R\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Get Session Entry for Downlink - - NewSess = SetupNewSession(Session, Bufferptr); - if (NewSess == NULL) - return; - - NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK; - - // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION - - memcpy(ourcall, NewSess->L4USER, 7); - - // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO) - - if ((Session->L4CIRCUITTYPE & BPQHOST))// host - goto noFlip3; - - if ((Session->L4CIRCUITTYPE & PACTOR)) - { - // incoming is Pactorlike - see if UZ7HO - - if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip3; - - if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession - goto noFlip3; - - ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip - } - else - - // Must be L2 uplink - flip - - ourcall[6] ^= 0x1e; // Flip SSID - -noFlip3: - - // SET UP NEW SESSION (OR RESET EXISTING ONE) - - FindLink(axcalls, ourcall, Port, &LINK); - - if (LINK == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - // Should release NewSess - - return; - } - - memcpy(LINK->LINKCALL, axcalls, 7); - memcpy(LINK->OURCALL, ourcall, 7); - - LINK->LINKPORT = PORT; - - LINK->L2TIME = PORT->PORTT1; - - // Copy Digis - - n = 7; - ptr = &LINK->DIGIS[0]; - - while (axcalls[n]) - { - memcpy(ptr, &axcalls[n], 7); - n += 7; - ptr += 7; - - LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI - } - - LINK->LINKTYPE = 2; // DOWNLINK - LINK->LINKWINDOW = PORT->PORTWINDOW; - - RESET2(LINK); // RESET ALL FLAGS - - if (CMD->String[0] == 'N' && SUPPORT2point2) - LINK->L2STATE = 1; // New (2.2) send XID - else - LINK->L2STATE = 2; // Send SABM - - LINK->CIRCUITPOINTER = NewSess; - - NewSess->L4TARGET.LINK = LINK; - - if (PORT->PORTPACLEN) - NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN; - - if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM - { - if (LINK->L2STATE == 1) - L2SENDXID(LINK); - else - SENDSABM(LINK); - } - ReleaseBuffer((UINT *)REPLYBUFFER); - return; -} - -BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls) -{ - // CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN - // CONVERTED STRING IN AXCALLS. Return FALSE if invalied - - char * axptr = AXCalls; - char * ptr, *Context; - int CQFLAG = 0; // NOT CQ CALL - int n = 8; // Max digis - - *Stay = 0; - *Spy = 0; - - memset(AXCalls, 0, 64); - - ptr = strtok_s(Calls, " ,", &Context); - - if (ptr == NULL) - return FALSE; - - // First field is Call - - if (ConvToAX25(ptr, axptr) == 0) - return FALSE; - - axptr += 7; - - ptr = strtok_s(NULL, " ,", &Context); - - while (ptr && n--) - { - // NEXT FIELD = COULD BE CALLSIGN, VIA, OR S (FOR STAY) - - if (strcmp(ptr, "S") == 0) - *Stay = TRUE; - else if (strcmp(ptr, "Z") == 0) - *Spy = TRUE; - else if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0) - { - } //skip via - else - { - // Convert next digi - - if (ConvToAX25(ptr, axptr) == 0) - return FALSE; - - axptr += 7; - } - - ptr = strtok_s(NULL, " ,", &Context); - } - - return TRUE; -} - - -VOID LINKCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS *** LINKED to CALLSIGN - - char * ptr, *Context; - UCHAR axcall[7]; - int ret; - - if (LINKEDFLAG == 'Y' || // UNCONDITIONAL? - (LINKEDFLAG == 'A' && - ((Session->L4CIRCUITTYPE & BPQHOST) || Session->Secure_Session || Session->PASSWORD == 0xffff))) - { - ptr = strtok_s(CmdTail, " ", &Context); - if (ptr) - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - { - ret = ConvToAX25Ex(ptr, axcall); - - if (ret) - { - memcpy(Session->L4USER, axcall, 7); - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - strcpy(Bufferptr, BADMSG); - Bufferptr += (int)strlen(BADMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - memcpy(Bufferptr, PASSWORDMSG, LPASSMSG); - Bufferptr += LPASSMSG; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -int CompareNode(const void *a, const void *b); -int CompareAlias(const void *a, const void *b); - -char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest) -{ - char Normcall[10]; - char Alias[10]; - struct NR_DEST_ROUTE_ENTRY * NRRoute; - struct DEST_ROUTE_ENTRY * Route; - struct ROUTE * Neighbour; - int i, Active, len; - - Alias[6] = 0; - - memcpy(Alias, Dest->DEST_ALIAS, 6); - strlop(Alias, ' '); - - Normcall[ConvFromAX25(Dest->DEST_CALL, Normcall)] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Routes to: %s:%s", Alias, Normcall); - - if (Dest->DEST_COUNT) - Bufferptr = Cmdprintf(Session, Bufferptr, " RTT=%4.2f FR=%d %c %.1d\r", - Dest->DEST_RTT /1000.0, Dest->DEST_COUNT, - (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - NRRoute = &Dest->NRROUTE[0]; - - Active = Dest->DEST_ROUTE; - - for (i = 1; i < 4; i++) - { - Neighbour = NRRoute->ROUT_NEIGHBOUR; - - if (Neighbour) - { - len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); - Normcall[len] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %d %d %s\r", - (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); - } - NRRoute++; - } - - // DISPLAY INP3 ROUTES - - Route = &Dest->ROUTE[0]; - - Active = Dest->DEST_ROUTE; - - for (i = 1; i < 4; i++) - { - Neighbour = Route->ROUT_NEIGHBOUR; - - if (Neighbour) - { - double srtt = Route->SRTT/1000.0; - - len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); - Normcall[len] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %4.2fs %d %s\r", - (Active == i + 3)?'>':' ',Route->Hops, srtt, Neighbour->NEIGHBOUR_PORT, Normcall); - } - Route++; - } - - return Bufferptr; -} - - -int DoViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) -{ - char Portcall[10]; - int len; - - if (Dest->NRROUTE[n].ROUT_NEIGHBOUR != 0 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 0) - { - len=ConvFromAX25(Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); - Portcall[len]=0; - - len=sprintf(&line[cursor],"%s %d %d ", - Portcall, - Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, - Dest->NRROUTE[n].ROUT_QUALITY); - - cursor+=len; - - if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) - { - len=sprintf(&line[cursor],"! "); - cursor+=len; - } - } - return cursor; -} - -int DoINP3ViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) -{ - char Portcall[10]; - int len; - double srtt; - - if (Dest->ROUTE[n].ROUT_NEIGHBOUR != 0) - { - srtt = Dest->ROUTE[n].SRTT/1000.0; - - len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); - Portcall[len]=0; - - len=sprintf(&line[cursor],"%s %d %d %4.2fs ", - Portcall, - Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, - Dest->ROUTE[n].Hops, srtt); - - cursor+=len; - - if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) - { - len=sprintf(&line[cursor],"! "); - cursor+=len; - } - } - return cursor; -} - -int WildCmp(char * pattern, char * string) -{ - // Check if string is at end or not. - - if (*pattern == '\0') - return *string == '\0'; - - // Check for single character missing or match - - if (*pattern == '?' || *pattern == *string) - return *string != '\0' && WildCmp(pattern + 1, string + 1); - - if (*pattern == '*') - { - // Check for multiple character missing - - return WildCmp(pattern + 1, string) || (*string != '\0' && WildCmp(pattern, string + 1)); - } - - return 0; -} - -VOID CMDN00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - struct DEST_LIST * Dest = DESTS; - int count = MAXDESTS, i; - char Normcall[10]; - char Alias[10]; - int Width = 4; - int x = 0, n = 0; - struct DEST_LIST * List[1000]; - char Param = 0; - char * ptr, * param2,* Context; - char Nodeline[21]; - char AXCALL[7]; - char * Call; - char * Qualptr; - int Qual; - char line[160]; - int cursor, len; - UCHAR axcall[7]; - int SavedOBSINIT = OBSINIT; - struct ROUTE * ROUTE = NULL; - char Pattern[80] = ""; - char * firststar; - int minqual = 0; - - ptr = strtok_s(CmdTail, " ", &Context); - param2 = strtok_s(NULL, " ", &Context); - - if (ptr) - { - if (strcmp(ptr, "ADD") == 0) - goto NODE_ADD; - - if (strcmp(ptr, "DEL") == 0) - goto NODE_DEL; - - if (strcmp(ptr, "VIA") == 0) - goto NODE_VIA; - } - - if (ptr) - { - // Could be C or a pattern. Accept C pattern or pattern C - - if ((int)strlen(ptr) > 1) - { - strcpy(Pattern, ptr); - if (param2 && param2[0] == 'C') - Param = 'C'; - } - else - { - Param = ptr[0]; - if (param2) - strcpy(Pattern, param2); - } - } - - // Pattern >nnn selects nodes with at least that quality - - if (Pattern[0] == '>') - { - minqual = atoi(&Pattern[1]); - Pattern[0] = 0; - } - - // We need to pick out CALL or CALL* from other patterns (as call use detail display) - - firststar = strchr(Pattern, '*'); - - if ((firststar && *(firststar + 1) != 0)|| strchr(Pattern, '?')) //(* not on end) - - // definitely pattern - - goto DoNodePattern; - - // If it works as CALL*, process, else drop through - - if (Pattern[0]) - { - UCHAR AXCall[8]; - int count; - int paramlen = (int)strlen(ptr); - char parampadded[20]; - int n = 0; - - Alias[8] = 0; - strcpy(parampadded, Pattern); - strcat(parampadded, " "); - - ConvToAX25(Pattern, AXCall); - - // if * on end, list all ssids - - if (firststar) - { - AXCall[6] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - while (AXCall[6] < 32) - { - Dest = DESTS; - - for (count = 0; count < MAXDESTS; count++) - { - if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) - { - break; - } - Dest++; - } - - if (count < MAXDESTS) - { - Bufferptr = DoOneNode(Session, Bufferptr, Dest); - n++; - } - - AXCall[6] += 2; - } - - if (n) // Found Some - { - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Dest = DESTS; // Reset - - // Drop through to try as pattern - } - else - { - // process as just call - - for (count = 0; count < MAXDESTS; count++) - { - if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) - { - break; - } - Dest++; - } - - if (count == MAXDESTS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Bufferptr = DoOneNode(Session, Bufferptr, Dest); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - -DoNodePattern: - - Bufferptr = Cmdprintf(Session, Bufferptr, "Nodes\r"); - - while (count--) - { - if (Dest->DEST_CALL[0] != 0) - { - if (Dest->NRROUTE->ROUT_QUALITY >= minqual) - if (Param != 'T' || Dest->DEST_COUNT) - List[n++] = Dest; - - if (n > 999) - break; - } - Dest++; - } - - if (Param == 'C') - qsort(List, n, sizeof(void *), CompareNode); - else - qsort(List, n, sizeof(void *), CompareAlias); - - - for (i = 0; i < n; i++) - { - int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); - Normcall[len]=0; - - memcpy(Alias, List[i]->DEST_ALIAS, 6); - Alias[6] = 0; - strlop(Alias, ' '); - - if (strlen(Alias)) - strcat(Alias, ":"); - - if (Alias[0] == '#' && HIDENODES == 1 && Param != '*') // Hidden Node and not N * command - continue; - - if (Pattern[0]) - if (!WildCmp(Pattern, Normcall) && !WildCmp(Pattern, Alias)) - continue; - - if (Param == 'T') - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s RTT=%4.2f Frames = %d %c %.1d\r", - Alias, Normcall, List[i]->DEST_RTT /1000.0, List[i]->DEST_COUNT, - (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); - } - else - { - len = sprintf(Nodeline, "%s%s", Alias, Normcall); - memset(&Nodeline[len], ' ', 20 - len); - Nodeline[20] = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Nodeline); - - if (++x == Width) - { - x = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - } - } - } - - if (x) - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - goto SendReply; - - -NODE_VIA: - - // List Nodes reachable via a neighbour - - ptr = param2; - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); - goto SendReply; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - ConvToAX25(ptr, AXCALL); - - Dest = DESTS; - - Dest-=1; - - for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[0].ROUT_NEIGHBOUR == 0) - continue; - - - if ((Dest->NRROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->NRROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->NRROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - - || (Dest->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))) - { - len=ConvFromAX25(Dest->DEST_CALL,Normcall); - - Normcall[len]=0; - - memcpy(Alias,Dest->DEST_ALIAS,6); - - Alias[6]=0; - - for (i=0;i<6;i++) - { - if (Alias[i] == ' ') - Alias[i] = 0; - } - - cursor=sprintf(line,"%s:%s ", Alias,Normcall); - - cursor = DoViaEntry(Dest, 0, line, cursor); - cursor = DoViaEntry(Dest, 1, line, cursor); - cursor = DoViaEntry(Dest, 2, line, cursor); - cursor = DoINP3ViaEntry(Dest, 0, line, cursor); - cursor = DoINP3ViaEntry(Dest, 1, line, cursor); - cursor = DoINP3ViaEntry(Dest, 2, line, cursor); - - line[cursor++]='\r'; - line[cursor++]=0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", line); - } - } - - - goto SendReply; - -NODE_ADD: - - // FORMAT IS NODE ADD ALIAS:CALL QUAL ROUTE PORT - - - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - goto SendReply; - } - - ptr = param2; - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); - goto SendReply; - } - - Call = strlop(ptr, ':'); - - if (Call == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); - goto SendReply; - } - - - ConvToAX25(Call, AXCALL); - - Qualptr = strtok_s(NULL, " ", &Context); - - if (Qualptr == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Quality missing\r"); - goto SendReply; - } - - Qual = atoi(Qualptr); - - if (Qual < MINQUAL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Quality is below MINQUAL\r"); - goto SendReply; - } - - if (FindDestination(AXCALL, &Dest)) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Node already in Table\r"); - goto SendReply; - } - - if (Dest == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Table Full\r"); - goto SendReply; - } - - memcpy(Dest->DEST_CALL, AXCALL, 7); - memcpy(Dest->DEST_ALIAS, ptr, 6); - - NUMBEROFNODES++; - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr == NULL || ptr[0] == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Neighbour missing\r"); - goto SendReply; - } - - if (ConvToAX25(ptr, axcall) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Neighbour\r"); - goto SendReply; - } - else - { - int Port; - - ptr = strtok_s(NULL, " ", &Context); - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Port missing\r"); - goto SendReply; - } - - Port = atoi(ptr); - - if (Context[0] == '!') - { - OBSINIT = 255; //; SPECIAL FOR LOCKED - } - - if (FindNeighbour(axcall, Port, &ROUTE)) - { - PROCROUTES(Dest, ROUTE, Qual); - } - - OBSINIT = SavedOBSINIT; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Added\r"); - goto SendReply; - } - - - - -/* -PNODE48: - - -; GET NEIGHBOURS FOR THIS DESTINATION -; - CALL CONVTOAX25 - JNZ SHORT BADROUTE -; - CALL GETVALUE - MOV SAVEPORT,AL ; SET PORT FOR _FINDNEIGHBOUR - - CALL GETVALUE - MOV ROUTEQUAL,AL -; - MOV ESI,OFFSET32 AX25CALL - - PUSH EBX ; SAVE DEST - CALL _FINDNEIGHBOUR - MOV EAX,EBX ; ROUTE TO AX - POP EBX - - JZ SHORT NOTBADROUTE - - JMP SHORT BADROUTE - -NOTBADROUTE: -; -; UPDATE ROUTE LIST FOR THIS DEST -; - MOV ROUT1_NEIGHBOUR[EBX],EAX - MOV AL,ROUTEQUAL - MOV ROUT1_QUALITY[EBX],AL - MOV ROUT1_OBSCOUNT[EBX],255 ; LOCKED -; - POP EDI - POP EBX - - INC _NUMBEROFNODES - - JMP SENDOK - -BADROUTE: -; -; KILL IT -; - MOV ECX,TYPE DEST_LIST - MOV EDI,EBX - MOV AL,0 - REP STOSB - - JMP BADROUTECMD - -*/ - - goto SendReply; - - -NODE_DEL: - - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - goto SendReply; - } - - ptr = param2; - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); - goto SendReply; - } - - if (strcmp(ptr, "ALL") == 0) - { - struct DEST_LIST * DEST = DESTS; - int n = MAXDESTS; - - while (n--) - { - if (DEST->DEST_CALL[0] && ((DEST->DEST_STATE & 0x80) == 0)) // Don't delete appl node - REMOVENODE(DEST); - - DEST++; - } - - ClearNodes(); - - Bufferptr = Cmdprintf(Session, Bufferptr, "All Nodes Deleted\r"); - goto SendReply; - } - - ConvToAX25(ptr, AXCALL); - - if (FindDestination(AXCALL, &Dest) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); - goto SendReply; - } - - if (Dest->DEST_STATE & 0x80) - Bufferptr = Cmdprintf(Session, Bufferptr, "APPL Node - Can't delete\r"); - else - { - REMOVENODE(Dest); - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); - } - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); - -SendReply: - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDQUERY(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // DISPLAY AVAILABLE COMMANDS - - int n; - char * ptr; - char ApplList[2048]; - char * out = ApplList; - - CMDX * CMD = &COMMANDS[APPL1]; - - for (n = 0; n < NumberofAppls; n++) - { - ptr = &CMD->String[0]; - if (*(ptr) != '*') - { - while (*ptr != ' ') - { - *(out++) = *(ptr++); - } - *(out++) = ' '; - } - CMD++; - } - - *(out) = 0; - - n = CMDLISTLEN; - - if (NEEDMH == 0) - n -= 7; // Dont show MH - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s\r", ApplList, CMDLIST); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -char * FormatMH(MHSTRUC * MH, char Format); - -VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // DISPLAY HEARD LIST - - int Port = 0, sess = 0; - char * ptr, *Context, *pattern; - struct PORTCONTROL * PORT = NULL; - MHSTRUC * MH; - int count = MHENTRIES; - int n; - char Normcall[20]; - char From[10]; - char DigiList[100]; - char * Output; - int len; - char Digi = 0; - - - // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find - // how many digis there are - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - pattern = strtok_s(NULL, " ", &Context); - - if (pattern) - _strupr(pattern); // Optional filter - - MH = PORT->PORTMHEARD; - - if (MH == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "MHEARD not enabled on that port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (pattern && strstr(pattern, "CLEAR")) - { - if (Session->Secure_Session) - { - memset(MH, 0, MHENTRIES * sizeof(MHSTRUC)); - SaveMH(); - Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d Cleared\r", Port); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "MH Clear needs SYSOP status\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - else - { - if (CMD->String[2] == 'V') // MHV - { - Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port); - Bufferptr = Cmdprintf(Session, Bufferptr, "Callsign Last heard Pkts RX via Digi ;) \r"); - Bufferptr = Cmdprintf(Session, Bufferptr, "--------- ----------- ------- ------------------------------------------\r"); - } - else - if (pattern) - Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d filtered by %s\r", Port, pattern); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d\r", Port); - } - while (count--) - { - if (MH->MHCALL[0] == 0) - break; - - Digi = 0; - - len = ConvFromAX25(MH->MHCALL, Normcall); - - Normcall[len++] = MH->MHDIGI; - Normcall[len++] = 0; - - if (pattern && strstr(Normcall, pattern) == 0) - { - MH++; - continue; - } - - n = 8; // Max number of digi-peaters - - ptr = &MH->MHCALL[6]; // End of Address bit - - Output = &DigiList[0]; - - if ((*ptr & 1) == 0) - { - // at least one digi - - strcpy(Output, "via "); - Output += 4; - - while ((*ptr & 1) == 0) - { - // MORE TO COME - - From[ConvFromAX25(ptr + 1, From)] = 0; - Output += sprintf((char *)Output, "%s", From); - - ptr += 7; - n--; - - if (n == 0) - break; - - // See if digi actioned - put a * on last actioned - - if (*ptr & 0x80) - { - if (*ptr & 1) // if last address, must need * - { - *(Output++) = '*'; - Digi = '*'; - } - - else - if ((ptr[7] & 0x80) == 0) // Repeased by next? - { - *(Output++) = '*'; // No, so need * - Digi = '*'; - } - -} - *(Output++) = ','; - } - *(--Output) = 0; // remove last comma - } - else - *(Output) = 0; - - // if we used a digi set * on call and display via string - - - if (Digi) - Normcall[len++] = Digi; - else - DigiList[0] = 0; // Dont show list if not used - - Normcall[len++] = 0; - - - ptr = FormatMH(MH, CMD->String[2]); - - if (CMD->String[2] == 'V') // MHV - Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %-10s %-10d %-30s\r", - Normcall, ptr, MH->MHCOUNT, DigiList); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %s %s\r", Normcall, ptr, DigiList); - - MH++; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -int Rig_Command(int Session, char * Command); - -VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - char * ptr; - - if (Rig_Command(Session->CIRCUITINDEX, CmdTail)) - { - ReleaseBuffer((UINT *)REPLYBUFFER); - return; - } - - // Error Message is in buffer - - ptr = strchr(CmdTail, 13); - - if (ptr) - { - int len = (int)(++ptr - CmdTail); - - memcpy(Bufferptr, CmdTail, len); - Bufferptr += len; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - -VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session); - - -VOID NRRCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // PROCESS 'NRR - Netrom Record Route' COMMAND - - char * ptr, *Context; - struct DEST_LIST * Dest = DESTS; - int count = MAXDESTS; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - UCHAR AXCall[8]; - int count; - - ConvToAX25(ptr, AXCall); - strcat(ptr, " "); - - for (count = 0; count < MAXDESTS; count++) - { - if (memcmp(Dest->DEST_ALIAS, ptr, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) - { - SendNRRecordRoute(Dest, Session); - memcpy(Bufferptr, OKMSG, 3); - Bufferptr += 3; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - } - Dest++; - } - } - Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -int CHECKINTERLOCK(struct PORTCONTROL * OURPORT) -{ - // See if any Interlocked ports are Busy - - struct PORTCONTROL * PORT = PORTTABLE; - struct _EXTPORTDATA * EXTPORT; - - int n = NUMBEROFPORTS; - int ourgroup = OURPORT->PORTINTERLOCK; - - while (PORT) - { - if (PORT != OURPORT) - { - if (PORT->PORTINTERLOCK == ourgroup) - { - // Same Group - is it busy - - int i = 0; - - EXTPORT = (struct _EXTPORTDATA *)PORT; - - while (i < 27) - if (EXTPORT->ATTACHEDSESSIONS[i++]) - return PORT->PORTNUMBER; - } - } - PORT = PORT->PORTPOINTER; - } - - return 0; -} - -VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // ATTACH to a PACTOR or similar port - - TRANSPORTENTRY * NewSess; - struct _EXTPORTDATA * EXTPORT; - struct TNCINFO * TNC; - - int Port = 0, sess = 0; - char * ptr, *Context; - int ret; - struct PORTCONTROL * PORT = NULL; - struct DATAMESSAGE Message = {0}; - int Paclen, PortPaclen; - struct DATAMESSAGE * Buffer; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL || PORT->PROTOCOL < 10) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // If attach on telnet port, find a free stream - - EXTPORT = (struct _EXTPORTDATA *)PORT; - - if (strstr(EXTPORT->PORT_DLL_NAME, "TELNET")) - { - int count = EXTPORT->MAXHOSTMODESESSIONS; - count--; // First is Pactor Stream, count is now last ax.25 session - - while (count) - { - if (EXTPORT->ATTACHEDSESSIONS[count] == 0) - { - int Paclen, PortPaclen; - struct DATAMESSAGE Message = {0}; - - // Found a free one - use it - - // See if TNC is OK - - Message.PORT = count; - - ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); - - if ((ret & 0xff00) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK - - NewSess = SetupNewSession(Session, Bufferptr); - - if (NewSess == NULL) - return; - - EXTPORT->ATTACHEDSESSIONS[count] = NewSess; - - NewSess->Secure_Session = Session->Secure_Session; - - NewSess->KAMSESSION = count; - - // Set paclen to lower of incoming and outgoing - - Paclen = Session->SESSPACLEN; // Incoming PACLEN - - if (Paclen == 0) - Paclen = 256; // 0 = 256 - - PortPaclen = PORT->PORTPACLEN; - - if (PortPaclen == 0) - PortPaclen = 256; // 0 = 256 - - if (PortPaclen < Paclen) - Paclen = PortPaclen; - - NewSess->SESSPACLEN = Paclen; - Session->SESSPACLEN = Paclen; - - NewSess->L4STATE = 5; - NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; - NewSess->L4TARGET.PORT = PORT; - - ptr = strtok_s(NULL, " ", &Context); - sess = count; - - // Replace command tail with original (before conversion to upper case - - Context = Context + (OrigCmdBuffer - COMMANDBUFFER); - - goto checkattachandcall; - - - memcpy(Bufferptr, OKMSG, 3); - Bufferptr += 3; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - } - count--; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Message.PORT = 0; - - ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); - - if ((ret & 0xff00) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // See if "Attach and Call" (for VHF ports) - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr && strcmp(ptr, "S") == 0) - { - Session->STAYFLAG = TRUE; - ptr = strtok_s(NULL, " ", &Context); - } - - if (ptr) - { - // we have another param - - // if it is a single char it is a channel number for vhf attach - - if (strlen(ptr) == 1) - { - // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set - - if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - sess = ptr[0] - '@'; - - if (sess < 1 || sess > EXTPORT->MAXHOSTMODESESSIONS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Invalid Channel\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr && strcmp(ptr, "S") == 0) - { - Session->STAYFLAG = TRUE; - ptr = strtok_s(NULL, " ", &Context); - } - } - } - - if (ret & 0x8000) // Disconnecting - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Check Interlock. Only ports with a TNC record can be interlocked - - TNC = PORT->TNC; - - if (TNC) - { - // See if any interlocked ports are in use - - struct TNCINFO * OtherTNC; - int i; - int rxInterlock = TNC->RXRadio; - int txInterlock = TNC->TXRadio; - - if (rxInterlock || txInterlock) - { - for (i=1; i<33; i++) - { - OtherTNC = TNCInfo[i]; - - if (OtherTNC == NULL) - continue; - - if (OtherTNC == TNC) - continue; - - if (rxInterlock == OtherTNC->RXRadio || txInterlock == OtherTNC->TXRadio) // Same Group - { - int n; - - for (n = 0; n <= 26; n++) - { - if (OtherTNC->PortRecord->ATTACHEDSESSIONS[n]) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, interlocked port %d is in use\r", i); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - } - } - } - } - - - - - if (EXTPORT->ATTACHEDSESSIONS[sess]) - { - // In use - - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK - - NewSess = SetupNewSession(Session, Bufferptr); - - if (NewSess == NULL) - return; - - // if a UZ7HO port, and the uplink is L2 or Uz7HO and multisession, - // invert SSID bits - - if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip1; - - if (EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession - goto noFlip1; - - if ((Session->L4CIRCUITTYPE & BPQHOST)) // host - goto noFlip1; - - if ((Session->L4CIRCUITTYPE & PACTOR)) - { - // incoming is Pactorlike - see if UZ7HO - - if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip1; - else - NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip - } - else - - // Must be L2 uplink - flip - - NewSess->L4USER[6] ^= 0x1e; // Flip SSID -noFlip1: - - EXTPORT->ATTACHEDSESSIONS[sess] = NewSess; - - NewSess->KAMSESSION = sess; - - // Set paclen to lower of incoming and outgoing - - Paclen = Session->SESSPACLEN; // Incoming PACLEN - - if (Paclen == 0) - Paclen = 256; // 0 = 256 - - PortPaclen = PORT->PORTPACLEN; - - if (PortPaclen == 0) - PortPaclen = 256; // 0 = 256 - - if (PortPaclen < Paclen) - Paclen = PortPaclen; - - NewSess->SESSPACLEN = Paclen; - Session->SESSPACLEN = Paclen; - NewSess->L4STATE = 5; - NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; - NewSess->L4TARGET.PORT = PORT; - -checkattachandcall: - - if (ptr) - { - // we have a call to connect to - - char Callstring[80]; - int len; - - Buffer = REPLYBUFFER; - Buffer->PORT = sess; - Buffer->PID = 0xf0; - - len = sprintf(Callstring,"C %s", ptr); - - ptr = strtok_s(NULL, " ", &Context); - - while (ptr) // if any other params (such as digis) copy them - { - if (strcmp(ptr, "S") == 0) - { - Session->STAYFLAG = TRUE; - } - else - len += sprintf(&Callstring[len], " %s", ptr); - - ptr = strtok_s(NULL, " ", &Context); - } - - Callstring[len++] = 13; - Callstring[len] = 0; - - Buffer->LENGTH = len + MSGHDDRLEN + 1; - memcpy(Buffer->L2DATA, Callstring, len); - C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); - - return; - } - - memcpy(Bufferptr, OKMSG, 3); - Bufferptr += 3; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; -} - -// SYSOP COMMANDS - -CMDX COMMANDS[] = -{ - "SAVENODES ",8, SAVENODES, 0, - "TELRECONFIG ",4, RECONFIGTELNET, 0, - "SAVEMH ",6, SAVEMHCMD, 0, - "REBOOT ",6, REBOOT, 0, - "RIGRECONFIG ",8 , RIGRECONFIG, 0, - "RESTART ",7,RESTART,0, - "RESTARTTNC ",10,RESTARTTNC,0, - "SENDNODES ",8,SENDNODES,0, - "EXTRESTART ",10, EXTPORTVAL, offsetof(EXTPORTDATA, EXTRESTART), - "TXDELAY ",3, PORTVAL, offsetof(PORTCONTROLX, PORTTXDELAY), - "MAXFRAME ",3, PORTVAL, offsetof(PORTCONTROLX, PORTWINDOW), - "RETRIES ",3, PORTVAL, offsetof(PORTCONTROLX, PORTN2), - "FRACK ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT1), - "RESPTIME ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT2), - "PPACLEN ",3,PORTVAL, offsetof(PORTCONTROLX, PORTPACLEN), - "QUALITY ",3,PORTVAL, offsetof(PORTCONTROLX, PORTQUALITY), - "PERSIST ",2,PORTVAL, offsetof(PORTCONTROLX, PORTPERSISTANCE), - "TXTAIL ",3,PORTVAL, offsetof(PORTCONTROLX, PORTTAILTIME), - "XMITOFF ",7,PORTVAL, offsetof(PORTCONTROLX, PORTDISABLED), - "DIGIFLAG ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIFLAG), - "DIGIPORT ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIPORT), - "MAXUSERS ",4,PORTVAL, offsetof(PORTCONTROLX, USERS), - "L3ONLY ",6,PORTVAL, offsetof(PORTCONTROLX, PORTL3FLAG), - "BBSALIAS ",4,PORTVAL, offsetof(PORTCONTROLX, PORTBBSFLAG), - "VALIDCALLS ",5,VALNODES,0, - "WL2KSYSOP ",5,WL2KSYSOP,0, - "STOPPORT ",4,STOPPORT,0, - "STARTPORT ",5,STARTPORT,0, - "STOPCMS ",7,STOPCMS,0, - "STARTCMS ",8,STARTCMS,0, - - "FINDBUFFS ",4,FINDBUFFS,0, - "KISS ",4,KISSCMD,0, - "GETPORTCTEXT",9,GetPortCTEXT, 0, - -#ifdef EXCLUDEBITS - - "EXCLUDE ",4,ListExcludedCalls,0, - -#endif - - "FULLDUP ",4,PORTVAL, offsetof(PORTCONTROLX, FULLDUPLEX), - "SOFTDCD ",4,PORTVAL, offsetof(PORTCONTROLX, SOFTDCDFLAG), - "OBSINIT ",7,SWITCHVAL,(size_t)&OBSINIT, - "OBSMIN ",6,SWITCHVAL,(size_t)&OBSMIN, - "NODESINT ",8,SWITCHVAL,(size_t)&L3INTERVAL, - "L3TTL ",5,SWITCHVAL,(size_t)&L3LIVES, - "L4RETRIES ",5,SWITCHVAL,(size_t)&L4N2, - "L4TIMEOUT ",5,SWITCHVALW,(size_t)&L4T1, - "T3 ",2,SWITCHVALW,(size_t)&T3, - "NODEIDLETIME",8,SWITCHVALW,(size_t)&L4LIMIT, - "LINKEDFLAG ",10,SWITCHVAL,(size_t)&LINKEDFLAG, - "IDINTERVAL ",5,SWITCHVAL,(size_t)&IDINTERVAL, - "MINQUAL ",7,SWITCHVAL,(size_t)&MINQUAL, - "FULLCTEXT ",6,SWITCHVAL,(size_t)&FULL_CTEXT, - "HIDENODES ",8,SWITCHVAL,(size_t)&HIDENODES, - "L4DELAY ",7,SWITCHVAL,(size_t)&L4DELAY, - "L4WINDOW ",6,SWITCHVAL,(size_t)&L4DEFAULTWINDOW, - "BTINTERVAL ",5,SWITCHVAL,(size_t)&BTINTERVAL, - "PASSWORD ", 8, PWDCMD, 0, - - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, // Apppl 32 is internal Terminal - "*** LINKED ",10,LINKCMD,0, - "CQ ",2,CQCMD,0, - "CONNECT ",1,CMDC00,0, - "NC ",2,CMDC00,0, - "BYE ",1,BYECMD,0, - "QUIT ",1,BYECMD,0, - "INFO ",1,CMDI00,0, - "HELP ",1,HELPCMD,0, - "VERSION ",1,CMDV00,0, - "NODES ",1,CMDN00,0, - "LINKS ",1,CMDL00,0, - "LISTEN ",3,LISTENCMD,0, - "L4T1 ",2,CMDT00,0, - "PORTS ",1,CMDP00,0, - "PACLEN ",3,CMDPAC,0, - "IDLETIME ",4,CMDIDLE,0, - "ROUTES ",1,CMDR00,0, - "STATS ",1,CMDSTATS,0, - "USERS ",1,CMDS00,0, - "UNPROTO ",2,UNPROTOCMD,0, - "? ",1,CMDQUERY,0, - "DUMP ",4,DUMPCMD,0, - "MHU ",3,MHCMD,0, // UTC Times - "MHL ",3,MHCMD,0, // Local Times - "MHV ",3,MHCMD,0, - "MHEARD ",1,MHCMD,0, - "APRS ",2,APRSCMD,0, - "ATTACH ",1,ATTACHCMD,0, - "RADIO ",3,RADIOCMD,0, - "AXRESOLVER ",3,AXRESOLVER,0, - "AXMHEARD ",3,AXMHEARD,0, - "TELSTATUS ",3,SHOWTELNET,0, - "NRR ",1,NRRCMD,0, - "PING ",2,PING,0, - "AGWSTATUS ",3,SHOWAGW,0, - "ARP ",3,SHOWARP,0, - "NAT ",3,SHOWNAT,0, - "IPROUTE ",3,SHOWIPROUTE,0, - "..FLMSG ",7,FLMSG,0 -}; - -CMDX * CMD = NULL; - -int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(CMDX); - -char * ReplyPointer; // Pointer into reply buffer - -int DecodeNodeName(char * NodeName, char * ptr) -{ - // NodeName is TABLE ENTRY WITH AX25 CALL AND ALIAS - - // Copyies 20 byte 20 DECODED NAME IN FORM ALIAS:CALL to ptr - // Returns significant length of string - - int len; - char Normcall[10]; - char * alias = &NodeName[7]; - int n = 6; - char * start = ptr; - - memset(ptr, ' ', 20); - - len = ConvFromAX25(NodeName, Normcall); - - if (*(alias) > ' ') // Does alias start with a null or a space ? - { - while (*(alias) > ' ' && n--) - { - *ptr++ = *alias++; - } - *ptr++ = ':'; - } - - memcpy(ptr, Normcall, len); - ptr += len; - - return (int)(ptr - start); -} - -char * SetupNodeHeader(struct DATAMESSAGE * Buffer) -{ - char Header[20]; - int len; - - char * ptr = &Buffer->L2DATA[0]; - - len = DecodeNodeName(MYCALLWITHALIAS, Header); - - memcpy (ptr, Header, len); - ptr += len; - - (*ptr++) = HEADERCHAR; - (*ptr++) = ' '; - - return ptr; -} - -VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len) -{ - if (Len == (4 + sizeof(void *))) // Null Packet - { - ReleaseBuffer((UINT *)Buffer); - return; - } - - Buffer->LENGTH = Len; - - C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); - - PostDataAvailable(Session); -} - - -VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) -{ - // ignore frames with single NULL (Keepalive) - - if (Buffer->LENGTH == sizeof(void *) + 5 && Buffer->L2DATA[0] == 0) - { - ReleaseBuffer(Buffer); - return; - } - - if (Buffer->LENGTH > 100) - { -// Debugprintf("BPQ32 command too long %s", Buffer->L2DATA); - ReleaseBuffer(Buffer); - return; - } - -InnerLoop: - - InnerCommandHandler(Session, Buffer); - -// See if any more commands in buffer - - if (Session->PARTCMDBUFFER) - { - char * ptr1, * ptr2; - int len; - - Buffer = Session->PARTCMDBUFFER; - - // Check that message has a CR, if not save buffer and exit - - len = Buffer->LENGTH - (4 + sizeof(void *)); - ptr1 = &Buffer->L2DATA[0]; - - ptr2 = memchr(ptr1, 13, len); - - if (ptr2 == NULL) - return; - - Session->PARTCMDBUFFER = NULL; - - goto InnerLoop; - } -} - - -VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) -{ - char * ptr1, * ptr2, *ptr3; - int len, oldlen, newlen, rest, n; - struct DATAMESSAGE * OldBuffer; - struct DATAMESSAGE * SaveBuffer; - char c; - - // If a partial command is stored, append this data to it. - - if (Session->PARTCMDBUFFER) - { - len = Buffer->LENGTH - (sizeof(void *) + 4); - ptr1 = &Buffer->L2DATA[0]; - - OldBuffer = Session->PARTCMDBUFFER; // Old Data - - if (OldBuffer == Buffer) - { - // something has gone horribly wrong - - Session->PARTCMDBUFFER = NULL; - return; - } - - oldlen = OldBuffer->LENGTH; - - newlen = len + oldlen; - - if (newlen > 200) - { - // Command far too long - ignore previous - - OldBuffer->LENGTH = oldlen = sizeof(void *) + 4; - } - - OldBuffer->LENGTH += len; - memcpy(&OldBuffer->L2DATA[oldlen - (sizeof(void *) + 4)], Buffer->L2DATA, len); - - ReleaseBuffer((UINT *)Buffer); - - Buffer = OldBuffer; - - Session->PARTCMDBUFFER = NULL; - } - - // Check that message has a CR, if not save buffer and exit - - len = Buffer->LENGTH - (sizeof(void *) + 4); - ptr1 = &Buffer->L2DATA[0]; - - // Check for sending YAPP to Node - - if (len == 2 && ptr1[0] == 5 && ptr1[1] == 1) - { - ptr1[0] = 0x15; // NAK - - ptr1[1] = sprintf(&ptr1[2], "Node doesn't support YAPP Transfers"); - - Buffer->LENGTH += ptr1[1]; - - C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); - PostDataAvailable(Session); - return; - } - - - ptr2 = memchr(ptr1, ';', len); - - if (ptr2 == 0) - { - ptr2 = memchr(ptr1, 13, len); - - if (ptr2 == 0) - { - // No newline - - Session->PARTCMDBUFFER = Buffer; - return; - } - } - - ptr2++; - - rest = len - (int)(ptr2 - ptr1); - - if (rest) - { - // there are chars beyond the cr in the buffer - - // see if LF after CR - - if ((*ptr2) == 10) // LF - { - ptr2++; - rest--; - } - - if (rest) // May only have had LF - { - // Get a new buffer, and copy extra data to it. - - SaveBuffer = (struct DATAMESSAGE *)GetBuff(); - - if (SaveBuffer) //`Just ignore if no buffers - { - SaveBuffer->LENGTH = rest + MSGHDDRLEN + 1; - SaveBuffer->PID = 0xf0; - memcpy(&SaveBuffer->L2DATA[0], ptr2, rest); - Session->PARTCMDBUFFER = SaveBuffer; - } - } - } - - // GET PACLEN FOR THIS CONNECTION - - CMDPACLEN = Session->SESSPACLEN; - - if (CMDPACLEN == 0) - CMDPACLEN = PACLEN; // Use default if no Session PACLEN - - // If sesion is in UNPROTO Mode, send message as a UI message - - if (Session->UNPROTO) - { - DIGIMESSAGE Msg; - int Port = Session->UNPROTO; - int Len = Buffer->LENGTH - (MSGHDDRLEN -1); // Need PID - - // First check for UNPROTO exit - ctrl/z or /ex - - if (Buffer->L2DATA[0] == 26 || (Len == 6 && _memicmp(&Buffer->L2DATA[0], "/ex", 3) == 0)) // CTRL/Z or /ex - { - REPLYBUFFER = Buffer; - - Session->UNPROTO = 0; - memset(Session->UADDRESS, 0, 64); - - // SET UP HEADER - - Buffer->PID = 0xf0; - ptr1 = SetupNodeHeader(Buffer); - memcpy(ptr1, OKMSG, 3); - ptr1 += 3; - SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); - - return; - } - - memset(&Msg, 0, sizeof(Msg)); - - Msg.PORT = Port; - Msg.CTL = 3; // UI - memcpy(Msg.DEST, Session->UADDRESS, 7); - memcpy(Msg.ORIGIN, Session->L4USER, 7); - memcpy(Msg.DIGIS, &Session->UADDRESS[7], Session->UAddrLen - 7); - memcpy(&Msg.PID, &Buffer->PID, Len); - - Send_AX_Datagram(&Msg, Len, Port); // Len is Payload - CTL, PID and Data - -// SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO); - - ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply - - return; - } - - memset(COMMANDBUFFER, 32, 80); // Clear to spaces - - ptr1 = &Buffer->L2DATA[0]; - ptr2 = &COMMANDBUFFER[0]; - ptr3 = &OrigCmdBuffer[0]; - - memset(OrigCmdBuffer, 0, 80); - n = 80; - - while (n--) - { - c = *(ptr1++) & 0x7f; // Mask paritu - - if (c == 13 || c == ';') - break; // CR - - *(ptr3++) = c; // Original Case - - c = toupper(c); - *(ptr2++) = c; - } - - - // USE INPUT MESSAGE _BUFFER FOR REPLY - - REPLYBUFFER = Buffer; - - // SET UP HEADER - - Buffer->PID = 0xf0; - ptr1 = SetupNodeHeader(Buffer); - - ReplyPointer = ptr1; - - ALIASINVOKED = 0; // Clear "Invoked by APPL ALIAS flag" - - DoTheCommand(Session); // We also call DotheCommand when we need to reprocess - eg for alias handling -} - -VOID DoTheCommand(TRANSPORTENTRY * Session) -{ - struct DATAMESSAGE * Buffer = REPLYBUFFER; - char * ptr1, * ptr2; - int n; - - ptr1 = &COMMANDBUFFER[0]; // - - n = 10; - - while ((*ptr1 == ' ' || *ptr1 == 0) && n--) - ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) - - if (n == -1) - { - // Null command - - ReleaseBuffer((UINT *)Buffer); - return; - } - - ptr2 = ptr1; // Save - - - CMD = &COMMANDS[0]; - n = 0; - - for (n = 0; n < NUMBEROFCOMMANDS; n++) - { - int CL = CMD->CMDLEN; - - ptr1 = ptr2; - - CMDPTR = CMD; - - if (n == APPL1) // First APPL command - { - APPLMASK = 1; // FOR APPLICATION ATTACH REQUESTS - ALIASPTR = &CMDALIAS[0][0]; - } - - // ptr1 is input command - - if (memcmp(CMD->String, ptr1, CL) == 0) - { - // Found match so far - check rest - - char * ptr2 = &CMD->String[CL]; - - ptr1 += CL; - - if (*(ptr1) != ' ') - { - while(*(ptr1) == *ptr2 && *(ptr1) != ' ') - { - ptr1++; - ptr2++; - } - } - - if (*(ptr1) == ' ') - { - Session->BADCOMMANDS = 0; // RESET ERROR COUNT - - // SEE IF SYSOP COMMAND, AND IF SO IF PASSWORD HAS BEEN ENTERED - - if (n < PASSCMD) - { - //NEEDS PASSWORD FOR SYSOP COMMANDS - - if (Session->PASSWORD != 0xFFFF) - { - ptr1 = ReplyPointer; - - memcpy(ptr1, PASSWORDMSG, LPASSMSG); - ptr1 += LPASSMSG; - - SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); - return; - } - } -// VALNODESFLAG = 0; // NOT VALID NODES COMMAND - - ptr1++; // Skip space - - CMD->CMDPROC(Session, ReplyPointer, ptr1, CMD); - return; - } - } - - APPLMASK <<= 1; - ALIASPTR += ALIASLEN; - - CMD++; - - } - Session->BADCOMMANDS++; - - if (Session->BADCOMMANDS > 6) // TOO MANY ERRORS - { - ReleaseBuffer((UINT *)Buffer); - Session->STAYFLAG = 0; - CLOSECURRENTSESSION(Session); - return; - } - - ptr1 = ReplyPointer; - - memcpy(ptr1, CMDERRMSG, CMDERRLEN); - ptr1 += CMDERRLEN; - - SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); -} - - -VOID StatsTimer() -{ - struct PORTCONTROL * PORT = PORTTABLE; - int sum; - - while(PORT) - { - sum = PORT->SENDING / 11; - PORT->AVSENDING = sum; - - sum = (PORT->SENDING + PORT->ACTIVE) /11; - PORT->AVACTIVE = sum; - - PORT->SENDING = 0; - PORT->ACTIVE = 0; - - PORT = PORT->PORTPOINTER; - } -} - - - -extern struct AXIPPORTINFO * Portlist[]; - -#define TCPConnected 4 - - -VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // DISPLAY AXIP Resolver info - - int Port = 0, index =0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - struct AXIPPORTINFO * AXPORT; - char Normcall[11]; - char Flags[10]; - struct arp_table_entry * arp; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - AXPORT = Portlist[Port]; - - if (AXPORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Resolver info for Port %d\r", Port); - - while (index < AXPORT->arp_table_len) - { - arp = &AXPORT->arp_table[index]; - - if (arp->ResolveFlag && arp->error != 0) - { - // resolver error - Display Error Code - sprintf(AXPORT->hostaddr, "Error %d", arp->error); - } - else - { - if (arp->IPv6) - Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, AXPORT->hostaddr, TRUE); - else - Format_Addr((unsigned char *)&arp->destaddr.sin_addr, AXPORT->hostaddr, FALSE); - } - - ConvFromAX25(arp->callsign, Normcall); - - Flags[0] = 0; - - if (arp->BCFlag) - strcat(Flags, "B "); - - if (arp->TCPState == TCPConnected) - strcat(Flags, "C "); - - if (arp->AutoAdded) - strcat(Flags, "A"); - - if (arp->port == arp->SourcePort) - Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d = %-.42s %s\r", - Normcall, - arp->hostname, - arp->port, - AXPORT->hostaddr, - Flags); - - else - Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d<%d = %-.42s %s\r", - Normcall, - arp->hostname, - arp->port, - arp->SourcePort, - AXPORT->hostaddr, - Flags); - - index++; - } - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // DISPLAY AXIP Mheard info - - int Port = 0, index = 0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - struct AXIPPORTINFO * AXPORT; - int n = MHENTRIES; - char Normcall[11]; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - AXPORT = Portlist[Port]; - - if (AXPORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Mheard for Port %d\r", Port); - - while (index < MaxMHEntries) - { - if (AXPORT->MHTable[index].proto != 0) - { - char Addr[80]; - - Format_Addr((unsigned char *)&AXPORT->MHTable[index].ipaddr6, Addr, AXPORT->MHTable[index].IPv6); - - Normcall[ConvFromAX25(AXPORT->MHTable[index].callsign, Normcall)] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-15s %c %-6d %-25s%c\r", Normcall, - Addr, - AXPORT->MHTable[index].proto, - AXPORT->MHTable[index].port, - asctime(gmtime( &AXPORT->MHTable[index].LastHeard )), - (AXPORT->MHTable[index].Keepalive == 0) ? ' ' : 'K'); - - Bufferptr[-3] = ' '; // Clear CR returned by asctime - } - - index++; - } - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -#pragma pack() - -extern struct TNCINFO * TNCInfo[41]; - -extern char WL2KCall[10]; -extern char WL2KLoc[7]; - -BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); -BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); - -VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - - char LastUpdated[100]; - char Name[100] = ""; - char Addr1[100] = ""; - char Addr2[100] = ""; - char City[100] = ""; - char State[100] = ""; - char Country[100] = ""; - char PostCode[100] = ""; - char Email[100] = ""; - char Website[100] = ""; - char Phone[100] = ""; - char Data[100] = ""; - char LOC[100] = ""; - BOOL Exists = TRUE; - time_t LastUpdateSecs = 0; - char * ptr1, * ptr2; - - SOCKET sock; - - int Len; - char Message[2048]; - - if (WL2KCall[0] < 33) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Winlink reporting is not configured\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to connect to WL2K Database\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (strstr(_REPLYBUFFER, "\"ErrorMessage\":")) - Exists = FALSE; - - GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Name); - GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Addr1); - GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Addr2); - GetJSONValue(_REPLYBUFFER, "\"City\":", City); - GetJSONValue(_REPLYBUFFER, "\"State\":", State); - GetJSONValue(_REPLYBUFFER, "\"Country\":", Country); - GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", PostCode); - GetJSONValue(_REPLYBUFFER, "\"Email\":", Email); - GetJSONValue(_REPLYBUFFER, "\"Website\":", Website); - GetJSONValue(_REPLYBUFFER, "\"Phones\":", Phone); - GetJSONValue(_REPLYBUFFER, "\"Comments\":", Data); - GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", LOC); - GetJSONValue(_REPLYBUFFER, "\"Timestamp\":", LastUpdated); - - ptr1 = strchr(LastUpdated, '('); - - if (ptr1) - { - ptr2 = strchr(++ptr1, ')'); - - if (ptr2) - { - *(ptr2 - 3) = 0; // remove millisecs - LastUpdateSecs = atoi(ptr1); - - FormatTime3(LastUpdated, LastUpdateSecs); - } - } - - if (_memicmp(CmdTail, "SET ", 4) == 0) - { - if (Exists) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Record already exists in WL2K Database\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Set New Values. Any other params are values to set, separated by | - -// ptr1 = strtok_s(&CmdTail[4], ",", &Context); - -// if (ptr1 == NULL) -// goto DoReplace; - -// strcpy(Name, ptr1); - -//DoReplace: - - Len = sprintf(Message, - "\"Callsign\":\"%s\"," - "\"GridSquare\":\"%s\"," - "\"SysopName\":\"%s\"," - "\"StreetAddress1\":\"%s\"," - "\"StreetAddress2\":\"%s\"," - "\"City\":\"%s\"," - "\"State\":\"%s\"," - "\"Country\":\"%s\"," - "\"PostalCode\":\"%s\"," - "\"Email\":\"%s\"," - "\"Phones\":\"%s\"," - "\"Website\":\"%s\"," - "\"Comments\":\"%s\",", - - WL2KCall, WL2KLoc, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); - - Debugprintf("Sending %s", Message); - - sock = OpenWL2KHTTPSock(); - - if (sock) - SendHTTPRequest(sock, "api.winlink.org", 80, - "/sysop/add", Message, Len, NULL); - - closesocket(sock); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Database Updated\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (Exists) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "\rWL2K SYSOP Info for %s\r", WL2KCall); - Bufferptr = Cmdprintf(Session, Bufferptr, "Grid Square: %s\r", LOC); - Bufferptr = Cmdprintf(Session, Bufferptr, "Name: %s\r", Name); - Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 1: %s\r", Addr1); - Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 2: %s\r", Addr2); - Bufferptr = Cmdprintf(Session, Bufferptr, "City: %s\r", City); - Bufferptr = Cmdprintf(Session, Bufferptr, "State: %s\r", State); - Bufferptr = Cmdprintf(Session, Bufferptr, "Country: %s\r", Country); - Bufferptr = Cmdprintf(Session, Bufferptr, "PostCode: %s\r", PostCode); - Bufferptr = Cmdprintf(Session, Bufferptr, "Email Address: %s\r", Email); - Bufferptr = Cmdprintf(Session, Bufferptr, "Website: %s\r", Website); - Bufferptr = Cmdprintf(Session, Bufferptr, "Phone: %s\r", Phone); - Bufferptr = Cmdprintf(Session, Bufferptr, "Additional Data: %s\r", Data); - Bufferptr = Cmdprintf(Session, Bufferptr, "Last Updated: %s\r", LastUpdated); - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, "No SYSOP record for %s\r", WL2KCall); - - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -VOID CloseKISSPort(struct PORTCONTROL * PortVector); -int OpenConnection(struct PORTCONTROL * PortVector); - -VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - - struct TNCINFO * TNC; - struct TCPINFO * TCP; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - TNC = TNCInfo[portno]; - - if (!TNC || !TNC->TCPInfo) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - TCP = TNC->TCPInfo; - - TCP->CMS = 0; - TCP->CMSOK = FALSE; -#ifndef LINBPQ - CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); - SetWindowText(TCP->hCMSWnd, "CMS Off"); -#endif - Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Disabled\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - - struct TNCINFO * TNC; - struct TCPINFO * TCP; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - TNC = TNCInfo[portno]; - - if (!TNC || !TNC->TCPInfo) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - TCP = TNC->TCPInfo; - TCP->CMS = 1; -#ifndef LINBPQ - CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); -#endif - CheckCMS(TNC); - - Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Enabled\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - struct KISSINFO * KISS; - - if (PORT->PORTSTOPCODE) - { - // Port has Close Routine - - PORT->PortStopped = TRUE; - - if (PORT->PORTSTOPCODE(PORT)) - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Close Failed\r"); - - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - if (PORT->PORTTYPE != 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - KISS = (struct KISSINFO *) PORT; - - if (KISS->FIRSTPORT != KISS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - CloseKISSPort(PORT); - PORT->PortStopped = TRUE; - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - struct KISSINFO * KISS; - - if (PORT->PORTSTARTCODE) - { - // Port has Open Routine - - PORT->PortStopped = FALSE; - - if (PORT->PORTSTARTCODE(PORT)) - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - if (PORT->PORTTYPE != 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - KISS = (struct KISSINFO *) PORT; - - if (KISS->FIRSTPORT != KISS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (OpenConnection(PORT), TRUE) - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); - - PORT->PortStopped = FALSE; - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - - -#define FEND 0xC0 -int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count); - - -VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno = 0; - int cmd = 0, val = 0; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Send KISS Command to TNC - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - { - cmd = atoi (ptr); - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - val = atoi (ptr); - } - } - - if (portno == 0 || cmd == 0) - { - strcpy(Bufferptr, BADMSG); - Bufferptr += (int)strlen(BADMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - struct KISSINFO * KISS; - UCHAR ENCBUFF[16]; - unsigned char * ptr = ENCBUFF; - - if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - KISS = (struct KISSINFO *) PORT; - - if (KISS->FIRSTPORT != KISS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Send Command - - *(ptr++) = FEND; - *(ptr++) = KISS->OURCTRL | cmd; - *(ptr++) = (UCHAR)val; - *(ptr++) = FEND; - - PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q - - PORT->Session = Session; - PORT->LastKISSCmdTime = time(NULL); - - ASYSEND(PORT, ENCBUFF, 4); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - FindLostBuffers(); - -#ifdef WIN32 - Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to Debugview\r"); -#else - Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to syslog\r"); -#endif - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // Telnet Connection from FLMSG - CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link - ReleaseBuffer((UINT *)REPLYBUFFER); -} - -BOOL CheckExcludeList(UCHAR * Call) -{ - UCHAR * ptr1 = ExcludeList; - - while (*ptr1) - { - if (memcmp(Call, ptr1, 6) == 0) - return FALSE; - - ptr1 += 7; - } - - return TRUE; -} - - -void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - - UCHAR * ptr = ExcludeList; - char Normcall[10] = ""; - UCHAR AXCall[8] = ""; - - if (*CmdTail == ' ') - goto DISPLIST; - - if (*CmdTail == 'Z') - { - // CLEAR LIST - - memset(ExcludeList, 0, 70); - goto DISPLIST; - } - - ConvToAX25(CmdTail, AXCall); - - if (strlen(ExcludeList) < 70) - strcat(ExcludeList, AXCall); - -DISPLIST: - - while (*ptr) - { - Normcall[ConvFromAX25(ptr, Normcall)] = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); - ptr += 7; - } - - *(Bufferptr++) = '\r'; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr) -{ - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return FALSE; - } - - return TRUE; -} - -VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - char * ptr1, * ptr, * ptr2; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BPQDirectory, "NodeHelp.txt"); - - if (stat(MsgFile, &STAT) == -1) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - MsgBytes = malloc(FileSize+1); - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize] = 0; - - ptr1 = MsgBytes; - - // Replace LF or CRLF with CR - - // First remove cr from crlf - - while(ptr2 = strstr(ptr1, "\r\n")) - { - memmove(ptr2, ptr2 + 1, strlen(ptr2)); - } - - // Now replace lf with cr - - ptr1 = MsgBytes; - - while (*ptr1) - { - if (*ptr1 == '\n') - *(ptr1) = '\r'; - - ptr1++; - } - - ptr = ptr1 = MsgBytes; - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - // Read and send a line at a time, converting any line endings into CR - - while (*ptr1) - { - if (*ptr1 == '\r') - { - *(ptr1++) = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", ptr); - - ptr = ptr1; - } - else - ptr1++; - } - - free(MsgBytes); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - - - - - - - - - diff --git a/Cmd-HPLaptop.c b/Cmd-HPLaptop.c deleted file mode 100644 index 22fdd43..0000000 --- a/Cmd-HPLaptop.c +++ /dev/null @@ -1,5495 +0,0 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// C replacement for cmd.asm -// -#define Kernel - -#define _CRT_SECURE_NO_DEPRECATE -#pragma data_seg("_BPQDATA") - -//#include "windows.h" -//#include "winerror.h" - - -#include "time.h" -#include "stdio.h" -#include -//#include "vmm.h" -//#include "SHELLAPI.H" - -#include "CHeaders.h" -#include "bpqaprs.h" - -#pragma pack() - -#include "tncinfo.h" -#include "telnetserver.h" - -//#include "GetVersion.h" - -//#define DllImport __declspec( dllimport ) -//#define DllExport __declspec( dllexport ) - -BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR *AXCalls); -VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); -int APIENTRY ClearNodes(); -VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); -VOID SendHTTPRequest(SOCKET sock, char * Host, int Port, char * Request, char * Params, int Len, char * Return); -SOCKET OpenWL2KHTTPSock(); -VOID FormatTime3(char * Time, time_t cTime); -VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6); -VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst); -VOID FindLostBuffers(); -BOOL CheckCMS(struct TNCINFO * TNC); -VOID L2SENDXID(struct _LINKTABLE * LINK); -int CountBits(unsigned long in); -VOID SaveMH(); -BOOL RestartTNC(struct TNCINFO * TNC); -void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID WriteMiniDump(); - -char COMMANDBUFFER[81] = ""; // Command Hander input buffer -char OrigCmdBuffer[81] = ""; // Command Hander input buffer - -struct DATAMESSAGE * REPLYBUFFER = NULL; -UINT APPLMASK = 0; -UCHAR SAVEDAPPLFLAGS = 0; - -UCHAR ALIASINVOKED = 0; - -extern struct TNCINFO * TNCInfo[41]; - -VOID * CMDPTR = 0; - -short CMDPACLEN = 0; - -char OKMSG[] = "Ok\r"; - -char CMDERRMSG[] = "Invalid command - Enter ? for command list\r"; -#define CMDERRLEN sizeof(CMDERRMSG) - 1 - -char PASSWORDMSG[] = "Command requires SYSOP status - enter password\r"; -#define LPASSMSG sizeof(PASSWORDMSG) - 1 - -char CMDLIST[] = "CONNECT BYE INFO NODES PORTS ROUTES USERS MHEARD"; - -#define CMDLISTLEN sizeof(CMDLIST) - 1 - -char BADMSG[] = "Bad Parameter\r"; -char BADPORT[] = "Invalid Port Number\r"; -char NOTEXTPORT[] = "Only valid on EXT ports\r"; -char NOVALCALLS[] = "No Valid Calls defined on this port\r"; - -char BADVALUEMSG[] = "Invalid parameter\r"; - -char BADCONFIGMSG[] = "Configuration File check falled - will continue with old config\r"; -#ifdef LINBPQ -char REBOOTOK[] = "Rebooting\r"; -#else -char REBOOTOK[] = "Rebooting in 20 secs\r"; -#endif -char REBOOTFAILED[] = "Shutdown failed\r"; - -char RESTARTOK[] = "Restarting\r"; -char RESTARTFAILED[] = "Restart failed\r"; - -UCHAR ARDOP[7] = {'A'+'A','R'+'R','D'+'D','O'+'O','P'+'P',' '+' '}; // ARDOP IN AX25 -UCHAR VARA[7] = {'V'+'V','A'+'A','R'+'R','A'+'A',' '+' ',' '+' '}; // VARA IN AX25 - -int STATSTIME = 0; -int MAXBUFFS = 0; -int QCOUNT = 0; -int MINBUFFCOUNT = 65535; -int NOBUFFCOUNT = 0; -int BUFFERWAITS = 0; -int MAXDESTS = 0; -int NUMBEROFNODES = 0; -int L4CONNECTSOUT = 0; -int L4CONNECTSIN = 0; -int L4FRAMESTX = 0; -int L4FRAMESRX = 0; -int L4FRAMESRETRIED = 0; -int OLDFRAMES = 0; -int L3FRAMES = 0; - -VOID SENDSABM(); -VOID RESET2(); - -int APPL1 = 0; -int PASSCMD = 0; - -#pragma pack(1) - -struct _EXTPORTDATA DP; // Only way I can think of to get offets to port data into cmd table - -char CMDALIAS[ALIASLEN][NumberofAppls] = {0}; -char * ALIASPTR = &CMDALIAS[0][0]; - -extern int RigReconfigFlag; - -CMDX COMMANDS[]; - -int CMDXLEN = sizeof (CMDX); - -VOID SENDNODESMSG(); -VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); -void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); -VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); - -char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...) -{ - // Send Command response checking PACLEN - - char Mess[4096]; - va_list(arglist); - int OldLen; - int MsgLen; - struct DATAMESSAGE * Buffer; - char * Messptr = Mess; - int Paclen = Session->SESSPACLEN; - - if (Paclen == 0) - Paclen = 255; - - va_start(arglist, format); - - MsgLen = vsprintf(Mess, format, arglist); - - OldLen = (int)(Bufferptr - (char *)REPLYBUFFER->L2DATA); - - while ((OldLen + MsgLen) > Paclen) - { - // Have to send Paclen then get a new buffer - - int ThisBit = Paclen - OldLen; // What we can send this time - - if (ThisBit < 0) - ThisBit = 0; // How can this happen?? - - memcpy(Bufferptr, Messptr, ThisBit); - Messptr += ThisBit; - MsgLen -= ThisBit; - - // QUEUE IT AND GET ANOTHER BUFFER - - Buffer = (struct DATAMESSAGE *)GetBuff(); - - if (Buffer == NULL) - - // No buffers, so just reuse the old one (better than crashing !!) - - Buffer = REPLYBUFFER; - else - SendCommandReply(Session, REPLYBUFFER, Paclen + (4 + sizeof(void *))); - - - REPLYBUFFER = Buffer; - Buffer->PID = 0xf0; - - Bufferptr = &Buffer->L2DATA[0]; - OldLen = 0; - } - - // Add last bit to buffer - - memcpy(Bufferptr, Messptr, MsgLen); - - return Bufferptr + MsgLen; -} - - -VOID SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - SENDNODESMSG(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID SAVEMHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - SaveMH(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID SAVENODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - SaveNodes(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID DUMPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - WriteMiniDump(); - - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID RIGRECONFIG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (!ProcessConfig()) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check falled - will continue with old config"); - } - else - { - RigReconfigFlag=TRUE; - Bufferptr = Cmdprintf(Session, Bufferptr, "Rigcontrol Reconfig requested"); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID REBOOT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (Reboot()) - { - strcpy(Bufferptr, REBOOTOK); - Bufferptr += (int)strlen(REBOOTOK); - } - else - { - strcpy(Bufferptr, REBOOTFAILED); - Bufferptr += (int)strlen(REBOOTFAILED); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID RESTART(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (Restart()) - { - strcpy(Bufferptr, RESTARTOK); - Bufferptr += (int)strlen(RESTARTOK); - } - else - { - strcpy(Bufferptr, RESTARTFAILED); - Bufferptr += (int)strlen(RESTARTFAILED); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID RESTARTTNC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char * ptr, *Context; - int portno; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno && portno < 33) - { - struct TNCINFO * TNC = TNCInfo[portno]; - - if (TNC == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - } - else - { - if (TNC->ProgramPath) - { - if (RestartTNC(TNC)) - Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Ok\r", TNC->ProgramPath); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Failed\r", TNC->ProgramPath); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "PATH not defined so can't restart TNC\r"); - } - } - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -UCHAR VALNODESFLAG = 0, EXTONLY = 0; - -VOID PORTVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD); - -VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - VALNODESFLAG = 1; - PORTVAL(Session, Bufferptr, CmdTail, CMD); -} - -VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - EXTONLY = 1; - PORTVAL(Session, Bufferptr, CmdTail, CMD); -} -VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS PORT VALUE COMMANDS - - char * ptr, *Context, * ptr1; - int portno; - UCHAR oldvalue, newvalue; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - UCHAR * valueptr; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - if (VALNODESFLAG) - { - char * VNPtr = PORT->PERMITTEDCALLS; - char Normcall[10]; - - VALNODESFLAG = 0; - - if (VNPtr) - { - while (VNPtr[0]) - { - Normcall[ConvFromAX25(VNPtr, Normcall)] = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); - VNPtr += 7; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", NOVALCALLS); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - - } - - if (EXTONLY) - { - // Make sure an Extenal Port - - EXTONLY = 0; - - if (PORT->PORTTYPE != 0x10) - { - strcpy(Bufferptr, NOTEXTPORT); - Bufferptr += (int)strlen(NOTEXTPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - valueptr = (UCHAR *)PORT + CMD->CMDFLAG; - oldvalue = *valueptr; - - // Display Param Namee - - ptr1 = &CMD->String[0]; - n = 12; - - while (*(ptr1) != ' ' && n--) - *(Bufferptr++) = *(ptr1++); - - // See if another param - if not, just display current value - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - *valueptr = newvalue; - - Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); - } - - else - Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - -VOID SWITCHVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Update switch 8 bit value - - char * ptr, *Context, * ptr1; - UCHAR oldvalue, newvalue; - int n; - UCHAR * valueptr; - - valueptr = (UCHAR *)CMD->CMDFLAG; - - oldvalue = *valueptr; - - // Display Param Name - - ptr1 = &CMD->String[0]; - n = 12; - - while (*(ptr1) != ' ' && n--) - *(Bufferptr++) = *(ptr1++); - - // See if a param - if not, just display current value - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - *valueptr = newvalue; - - Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); - - if (memcmp(CMD->String, "NODESINT ", 8) == 0) - L3TIMER = L3INTERVAL; - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - -VOID SWITCHVALW (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Update switch 16 bit value - - char * ptr, *Context, * ptr1; - USHORT oldvalue, newvalue; - int n; - USHORT * valueptr; - - valueptr = (USHORT *)CMD->CMDFLAG; - - oldvalue = (USHORT)*valueptr; - - // Display Param Name - - ptr1 = &CMD->String[0]; - n = 12; - - while (*(ptr1) != ' ' && n--) - *(Bufferptr++) = *(ptr1++); - - // See if a param - if not, just display current value - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - *valueptr = newvalue; - - Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue); - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - -TRANSPORTENTRY * SetupSessionFromSession(TRANSPORTENTRY * Session, PBPQVECSTRUC HOSTSESS, UINT APPLMASK) -{ - // Create a Transport (L4) session linked to an incoming Session - - TRANSPORTENTRY * NewSess = L4TABLE; - int Index = 0; - - while (Index < MAXCIRCUITS) - { - if (NewSess->L4USER[0] == 0) - { - // Got One - - UCHAR * ourcall = &MYCALL[0]; - - Session->L4CROSSLINK = NewSess; - NewSess->L4CROSSLINK = Session; - - if (APPLMASK) - { - // Circuit for APPL - look for an APPLCALL - - APPLCALLS * APPL = APPLCALLTABLE; - - while ((APPLMASK & 1) == 0) - { - APPLMASK >>= 1; - APPL++; - } - if (APPL->APPLCALL[0] > 0x40) // We have an applcall - ourcall = &APPL->APPLCALL[0]; - } - - memcpy(NewSess->L4USER, ourcall, 7); - memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); - - NewSess->CIRCUITINDEX = Index; //OUR INDEX - NewSess->CIRCUITID = NEXTID; - - NEXTID++; - if (NEXTID == 0) - NEXTID++; // kEEP nON-ZERO - - NewSess->SESSIONT1 = Session->SESSIONT1; - NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; - NewSess->SESSPACLEN = PACLEN; // Default; - - NewSess->L4TARGET.HOST = HOSTSESS; - NewSess->L4STATE = 5; - return NewSess; - } - Index++; - NewSess++; - } - return NULL; -} - -extern int GETCONNECTIONINFO(); - - -BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions) -{ - PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; - TRANSPORTENTRY * NewSess; - int ApplNum; - int n = BPQHOSTSTREAMS; - int ConfigedPorts = 0; - - // LOOK FOR A FREE HOST SESSION - - while (n--) - { - if (HOSTSESS->HOSTAPPLMASK & Mask) - { - // Right appl - - ConfigedPorts++; - - if (HOSTSESS->HOSTSESSION == NULL && (HOSTSESS->HOSTFLAGS & 3) == 0) // Not attached and no report outstanding - { - // WEVE GOT A FREE BPQ HOST PORT - USE IT - - NewSess = SetupSessionFromSession(Session, HOSTSESS, Mask); - - if (NewSess == NULL) - return FALSE; // Appl not available - - HOSTSESS->HOSTSESSION = NewSess; - - // Convert APPLMASK to APPLNUM - - ApplNum = 1; - - while (APPLMASK && (APPLMASK & 1) == 0) - { - ApplNum++; - APPLMASK >>= 1; - } - - HOSTSESS->HOSTAPPLNUM = ApplNum; - - HOSTSESS->HOSTFLAGS |= 2; // Indicate State Change - - NewSess->L4CIRCUITTYPE = BPQHOST | DOWNLINK; - - PostStateChange(NewSess); - - NewSess->SESS_APPLFLAGS = HOSTSESS->HOSTAPPLFLAGS; - - NewSess->SESSPACLEN = Paclen; - - return TRUE; - } - } - HOSTSESS++; - } - - *AnySessions = ConfigedPorts; // to distinguish between none and all in use - return FALSE; -} - -VOID APPLCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - BOOL CONFAILED = 0; - UINT CONERROR ; - char APPName[13]; - char * ptr1, *ptr2; - int n = 12; - BOOL Stay = FALSE; - - // Copy Appl and Null Terminate - - ptr1 = &CMD->String[0]; - ptr2 = APPName; - - while (*(ptr1) != ' ' && n--) - *(ptr2++) = *(ptr1++); - - *(ptr2) = 0; - - if (Session->LISTEN) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Can't use %s while listening\r", APPName); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - if (CmdTail[0] == 'S') - Stay = TRUE; - - Session->STAYFLAG = Stay; - - memcpy(Session->APPL, CMD->String, 12); - - // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND - - if (ALIASPTR[0] > ' ') - { - // COPY ALIAS TO COMMAND BUFFER, THEN REENTER COMMAND HANDLER - - int SaveSecure = Session->Secure_Session; - - memcpy(COMMANDBUFFER, ALIASPTR, ALIASLEN); - _strupr(COMMANDBUFFER); - memcpy(OrigCmdBuffer, ALIASPTR, ALIASLEN); // In case original case version needed - - ALIASINVOKED = 1; // To prevent Alias Loops - - // Set secure session for application alias in case telnet outward connect - - Session->Secure_Session = 1; - DoTheCommand(Session); - Session->Secure_Session = SaveSecure; - - return; - } - - if (cATTACHTOBBS(Session, APPLMASK, CMDPACLEN, &CONERROR) == 0) - { - // No Streams - - if (CONERROR) - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, All %s Ports are in use - Please try later\r", APPName); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Application %s is not running - Please try later\r", APPName); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // IF CMD_TO_APPL SET IN APPLFLAGS, SEND INPUT MSG TO APPL - - if (Session->L4CROSSLINK->SESS_APPLFLAGS & CMD_TO_APPL) - { - struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff(); - TRANSPORTENTRY * XSession = Session->L4CROSSLINK; - - if (Msg) - { - COMMANDBUFFER[72] = 13; - memcpy(Msg->L2DATA, COMMANDBUFFER, 73); - Msg->LENGTH = 73 + 4 + sizeof(void *); - Msg->PID = 0xf0; - - C_Q_ADD(&XSession->L4TX_Q, (UINT *)Msg); - PostDataAvailable(XSession); - } - } - - if (Stay) - Session->L4CROSSLINK->L4TARGET.HOST->HOSTFLAGS |= 0x20; - - // IF MSG_TO_USER SET, SEND 'CONNECTED' MESSAGE TO USER - - Session->SESS_APPLFLAGS = Session->L4CROSSLINK->SESS_APPLFLAGS; - - if (Session->L4CROSSLINK->SESS_APPLFLAGS & MSG_TO_USER) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Connected to %s\r", APPName); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - // DONT NEED BUFFER ANY MORE - - ReleaseBuffer((UINT *)REPLYBUFFER); - return; -} - - -VOID CMDI00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - if (sizeof(void *) == 4) - Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s\r", VersionString); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s (64 bit)\r", VersionString); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID BYECMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link - ReleaseBuffer((UINT *)REPLYBUFFER); - return; -} - -VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // SET PACLEN FOR THIS SESSION - - char * ptr, *Context; - int newvalue; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - if (newvalue > 29 && newvalue < 256) - Session->SESSPACLEN = newvalue & 0xff; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "PACLEN - %d\r", Session->SESSPACLEN); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDIDLE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // SET IDLETIME FOR THIS SESSION - - char * ptr, *Context; - int newvalue; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - if (newvalue > 59 && newvalue < 901) - Session->L4LIMIT = newvalue; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "IDLETIME - %d\r", Session->L4LIMIT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - -} -VOID CMDT00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // SET L4 TIMEOUT FOR CONNECTS ON THIS SESSION - - char * ptr, *Context; - int newvalue; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Get new value - - newvalue = atoi(ptr); - if (newvalue > 20) - Session->SESSIONT1 = newvalue; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "L4TIMEOUT - %d\r", Session->SESSIONT1); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -UCHAR PWLen; -char PWTEXT[80]; - -VOID PWDCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char * ptr, *Context; - USHORT pwsum = 0; - int n = 5, p1, p2, p3, p4, p5; - - if (Session->Secure_Session) // HOST - SET AUTHORISED REGARDLESS - { - Session->PASSWORD = 0xFFFF; // SET AUTHORISED - Session->Secure_Session = 1; - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - { - // Check Password - - n = 5; - - while (n--) - pwsum += *(ptr++); - - if (Session->PASSWORD == pwsum) - { - Session->PASSWORD = 0xFFFF; // SET AUTHORISED - Session->Secure_Session = 1; - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - ReleaseBuffer((UINT *)REPLYBUFFER); - return; - } - - // SEND PASSWORD PROMPT - - if (PWLen == 0) - PWLen = 1; - - p1 = rand() % PWLen; - pwsum += PWTEXT[p1++]; - - p2 = rand() % PWLen; - pwsum += PWTEXT[p2++]; - - p3 = rand() % PWLen; - pwsum += PWTEXT[p3++]; - - p4 = rand() % PWLen; - pwsum += PWTEXT[p4++]; - - p5 = rand() % PWLen; - pwsum += PWTEXT[p5++]; - - Session->PASSWORD = pwsum; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%d %d %d %d %d\r", p1, p2, p3, p4, p5); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char * ptr, *Context; - int Port = 0, cols = NUMBEROFPORTS, i; - char * uptime; - struct PORTCONTROL * PORT = PORTTABLE; - struct PORTCONTROL * STARTPORT; - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - // SEE IF ANY PARAM - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && ptr[0]) - Port = atoi(ptr); - - // IF ASKING FOR PORT STATS, DONT DO SYSTEM ONES - - if (Port == 0) - { - uptime = FormatUptime(STATSTIME); - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", uptime); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Semaphore Get-Rel/Clashes %9d%9d\r", - Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Buffers:Max/Cur/Min/Out/Wait%9d%9d%9d%9d%9d\r", - MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Known Nodes/Max Nodes %9d%9d\r", - NUMBEROFNODES, MAXDESTS); - - Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Connects Sent/Rxed %9d%9d\r", - L4CONNECTSOUT, L4CONNECTSIN); - - Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Frames TX/RX/Resent/Reseq%9d%9d%9d%9d\r", - L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); - - Bufferptr = Cmdprintf(Session, Bufferptr, "L3 Frames Relayed %9d\r", L3FRAMES); - - if (ptr && ptr[0] == 'S') - { - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - // POSITION TO REQUESTED PORT - - if (Port) - { - while (PORT && PORT->PORTNUMBER != Port) - { - PORT = PORT->PORTPOINTER; - cols--; - } - } - - if (PORT == NULL) // REQUESTED PORT NOT FOUND - { - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - STARTPORT = PORT; - - if (cols > 7) - cols = 7; - - Bufferptr = Cmdprintf(Session, Bufferptr, " "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Port %02d ", PORT->PORTNUMBER); - PORT = PORT->PORTPOINTER; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Digied"); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2DIGIED); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Heard "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMES); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Rxed "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESFORUS); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Sent "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESSENT); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Timeouts "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2TIMEOUTS); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "REJ Frames Rxed "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2REJCOUNT); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "RX out of Seq "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2OUTOFSEQ); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Resequenced "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2RESEQ); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Undrun/Poll T/o "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2URUNC); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "RX Overruns "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2ORUNC); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "RX CRC Errors "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->RXERRORS); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Sent "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRTX); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Received "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRRX); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Frames abandoned"); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L1DISCARD); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - PORT = STARTPORT; - Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% "); - - for (i = 0; i < cols; i++) - { - Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d", PORT->AVSENDING, PORT->AVACTIVE); - PORT = PORT->PORTPOINTER; - } - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS 'LINKS' MESSAGE - - struct _LINKTABLE * LINK = LINKS; - int n = MAXLINKS; - int len; - char Normcall[11] = ""; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r"); - - while (n--) - { - if (LINK->LINKCALL[0]) - { - len = ConvFromAX25(LINK->LINKCALL, Normcall); - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); - - len = ConvFromAX25(LINK->OURCALL, Normcall); - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall); - - if (LINK->Ver2point2) - Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=2.2\r", - LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE); - else - Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r", - LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG); - } - LINK++; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - -VOID CMDS00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS 'USERS' - - int n = MAXCIRCUITS; - TRANSPORTENTRY * L4 = L4TABLE; - TRANSPORTENTRY * Partner; - int MaxLinks = MAXLINKS; - char State[12] = "", Type[12] = "Uplink"; - char LHS[50] = "", MID[10] = "", RHS[50] = ""; - char Line[100]; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%d)\r", SESSIONHDDR, QCOUNT); - - while (n--) - { - if (L4->L4USER[0]) - { - RHS[0] = MID[0] = 0; - - if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES - { - // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK - - if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END - { - // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION - // DISPLAY TO THE RIGHT FOR NOW - - strcpy(LHS, "(Closing) "); - DISPLAYCIRCUIT(L4, RHS); - goto CMDS50; - } - else - goto CMDS60; // WILL PROCESS FROM OTHER END - } - - if (L4->L4CROSSLINK == 0) - { - // Single Entry - - DISPLAYCIRCUIT(L4, LHS); - } - else - { - DISPLAYCIRCUIT(L4, LHS); - - Partner = L4->L4CROSSLINK; - - if (Partner->L4STATE == 5) - strcpy(MID, "<-->"); - else - strcpy(MID, "<~~>"); - - DISPLAYCIRCUIT(Partner, RHS); - } -CMDS50: - memset(Line, 32, 100); - memcpy(Line, LHS, (int)strlen(LHS)); - memcpy(&Line[35], MID, (int)strlen(MID)); - strcpy(&Line[40], RHS); - strcat(&Line[40], "\r"); - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Line); - } -CMDS60: - L4++; - } - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Process PORTS Message - - struct PORTCONTROL * PORT = PORTTABLE; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r"); - - while (PORT) - { - if (PORT->Hide == 0) - Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION); - - PORT = PORT->PORTPOINTER; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -char * DisplayRoute(TRANSPORTENTRY * Session, char * Bufferptr, struct ROUTE * Routes, char Verbose) -{ - char Normcall[10]; - char locked[] = " ! "; - int NodeCount; - int Percent = 0; - char PercentString[20]; - int Iframes, Retries; - char Active[10]; - int Queued; - - int Port = 0; - - int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); - - Normcall[9]=0; - - if ((Routes->NEIGHBOUR_FLAG & 1) == 1) - strcpy(locked, "!"); - else - strcpy(locked, " "); - - NodeCount = COUNTNODES(Routes); - - if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) - strcpy(Active, ">"); - else - strcpy(Active, " "); - - if (Verbose) - { - if (Routes->NEIGHBOUR_LINK) - Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); // SEE HOW MANY QUEUED - else - Queued = 0; - - Iframes = Routes->NBOUR_IFRAMES; - Retries = Routes->NBOUR_RETRIES; - - if (Iframes) - { - Percent = (Retries * 100) / Iframes; - sprintf(PercentString, "%3d%%", Percent); - } - else - strcpy(PercentString, " "); - - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%2d %s %3d %3d%s%4d %4d %s %d %d %02d:%02d %d %d", - Active, Routes->NEIGHBOUR_PORT, Normcall, - Routes->NEIGHBOUR_QUAL, NodeCount, locked, Iframes, Retries, PercentString, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, - Routes->NEIGHBOUR_TIME >> 8, (Routes->NEIGHBOUR_TIME) & 0xff, Queued, Routes->OtherendsRouteQual); - - // IF INP3 DISPLAY SRTT - - if (Routes->INP3Node) // INP3 Enabled? - { - double srtt = Routes->SRTT/1000.0; - double nsrtt = Routes->NeighbourSRTT/1000.0; - - Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt); - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s %d %s %d %d%s\r", - Active, Routes->NEIGHBOUR_PORT, Normcall, Routes->NEIGHBOUR_QUAL, NodeCount, locked); - } - - return Bufferptr; -} - - -VOID CMDR00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - struct ROUTE * Routes = NEIGHBOURS; - int MaxRoutes = MAXNEIGHBOURS; - char locked[] = " ! "; - int Percent = 0; - char * ptr, * Context; - char Verbose = 0; - int Port = 0; - char AXCALL[7]; - BOOL Found; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr && (int)strlen(ptr) > 1) - { - // Route Update - - goto ROUTEUPDATE; - } - - if (ptr) - { - Verbose = ptr[0]; - ptr = strtok_s(NULL, " ", &Context); - if (ptr) - Port = atoi(ptr); - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Routes\r"); - - while (MaxRoutes--) - { - if (Routes->NEIGHBOUR_CALL[0] != 0) - if (Port == 0 || Port == Routes->NEIGHBOUR_PORT) - Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose); - - Routes++; - } - goto SendReply; - -ROUTEUPDATE: - - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - goto SendReply; - } - - // Line is - - // ROUTES G8BPQ-2 2 100 - Set quality to 100 - // ROUTES G8BPQ-2 2 ! - Toggle 'Locked Route' flag - // ROUTES G8BPQ-2 2 100 ! - Set quality and toggle 'locked' flag - - - ConvToAX25(ptr, AXCALL); - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number Missing \r"); - goto SendReply; - } - - Found = FindNeighbour(AXCALL, Port, &Routes); - - if (Context && Context[0] > 32) - { - // More Params - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - { - // Adding - - memcpy(Routes->NEIGHBOUR_CALL, AXCALL, 7); // In case Add - Routes->NEIGHBOUR_PORT = Port; - Found = TRUE; - } - - if (strcmp(ptr, "!") == 0) - { - // Toggle Lock - - Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT - goto Displayit; - } - - if (strcmp(ptr, "Z") == 0) - { - // Clear Counts - - Routes->NBOUR_IFRAMES = 0; - Routes->NBOUR_RETRIES = 0; - goto Displayit; - } - - Routes->NEIGHBOUR_QUAL = atoi(ptr); - - if (Context && Context[0] == '!') - { - // Toggle Lock - - Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT - goto Displayit; - } - } - -Displayit: - - // Just display - - if (Found) - Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); - - - -/* MOV ROUTEDISP,1 - - CMP BYTE PTR [ESI],20H - JE SHORT JUSTDISPLAY - - MOV ZAPFLAG,0 - - CMP BYTE PTR [ESI],'Z' - JNE SHORT NOTZAP - - MOV ZAPFLAG,1 - JMP SHORT JUSTDISPLAY - - PUBLIC NOTZAP -NOTZAP: - - MOV ROUTEDISP,2 ; LOCK UPDATE - - CMP BYTE PTR [ESI],'!' - JE SHORT JUSTDISPLAY -; -; LOOK FOR V FOR ADDING A DIGI -; - CMP WORD PTR [ESI],' V' ; V [SPACE] - JE ADDDIGI - - CALL GETVALUE ; GET NUMBER, UP TO SPACE , CR OR OFFH - JC SHORT BADROUTECMD ; INVALID DIGITS - - MOV NEWROUTEVAL,AL - - MOV ROUTEDISP,0 - - CALL SCAN ; SEE IF ! - MOV AH,[ESI] - - - PUBLIC JUSTDISPLAY -JUSTDISPLAY: - - - MOV ESI,OFFSET32 AX25CALL - CALL _FINDNEIGHBOUR - JZ SHORT FOUNDROUTE ; IN LIST - OK - - CMP EBX,0 - JE SHORT BADROUTECMD ; TABLE FULL?? - - MOV ECX,7 - MOV EDI,EBX - REP MOVSB ; PUT IN CALL - - MOV AL,SAVEPORT - MOV NEIGHBOUR_PORT[EBX],AL - - JMP SHORT FOUNDROUTE - - - PUBLIC BADROUTECMD -BADROUTECMD: - - POP EDI - - JMP PBADVALUE - - PUBLIC FOUNDROUTE -FOUNDROUTE: - - CMP ZAPFLAG,1 - JNE SHORT NOTCLEARCOUNTS - - XOR AX,AX - MOV ES:WORD PTR NBOUR_IFRAMES[EDI],AX - MOV ES:WORD PTR NBOUR_IFRAMES+2[EDI],AX - MOV ES:WORD PTR NBOUR_RETRIES[EDI],AX - MOV ES:WORD PTR NBOUR_RETRIES+2[EDI],AX - - JMP SHORT NOUPDATE - - PUBLIC NOTCLEARCOUNTS -NOTCLEARCOUNTS: - - CMP ROUTEDISP,1 - JE SHORT NOUPDATE - - CMP ROUTEDISP,2 - JE SHORT LOCKUPDATE - - MOV AL,NEWROUTEVAL - MOV NEIGHBOUR_QUAL[EBX],AL - - CMP AH,'!' - JNE SHORT NOUPDATE - - PUBLIC LOCKUPDATE -LOCKUPDATE: - - XOR NEIGHBOUR_FLAG[EBX],1 ; FLIP LOCKED BIT - - PUBLIC NOUPDATE -NOUPDATE: - - MOV ESI,EBX - POP EDI - - POP EBX - CALL DISPLAYROUTE - - JMP SENDCOMMANDREPLY - - PUBLIC ADDDIGI -ADDDIGI: - - ADD ESI,2 - PUSH ESI ; SAVE INPUT BUFFER - - MOV ESI,OFFSET32 AX25CALL - CALL _FINDNEIGHBOUR - - POP ESI - - JZ SHORT ADD_FOUND ; IN LIST - OK - - JMP BADROUTECMD - - PUBLIC ADD_FOUND -ADD_FOUND: - - CALL CONVTOAX25 ; GET DIGI CALLSIGN - - PUSH ESI - - MOV ESI,OFFSET32 AX25CALL - LEA EDI,NEIGHBOUR_DIGI[EBX] - MOV ECX,7 - REP MOVSB - - POP ESI ; MSG BUFFER -; -; SEE IF ANOTHER DIGI -; - CMP BYTE PTR [ESI],20H - JE SHORT NOMORE - - CALL CONVTOAX25 ; GET DIGI CALLSIGN - MOV ESI,OFFSET32 AX25CALL - LEA EDI,NEIGHBOUR_DIGI+7[EBX] - MOV ECX,7 - REP MOVSB - - PUBLIC NOMORE -NOMORE: - - JMP NOUPDATE - - - -*/ - -SendReply: - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - -VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS LISTEN COMMAND - - // for monitoring a remote ax.25 port - - int Port = 0, index =0; - unsigned int ListenMask = 0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - char ListenPortList[128] = ""; - - ptr = strtok_s(CmdTail, " ,", &Context); - - // Now accepts a list of ports - - if (ptr == 0 || memcmp(ptr, "OFF", 3) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Listening disabled\r"); - Session->LISTEN = 0; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - while (ptr) - { - Port = atoi(ptr); - - if (Port == 0 && NUMBEROFPORTS == 1) - Port = 1; - - ptr = strtok_s(NULL, ", ", &Context); // Get Unproto String - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port %d\r", Port); - continue; - } - - if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is not an ax.25 port\r", Port); - continue; - } - - if (PORT->PORTL3FLAG) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is for internode traffic only\r", Port); - continue; - } - - if (Session->L4CIRCUITTYPE == L2LINK + UPLINK) - { - if (Session->L4TARGET.LINK->LINKPORT->PORTNUMBER == Port) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "You can't Listen to the port you are connected on\r"); - continue; - } - } - - sprintf(ListenPortList, "%s %d", ListenPortList, Port); - - - ListenMask |= (1 << (Port - 1)); - } - - Session->LISTEN = ListenMask; - - if (ListenMask) - { - if (CountBits(ListenMask) == 1) - Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on port%s. Use CQ to send a beacon, LIS to disable\r", ListenPortList); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on ports%s. Use LIS to disable\r", ListenPortList); - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID UNPROTOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS UNPROTO COMMAND - - int Port = 0, index =0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - UCHAR axcalls[64]; - BOOL Stay, Spy; - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port == 0 && NUMBEROFPORTS == 1) - Port = 1; - else - ptr = strtok_s(NULL, " ", &Context); // Get Unproto String - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Destination missing\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - ptr[strlen(ptr)] = ' '; // Put param back together - - if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PORTL3FLAG) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Copy Address Info to Session Record - - Session->UNPROTO = Port; - Session->UAddrLen = (int)strlen(axcalls); - memcpy(Session->UADDRESS, axcalls, 63); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Unproto Mode - enter ctrl/z or /ex to exit\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -VOID CALCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS CAL COMMAND - - int Port = 0, index = 0, Count = 0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port == 0 && NUMBEROFPORTS == 1) - Port = 1; - else - ptr = strtok_s(NULL, " ", &Context); // Get Unproto String - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Count Missing\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Count = atoi(ptr); - - ptr = strtok_s(NULL, " ", &Context); // Get Unproto String - - Bufferptr = Cmdprintf(Session, Bufferptr, "Ok\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - - -VOID CQCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // Send a CQ Beacon on a radio port. Must be in LISTEN state - - DIGIMESSAGE Msg; - int Port = 0; - int OneBits = 0; - unsigned int MaskCopy = Session->LISTEN; - int Len; - UCHAR CQCALL[7]; - char Empty[] = ""; - char * ptr1 = &OrigCmdBuffer[3]; - UCHAR * axptr = &Msg.DIGIS[0][0]; - char * ptr2, *Context; - - while (MaskCopy) - { - if (MaskCopy & 1) - OneBits++; - - Port++; - MaskCopy = MaskCopy >> 1; - } - - if (OneBits == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "You must enter LISTEN before calling CQ\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (OneBits > 1) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "You can't call CQ if LISTENing on more than one port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - Len = (int)strlen(OrigCmdBuffer) - 3; - - if (Len < 0) - Len = 0; - - memset(&Msg, 0, sizeof(Msg)); - - Msg.PORT = Port; - Msg.CTL = 3; // UI - - // see if a Via specified - - if (_memicmp(ptr1, "via ", 4) == 0) - { - ptr2 = strtok_s(ptr1 + 4, ",", &Context); - - while (ptr2) - { - if (ConvToAX25(ptr2, axptr) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid via string\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - axptr += 7; - - if (axptr == &Msg.DIGIS[7][0]) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Too many digis\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - ptr1 = ptr2; - ptr2 = strtok_s(NULL, ",", &Context); - } - - // ptr1 is start of last digi call. We need to position to data - - ptr1 = strchr(ptr1, ' '); - - if (ptr1 == NULL) - ptr1 = Empty; - else - ptr1++ ; // to message - - Len = (int)strlen(ptr1); - - } - - ConvToAX25("CQ", CQCALL); - memcpy(Msg.DEST, CQCALL, 7); - memcpy(Msg.ORIGIN, Session->L4USER, 7); - Msg.ORIGIN[6] ^= 0x1e; // Flip SSID - Msg.PID = 0xf0; // Data PID - memcpy(&Msg.L2DATA, ptr1, Len); - - Send_AX_Datagram(&Msg, Len + 2, Port); // Len is Payload ie CTL, PID and Data - - Bufferptr = Cmdprintf(Session, Bufferptr, "CQ sent\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - -} - - -TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr) -{ - TRANSPORTENTRY * NewSess = L4TABLE; - int Index = 0; - - while (Index < MAXCIRCUITS) - { - if (NewSess->L4USER[0] == 0) - { - // Got One - - Session->L4CROSSLINK = NewSess; - NewSess->L4CROSSLINK = Session; - - memcpy(NewSess->L4USER, Session->L4USER, 7); - memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7); - - - NewSess->CIRCUITINDEX = Index; //OUR INDEX - NewSess->CIRCUITID = NEXTID; - - NEXTID++; - if (NEXTID == 0) - NEXTID++; // kEEP nON-ZERO - - NewSess->SESSIONT1 = Session->SESSIONT1; - NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; - - return NewSess; - } - Index++; - NewSess++; - } - - if (Bufferptr) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - } - - return NULL; -} - - -VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy) -{ - TRANSPORTENTRY * NewSess; - - NewSess = SetupNewSession(Session, Bufferptr); - - if (NewSess == NULL) - return; // Tables Full - - NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK; - - NewSess->L4TARGET.DEST = Dest; - NewSess->L4STATE = 2; // CONNECTING - - NewSess->SPYFLAG = Spy; - - ReleaseBuffer((UINT *)REPLYBUFFER); - - SENDL4CONNECT(NewSess); - - L4CONNECTSOUT++; - - return; -} - -BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK) -{ - struct _LINKTABLE * LINK = LINKS; - struct _LINKTABLE * FIRSTSPARE = NULL; - int n = MAXLINKS; - - while (n--) - { - if (LINK->LINKCALL[0] == 0) // Spare - { - if (FIRSTSPARE == NULL) - FIRSTSPARE = LINK; - - LINK++; - continue; - } - - if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall)) - { - *REQLINK = LINK; - return TRUE; - } - - LINK++; - } - // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL - - *REQLINK = FIRSTSPARE; - return FALSE; -} - -VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD); - -VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS CONNECT COMMAND - - TRANSPORTENTRY * NewSess; - - int CONNECTPORT, Port; - BOOL CallEvenIfInNodes = FALSE; - char * ptr, *Context; - UCHAR axcalls[64]; - UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted - int ret; - struct PORTCONTROL * PORT = PORTTABLE; - struct _LINKTABLE * LINK; - int CQFLAG = 0; // NOT CQ CALL - BOOL Stay, Spy; - int n; - char TextCall[10]; - int TextCallLen; - char PortString[10]; - char cmdCopy[256]; - struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;; - - -#ifdef EXCLUDEBITS - - if (CheckExcludeList(Session->L4USER) == FALSE) - { - // CONNECTS FROM THIS STATION ARE NOT ALLOWED - - ReleaseBuffer((UINT *)REPLYBUFFER); - return; - } - -#endif - - if (Session->LISTEN) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Can't connect while listening\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - CONNECTPORT = 0; // NO PORT SPECIFIED - - ptr = strtok_s(CmdTail, " ", &Context); - - strcpy(cmdCopy, Context); // Save in case Telnet Connect - - if (ptr == 0) - { - // No param - - if (CFLAG) // C Command Disabled ? - { - // Convert to HOST (appl 32) command - - //MOV _CMDPTR,OFFSET32 _HOSTCMD - //MOV _ALIASPTR,OFFSET32 _HOSTCMD + 32 * 31 - - //MOV _APPLMASK, 80000000H ; Internal Term - - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Port = atoi(ptr); - - if (Port) - { - // IF THERE IS NOTHING FOLLOWING THE NUMBER, ASSUME IT IS A - // NUMERIC ALIAS INSTEAD OF A PORT - - sprintf(PortString, "%d", Port); - - if (strlen(PortString) < (int)strlen(ptr)) - goto NoPort; - - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - EXTPORT = (struct _EXTPORTDATA *)PORT; - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr == 0) - { - // No param - - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - CONNECTPORT = Port; - - if (strcmp(ptr, "CMS") == 0 || strcmp(ptr, "HOST") == 0) // In case someeone has CMS or HOST as an alias - goto Downlink; - - } - -NoPort: - - ptr[strlen(ptr)] = ' '; // Put param back together - - if (ptr[0] == '!') - { - CallEvenIfInNodes = TRUE; - ptr++; - } - - if (memcmp(ptr, "RELAY ", 5) == 0 || memcmp(ptr, "SYNC ", 5) == 0) - { - // c p relay with extra parms - - goto Downlink; - } - - // Skip call validation if using a ptc to allow 1:call, 2:call format - - if (PORT->PROTOCOL == 10 && memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) - { - char * p; - - if (p = strstr(cmdCopy, " S ")) - { - Stay = TRUE; - p++; - *p = ' '; - } - - if (p = strstr(cmdCopy, " Z ")) - { - Spy = TRUE; - p++; - *p = ' '; - } - - goto Downlink; - } - else - { - if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - Session->STAYFLAG = Stay; - - TextCallLen = ConvFromAX25(axcalls, TextCall); - - if (CallEvenIfInNodes) - goto Downlink; - - // SEE IF CALL TO ANY OF OUR HOST SESSIONS - UNLESS DIGIS SPECIFIED - - if (axcalls[7] == 0) - { - // If this connect is as a result of a command alias, don't check appls or we will loop - - if (ALIASINVOKED == 0) - { - APPLCALLS * APPL = APPLCALLTABLE; - int n = NumberofAppls; - APPLMASK = 1; - - while (n--) - { - if (memcmp(axcalls, APPL->APPLALIAS, 6) == 0 || CompareCalls(axcalls, APPL->APPLCALL)) - { - // Call to an appl - - // Convert to an APPL command, so any alias is actioned - - // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND - - if (APPL->APPLHASALIAS && APPL->APPLALIASVAL[0] != 0x20) - { - // COPY ALIAS TO COMMAND _BUFFER, THEN REENTER COMMAND HANDLER - - memcpy(COMMANDBUFFER, APPL->APPLALIASVAL, ALIASLEN); - COMMANDBUFFER[80] = 0; - _strupr(COMMANDBUFFER); - memcpy(OrigCmdBuffer, APPL->APPLALIASVAL, ALIASLEN); // In case original case version needed - - ALIASINVOKED = TRUE; // To prevent Alias Loops - } - else - { - - // Copy Appl Command to Command Buffer - - memcpy(COMMANDBUFFER, APPL->APPLCMD, 12); - COMMANDBUFFER[12] = 13; - } - DoTheCommand(Session); - return; - } - APPL++; - APPLMASK <<= 1; - } - } - } - - if (axcalls[7] == 0) - { - // SEE IF CALL TO ANOTHER NODE - - struct DEST_LIST * Dest = DESTS; - int n = MAXDESTS; - - if (axcalls[6] == 0x60) // if SSID, dont check aliases - { - while (n--) - { - if (memcmp(Dest->DEST_ALIAS, TextCall, 6) == 0) - { - DoNetromConnect(Session, Bufferptr, Dest, Spy); - return; - } - Dest++; - } - } - - Dest = DESTS; - n = MAXDESTS; - - while (n--) - { - if (CompareCalls(Dest->DEST_CALL, axcalls)) - { - DoNetromConnect(Session, Bufferptr, Dest, Spy); - return; - } - Dest++; - } - } - - // Must be Downlink Connect - -Downlink: - - if (CONNECTPORT == 0 && NUMBEROFPORTS > 1) - { - // L2 NEEDS PORT NUMBER - - Bufferptr = Cmdprintf(Session, Bufferptr, "Downlink connect needs port number - C P CALLSIGN\r"); - - // Send Port List - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // ENSURE PORT IS AVAILABLE FOR L2 USE - - if (PORT->PROTOCOL >= 10) // Pactor=-style port? - { - int count; - - // if Via PACTOR ARDOP WINMOR or VARA, convert to attach and call = Digi's are in AX25STRING (+7) - - if (memcmp(&axcalls[7], &WINMOR[0], 6) == 0 || - memcmp(&axcalls[7], &ARDOP[0], 6) == 0 || - memcmp(&axcalls[7], &VARA[0], 6) == 0 || - memcmp(&axcalls[7], &PACTORCALL[0], 6) == 0) - { - char newcmd[80]; - - TextCall[TextCallLen] = 0; - sprintf(newcmd, "%s %s", CmdTail, TextCall); - - ATTACHCMD(Session, Bufferptr, newcmd, NULL); - return; - } - - // If on a KAM or SCS with ax.25 on port 2, do an Attach command, then pass on connect - - if (EXTPORT->MAXHOSTMODESESSIONS <= 1) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set - - if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - count = EXTPORT->MAXHOSTMODESESSIONS; - count--; // First is Pactor Stream, count is now last ax.25 session - - while (count) - { - if (EXTPORT->ATTACHEDSESSIONS[count] == 0) - { - int Paclen, PortPaclen; - struct DATAMESSAGE * Buffer; - struct DATAMESSAGE Message = {0}; - char Callstring[80]; - int len; - - // Found a free one - use it - - // See if TNC is OK - - Message.PORT = count; - - ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); - - if ((ret & 0xff00) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK - - NewSess = SetupNewSession(Session, Bufferptr); - if (NewSess == NULL) - return; - - // if a UZ7HO port, and the uplink is L2 or Uz7HO invert SSID bits - - // We only get here if multisession - - if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip; - - if ((Session->L4CIRCUITTYPE & BPQHOST))// host - goto noFlip; - - if ((Session->L4CIRCUITTYPE & PACTOR)) - { - // incoming is Pactorlike - see if UZ7HO - - if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip; - else - NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip - } - else - - // Must be L2 uplink - flip - - NewSess->L4USER[6] ^= 0x1e; // Flip SSID -noFlip: - EXTPORT->ATTACHEDSESSIONS[count] = NewSess; - - NewSess->KAMSESSION = count; - - // Set paclen to lower of incoming and outgoing - - Paclen = Session->SESSPACLEN; // Incoming PACLEN - - if (Paclen == 0) - Paclen = 256; // 0 = 256 - - PortPaclen = PORT->PORTPACLEN; - - if (PortPaclen == 0) - PortPaclen = 256; // 0 = 256 - - if (PortPaclen < Paclen) - Paclen = PortPaclen; - - NewSess->SESSPACLEN = Paclen; - Session->SESSPACLEN = Paclen; - - NewSess->L4STATE = 5; - NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; - NewSess->L4TARGET.PORT = PORT; - - // Send the connect command to the TNC - - Buffer = REPLYBUFFER; - - Buffer->PORT = count; - Buffer->PID = 0xf0; - - // if on Telnet Port convert use original cmd tail - - // Why just on telnet - what not all ports?? - - if (memcmp(EXTPORT->PORT_DLL_NAME, "TELNET", 6) == 0 || memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0) - { - NewSess->Secure_Session = Session->Secure_Session; - len = sprintf(Callstring,"C %s", cmdCopy); - } - else - { - TextCall[TextCallLen] = 0; - - len = sprintf(Callstring,"C %s", TextCall); - - if (axcalls[7]) - { - int digi = 7; - - // we have digis - - len += sprintf(&Callstring[len], " via"); - - while (axcalls[digi]) - { - TextCall[ConvFromAX25(&axcalls[digi], TextCall)] = 0; - len += sprintf(&Callstring[len], " %s", TextCall); - digi += 7; - } - } - } - Callstring[len++] = 13; - Callstring[len] = 0; - - Buffer->LENGTH = len + MSGHDDRLEN + 1; - memcpy(Buffer->L2DATA, Callstring, len); - C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); - - return; - } - count--; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if ((Session->L4CIRCUITTYPE & BPQHOST) == 0 && PORT->PORTL3FLAG) - { - //Port only for L3 - - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PortUIONLY) - { - //Port only for UI - - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for UI traffic only\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - Can't make ax.25 calls with SSID of T or R\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Get Session Entry for Downlink - - NewSess = SetupNewSession(Session, Bufferptr); - if (NewSess == NULL) - return; - - NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK; - - // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION - - memcpy(ourcall, NewSess->L4USER, 7); - - // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO) - - if ((Session->L4CIRCUITTYPE & BPQHOST))// host - goto noFlip3; - - if ((Session->L4CIRCUITTYPE & PACTOR)) - { - // incoming is Pactorlike - see if UZ7HO - - if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip3; - - if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession - goto noFlip3; - - ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip - } - else - - // Must be L2 uplink - flip - - ourcall[6] ^= 0x1e; // Flip SSID - -noFlip3: - - // SET UP NEW SESSION (OR RESET EXISTING ONE) - - FindLink(axcalls, ourcall, Port, &LINK); - - if (LINK == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - // Should release NewSess - - return; - } - - memcpy(LINK->LINKCALL, axcalls, 7); - memcpy(LINK->OURCALL, ourcall, 7); - - LINK->LINKPORT = PORT; - - LINK->L2TIME = PORT->PORTT1; - - // Copy Digis - - n = 7; - ptr = &LINK->DIGIS[0]; - - while (axcalls[n]) - { - memcpy(ptr, &axcalls[n], 7); - n += 7; - ptr += 7; - - LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI - } - - LINK->LINKTYPE = 2; // DOWNLINK - LINK->LINKWINDOW = PORT->PORTWINDOW; - - RESET2(LINK); // RESET ALL FLAGS - - if (CMD->String[0] == 'N' && SUPPORT2point2) - LINK->L2STATE = 1; // New (2.2) send XID - else - LINK->L2STATE = 2; // Send SABM - - LINK->CIRCUITPOINTER = NewSess; - - NewSess->L4TARGET.LINK = LINK; - - if (PORT->PORTPACLEN) - NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN; - - if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM - { - if (LINK->L2STATE == 1) - L2SENDXID(LINK); - else - SENDSABM(LINK); - } - ReleaseBuffer((UINT *)REPLYBUFFER); - return; -} - -BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls) -{ - // CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN - // CONVERTED STRING IN AXCALLS. Return FALSE if invalied - - char * axptr = AXCalls; - char * ptr, *Context; - int CQFLAG = 0; // NOT CQ CALL - int n = 8; // Max digis - - *Stay = 0; - *Spy = 0; - - memset(AXCalls, 0, 64); - - ptr = strtok_s(Calls, " ,", &Context); - - if (ptr == NULL) - return FALSE; - - // First field is Call - - if (ConvToAX25(ptr, axptr) == 0) - return FALSE; - - axptr += 7; - - ptr = strtok_s(NULL, " ,", &Context); - - while (ptr && n--) - { - // NEXT FIELD = COULD BE CALLSIGN, VIA, OR S (FOR STAY) - - if (strcmp(ptr, "S") == 0) - *Stay = TRUE; - else if (strcmp(ptr, "Z") == 0) - *Spy = TRUE; - else if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0) - { - } //skip via - else - { - // Convert next digi - - if (ConvToAX25(ptr, axptr) == 0) - return FALSE; - - axptr += 7; - } - - ptr = strtok_s(NULL, " ,", &Context); - } - - return TRUE; -} - - -VOID LINKCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // PROCESS *** LINKED to CALLSIGN - - char * ptr, *Context; - UCHAR axcall[7]; - int ret; - - if (LINKEDFLAG == 'Y' || // UNCONDITIONAL? - (LINKEDFLAG == 'A' && - ((Session->L4CIRCUITTYPE & BPQHOST) || Session->Secure_Session || Session->PASSWORD == 0xffff))) - { - ptr = strtok_s(CmdTail, " ", &Context); - if (ptr) - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - { - ret = ConvToAX25Ex(ptr, axcall); - - if (ret) - { - memcpy(Session->L4USER, axcall, 7); - strcpy(Bufferptr, OKMSG); - Bufferptr += (int)strlen(OKMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - - strcpy(Bufferptr, BADMSG); - Bufferptr += (int)strlen(BADMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - memcpy(Bufferptr, PASSWORDMSG, LPASSMSG); - Bufferptr += LPASSMSG; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -int CompareNode(const void *a, const void *b); -int CompareAlias(const void *a, const void *b); - -char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest) -{ - char Normcall[10]; - char Alias[10]; - struct NR_DEST_ROUTE_ENTRY * NRRoute; - struct DEST_ROUTE_ENTRY * Route; - struct ROUTE * Neighbour; - int i, Active, len; - - Alias[6] = 0; - - memcpy(Alias, Dest->DEST_ALIAS, 6); - strlop(Alias, ' '); - - Normcall[ConvFromAX25(Dest->DEST_CALL, Normcall)] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Routes to: %s:%s", Alias, Normcall); - - if (Dest->DEST_COUNT) - Bufferptr = Cmdprintf(Session, Bufferptr, " RTT=%4.2f FR=%d %c %.1d\r", - Dest->DEST_RTT /1000.0, Dest->DEST_COUNT, - (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - NRRoute = &Dest->NRROUTE[0]; - - Active = Dest->DEST_ROUTE; - - for (i = 1; i < 4; i++) - { - Neighbour = NRRoute->ROUT_NEIGHBOUR; - - if (Neighbour) - { - len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); - Normcall[len] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %d %d %s\r", - (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); - } - NRRoute++; - } - - // DISPLAY INP3 ROUTES - - Route = &Dest->ROUTE[0]; - - Active = Dest->DEST_ROUTE; - - for (i = 1; i < 4; i++) - { - Neighbour = Route->ROUT_NEIGHBOUR; - - if (Neighbour) - { - double srtt = Route->SRTT/1000.0; - - len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); - Normcall[len] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %4.2fs %d %s\r", - (Active == i + 3)?'>':' ',Route->Hops, srtt, Neighbour->NEIGHBOUR_PORT, Normcall); - } - Route++; - } - - return Bufferptr; -} - - -int DoViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) -{ - char Portcall[10]; - int len; - - if (Dest->NRROUTE[n].ROUT_NEIGHBOUR != 0 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 0) - { - len=ConvFromAX25(Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); - Portcall[len]=0; - - len=sprintf(&line[cursor],"%s %d %d ", - Portcall, - Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, - Dest->NRROUTE[n].ROUT_QUALITY); - - cursor+=len; - - if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) - { - len=sprintf(&line[cursor],"! "); - cursor+=len; - } - } - return cursor; -} - -int DoINP3ViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor) -{ - char Portcall[10]; - int len; - double srtt; - - if (Dest->ROUTE[n].ROUT_NEIGHBOUR != 0) - { - srtt = Dest->ROUTE[n].SRTT/1000.0; - - len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); - Portcall[len]=0; - - len=sprintf(&line[cursor],"%s %d %d %4.2fs ", - Portcall, - Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, - Dest->ROUTE[n].Hops, srtt); - - cursor+=len; - - if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127) - { - len=sprintf(&line[cursor],"! "); - cursor+=len; - } - } - return cursor; -} - -int WildCmp(char * pattern, char * string) -{ - // Check if string is at end or not. - - if (*pattern == '\0') - return *string == '\0'; - - // Check for single character missing or match - - if (*pattern == '?' || *pattern == *string) - return *string != '\0' && WildCmp(pattern + 1, string + 1); - - if (*pattern == '*') - { - // Check for multiple character missing - - return WildCmp(pattern + 1, string) || (*string != '\0' && WildCmp(pattern, string + 1)); - } - - return 0; -} - -VOID CMDN00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - struct DEST_LIST * Dest = DESTS; - int count = MAXDESTS, i; - char Normcall[10]; - char Alias[10]; - int Width = 4; - int x = 0, n = 0; - struct DEST_LIST * List[1000]; - char Param = 0; - char * ptr, * param2,* Context; - char Nodeline[21]; - char AXCALL[7]; - char * Call; - char * Qualptr; - int Qual; - char line[160]; - int cursor, len; - UCHAR axcall[7]; - int SavedOBSINIT = OBSINIT; - struct ROUTE * ROUTE = NULL; - char Pattern[80] = ""; - char * firststar; - - ptr = strtok_s(CmdTail, " ", &Context); - param2 = strtok_s(NULL, " ", &Context); - - if (ptr) - { - if (strcmp(ptr, "ADD") == 0) - goto NODE_ADD; - - if (strcmp(ptr, "DEL") == 0) - goto NODE_DEL; - - if (strcmp(ptr, "VIA") == 0) - goto NODE_VIA; - } - - if (ptr) - { - // Could be C or a pattern. Accept C pattern or pattern C - - if ((int)strlen(ptr) > 1) - { - strcpy(Pattern, ptr); - if (param2 && param2[0] == 'C') - Param = 'C'; - } - else - { - Param = ptr[0]; - if (param2) - strcpy(Pattern, param2); - } - } - - // We need to pick out CALL or CALL* from other patterns (as call use detail display) - - firststar = strchr(Pattern, '*'); - - if ((firststar && *(firststar + 1) != 0)|| strchr(Pattern, '?')) //(* not on end) - - // definitely pattern - - goto DoNodePattern; - - // If it works as CALL*, process, else drop through - - if (Pattern[0]) - { - UCHAR AXCall[8]; - int count; - int paramlen = (int)strlen(ptr); - char parampadded[20]; - int n = 0; - - Alias[8] = 0; - strcpy(parampadded, Pattern); - strcat(parampadded, " "); - - ConvToAX25(Pattern, AXCall); - - // if * on end, list all ssids - - if (firststar) - { - AXCall[6] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - while (AXCall[6] < 32) - { - Dest = DESTS; - - for (count = 0; count < MAXDESTS; count++) - { - if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) - { - break; - } - Dest++; - } - - if (count < MAXDESTS) - { - Bufferptr = DoOneNode(Session, Bufferptr, Dest); - n++; - } - - AXCall[6] += 2; - } - - if (n) // Found Some - { - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Dest = DESTS; // Reset - - // Drop through to try as pattern - } - else - { - // process as just call - - for (count = 0; count < MAXDESTS; count++) - { - if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) - { - break; - } - Dest++; - } - - if (count == MAXDESTS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Bufferptr = DoOneNode(Session, Bufferptr, Dest); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - -DoNodePattern: - - Bufferptr = Cmdprintf(Session, Bufferptr, "Nodes\r"); - - while (count--) - { - if (Dest->DEST_CALL[0] != 0) - { - if (Param != 'T' || Dest->DEST_COUNT) - List[n++] = Dest; - - if (n > 999) - break; - } - - Dest++; - } - - if (Param == 'C') - qsort(List, n, sizeof(void *), CompareNode); - else - qsort(List, n, sizeof(void *), CompareAlias); - - - for (i = 0; i < n; i++) - { - int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); - Normcall[len]=0; - - memcpy(Alias, List[i]->DEST_ALIAS, 6); - Alias[6] = 0; - strlop(Alias, ' '); - - if (strlen(Alias)) - strcat(Alias, ":"); - - if (Alias[0] == '#' && HIDENODES == 1 && Param != '*') // Hidden Node and not N * command - continue; - - if (Pattern[0]) - if (!WildCmp(Pattern, Normcall) && !WildCmp(Pattern, Alias)) - continue; - - if (Param == 'T') - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s RTT=%4.2f Frames = %d %c %.1d\r", - Alias, Normcall, List[i]->DEST_RTT /1000.0, List[i]->DEST_COUNT, - (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); - } - else - { - len = sprintf(Nodeline, "%s%s", Alias, Normcall); - memset(&Nodeline[len], ' ', 20 - len); - Nodeline[20] = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Nodeline); - - if (++x == Width) - { - x = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - } - } - } - - if (x) - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - goto SendReply; - - -NODE_VIA: - - // List Nodes reachable via a neighbour - - ptr = param2; - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); - goto SendReply; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); - - ConvToAX25(ptr, AXCALL); - - Dest = DESTS; - - Dest-=1; - - for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[0].ROUT_NEIGHBOUR == 0) - continue; - - - if ((Dest->NRROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->NRROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->NRROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - - || (Dest->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) - || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))) - { - len=ConvFromAX25(Dest->DEST_CALL,Normcall); - - Normcall[len]=0; - - memcpy(Alias,Dest->DEST_ALIAS,6); - - Alias[6]=0; - - for (i=0;i<6;i++) - { - if (Alias[i] == ' ') - Alias[i] = 0; - } - - cursor=sprintf(line,"%s:%s ", Alias,Normcall); - - cursor = DoViaEntry(Dest, 0, line, cursor); - cursor = DoViaEntry(Dest, 1, line, cursor); - cursor = DoViaEntry(Dest, 2, line, cursor); - cursor = DoINP3ViaEntry(Dest, 0, line, cursor); - cursor = DoINP3ViaEntry(Dest, 1, line, cursor); - cursor = DoINP3ViaEntry(Dest, 2, line, cursor); - - line[cursor++]='\r'; - line[cursor++]=0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", line); - } - } - - - goto SendReply; - -NODE_ADD: - - // FORMAT IS NODE ADD ALIAS:CALL QUAL ROUTE PORT - - - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - goto SendReply; - } - - ptr = param2; - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); - goto SendReply; - } - - Call = strlop(ptr, ':'); - - if (Call == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r"); - goto SendReply; - } - - - ConvToAX25(Call, AXCALL); - - Qualptr = strtok_s(NULL, " ", &Context); - - if (Qualptr == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Quality missing\r"); - goto SendReply; - } - - Qual = atoi(Qualptr); - - if (Qual < MINQUAL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Quality is below MINQUAL\r"); - goto SendReply; - } - - if (FindDestination(AXCALL, &Dest)) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Node already in Table\r"); - goto SendReply; - } - - if (Dest == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Table Full\r"); - goto SendReply; - } - - memcpy(Dest->DEST_CALL, AXCALL, 7); - memcpy(Dest->DEST_ALIAS, ptr, 6); - - NUMBEROFNODES++; - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr == NULL || ptr[0] == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Neighbour missing\r"); - goto SendReply; - } - - if (ConvToAX25(ptr, axcall) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Neighbour\r"); - goto SendReply; - } - else - { - int Port; - - ptr = strtok_s(NULL, " ", &Context); - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Port missing\r"); - goto SendReply; - } - - Port = atoi(ptr); - - if (Context[0] == '!') - { - OBSINIT = 255; //; SPECIAL FOR LOCKED - } - - if (FindNeighbour(axcall, Port, &ROUTE)) - { - PROCROUTES(Dest, ROUTE, Qual); - } - - OBSINIT = SavedOBSINIT; - - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Added\r"); - goto SendReply; - } - - - - -/* -PNODE48: - - -; GET NEIGHBOURS FOR THIS DESTINATION -; - CALL CONVTOAX25 - JNZ SHORT BADROUTE -; - CALL GETVALUE - MOV SAVEPORT,AL ; SET PORT FOR _FINDNEIGHBOUR - - CALL GETVALUE - MOV ROUTEQUAL,AL -; - MOV ESI,OFFSET32 AX25CALL - - PUSH EBX ; SAVE DEST - CALL _FINDNEIGHBOUR - MOV EAX,EBX ; ROUTE TO AX - POP EBX - - JZ SHORT NOTBADROUTE - - JMP SHORT BADROUTE - -NOTBADROUTE: -; -; UPDATE ROUTE LIST FOR THIS DEST -; - MOV ROUT1_NEIGHBOUR[EBX],EAX - MOV AL,ROUTEQUAL - MOV ROUT1_QUALITY[EBX],AL - MOV ROUT1_OBSCOUNT[EBX],255 ; LOCKED -; - POP EDI - POP EBX - - INC _NUMBEROFNODES - - JMP SENDOK - -BADROUTE: -; -; KILL IT -; - MOV ECX,TYPE DEST_LIST - MOV EDI,EBX - MOV AL,0 - REP STOSB - - JMP BADROUTECMD - -*/ - - goto SendReply; - - -NODE_DEL: - - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - goto SendReply; - } - - ptr = param2; - - if (ptr == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r"); - goto SendReply; - } - - if (strcmp(ptr, "ALL") == 0) - { - struct DEST_LIST * DEST = DESTS; - int n = MAXDESTS; - - while (n--) - { - if (DEST->DEST_CALL[0] && ((DEST->DEST_STATE & 0x80) == 0)) // Don't delete appl node - REMOVENODE(DEST); - - DEST++; - } - - ClearNodes(); - - Bufferptr = Cmdprintf(Session, Bufferptr, "All Nodes Deleted\r"); - goto SendReply; - } - - ConvToAX25(ptr, AXCALL); - - if (FindDestination(AXCALL, &Dest) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r"); - goto SendReply; - } - - if (Dest->DEST_STATE & 0x80) - Bufferptr = Cmdprintf(Session, Bufferptr, "APPL Node - Can't delete\r"); - else - { - REMOVENODE(Dest); - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); - } - Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r"); - -SendReply: - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID CMDQUERY(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // DISPLAY AVAILABLE COMMANDS - - int n; - char * ptr; - char ApplList[2048]; - char * out = ApplList; - - CMDX * CMD = &COMMANDS[APPL1]; - - for (n = 0; n < NumberofAppls; n++) - { - ptr = &CMD->String[0]; - if (*(ptr) != '*') - { - while (*ptr != ' ') - { - *(out++) = *(ptr++); - } - *(out++) = ' '; - } - CMD++; - } - - *(out) = 0; - - n = CMDLISTLEN; - - if (NEEDMH == 0) - n -= 7; // Dont show MH - - Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s\r", ApplList, CMDLIST); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -char * FormatMH(MHSTRUC * MH, char Format); - -VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // DISPLAY HEARD LIST - - int Port = 0, sess = 0; - char * ptr, *Context, *pattern; - struct PORTCONTROL * PORT = NULL; - MHSTRUC * MH; - int count = MHENTRIES; - int n; - char Normcall[20]; - char From[10]; - char DigiList[100]; - char * Output; - int len; - char Digi = 0; - - - // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find - // how many digis there are - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - pattern = strtok_s(NULL, " ", &Context); - - if (pattern) - _strupr(pattern); // Optional filter - - MH = PORT->PORTMHEARD; - - if (MH == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "MHEARD not enabled on that port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (pattern && strstr(pattern, "CLEAR")) - { - if (Session->Secure_Session) - { - memset(MH, 0, MHENTRIES * sizeof(MHSTRUC)); - SaveMH(); - Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d Cleared\r", Port); - } - else - { - Bufferptr = Cmdprintf(Session, Bufferptr, "MH Clear needs SYSOP status\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - else - { - if (CMD->String[2] == 'V') // MHV - { - Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port); - Bufferptr = Cmdprintf(Session, Bufferptr, "Callsign Last heard Pkts RX via Digi ;) \r"); - Bufferptr = Cmdprintf(Session, Bufferptr, "--------- ----------- ------- ------------------------------------------\r"); - } - else - if (pattern) - Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d filtered by %s\r", Port, pattern); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d\r", Port); - } - while (count--) - { - if (MH->MHCALL[0] == 0) - break; - - Digi = 0; - - len = ConvFromAX25(MH->MHCALL, Normcall); - - Normcall[len++] = MH->MHDIGI; - Normcall[len++] = 0; - - if (pattern && strstr(Normcall, pattern) == 0) - { - MH++; - continue; - } - - n = 8; // Max number of digi-peaters - - ptr = &MH->MHCALL[6]; // End of Address bit - - Output = &DigiList[0]; - - if ((*ptr & 1) == 0) - { - // at least one digi - - strcpy(Output, "via "); - Output += 4; - - while ((*ptr & 1) == 0) - { - // MORE TO COME - - From[ConvFromAX25(ptr + 1, From)] = 0; - Output += sprintf((char *)Output, "%s", From); - - ptr += 7; - n--; - - if (n == 0) - break; - - // See if digi actioned - put a * on last actioned - - if (*ptr & 0x80) - { - if (*ptr & 1) // if last address, must need * - { - *(Output++) = '*'; - Digi = '*'; - } - - else - if ((ptr[7] & 0x80) == 0) // Repeased by next? - { - *(Output++) = '*'; // No, so need * - Digi = '*'; - } - -} - *(Output++) = ','; - } - *(--Output) = 0; // remove last comma - } - else - *(Output) = 0; - - // if we used a digi set * on call and display via string - - - if (Digi) - Normcall[len++] = Digi; - else - DigiList[0] = 0; // Dont show list if not used - - Normcall[len++] = 0; - - - ptr = FormatMH(MH, CMD->String[2]); - - if (CMD->String[2] == 'V') // MHV - Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %-10s %-10d %-30s\r", - Normcall, ptr, MH->MHCOUNT, DigiList); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %s %s\r", Normcall, ptr, DigiList); - - MH++; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -int Rig_Command(int Session, char * Command); - -VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - char * ptr; - - if (Rig_Command(Session->CIRCUITINDEX, CmdTail)) - { - ReleaseBuffer((UINT *)REPLYBUFFER); - return; - } - - // Error Message is in buffer - - ptr = strchr(CmdTail, 13); - - if (ptr) - { - int len = (int)(++ptr - CmdTail); - - memcpy(Bufferptr, CmdTail, len); - Bufferptr += len; - } - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - - -VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session); - - -VOID NRRCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // PROCESS 'NRR - Netrom Record Route' COMMAND - - char * ptr, *Context; - struct DEST_LIST * Dest = DESTS; - int count = MAXDESTS; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - UCHAR AXCall[8]; - int count; - - ConvToAX25(ptr, AXCall); - strcat(ptr, " "); - - for (count = 0; count < MAXDESTS; count++) - { - if (memcmp(Dest->DEST_ALIAS, ptr, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall)) - { - SendNRRecordRoute(Dest, Session); - memcpy(Bufferptr, OKMSG, 3); - Bufferptr += 3; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - } - Dest++; - } - } - Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -int CHECKINTERLOCK(struct PORTCONTROL * OURPORT) -{ - // See if any Interlocked ports are Busy - - struct PORTCONTROL * PORT = PORTTABLE; - struct _EXTPORTDATA * EXTPORT; - - int n = NUMBEROFPORTS; - int ourgroup = OURPORT->PORTINTERLOCK; - - while (PORT) - { - if (PORT != OURPORT) - { - if (PORT->PORTINTERLOCK == ourgroup) - { - // Same Group - is it busy - - int i = 0; - - EXTPORT = (struct _EXTPORTDATA *)PORT; - - while (i < 27) - if (EXTPORT->ATTACHEDSESSIONS[i++]) - return PORT->PORTNUMBER; - } - } - PORT = PORT->PORTPOINTER; - } - - return 0; -} - -VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // ATTACH to a PACTOR or similar port - - TRANSPORTENTRY * NewSess; - struct _EXTPORTDATA * EXTPORT; - struct TNCINFO * TNC; - - int Port = 0, sess = 0; - char * ptr, *Context; - int ret; - struct PORTCONTROL * PORT = NULL; - struct DATAMESSAGE Message = {0}; - int Paclen, PortPaclen; - struct DATAMESSAGE * Buffer; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL || PORT->PROTOCOL < 10) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // If attach on telnet port, find a free stream - - EXTPORT = (struct _EXTPORTDATA *)PORT; - - if (strstr(EXTPORT->PORT_DLL_NAME, "TELNET")) - { - int count = EXTPORT->MAXHOSTMODESESSIONS; - count--; // First is Pactor Stream, count is now last ax.25 session - - while (count) - { - if (EXTPORT->ATTACHEDSESSIONS[count] == 0) - { - int Paclen, PortPaclen; - struct DATAMESSAGE Message = {0}; - - // Found a free one - use it - - // See if TNC is OK - - Message.PORT = count; - - ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); - - if ((ret & 0xff00) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK - - NewSess = SetupNewSession(Session, Bufferptr); - - if (NewSess == NULL) - return; - - EXTPORT->ATTACHEDSESSIONS[count] = NewSess; - - NewSess->Secure_Session = Session->Secure_Session; - - NewSess->KAMSESSION = count; - - // Set paclen to lower of incoming and outgoing - - Paclen = Session->SESSPACLEN; // Incoming PACLEN - - if (Paclen == 0) - Paclen = 256; // 0 = 256 - - PortPaclen = PORT->PORTPACLEN; - - if (PortPaclen == 0) - PortPaclen = 256; // 0 = 256 - - if (PortPaclen < Paclen) - Paclen = PortPaclen; - - NewSess->SESSPACLEN = Paclen; - Session->SESSPACLEN = Paclen; - - NewSess->L4STATE = 5; - NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; - NewSess->L4TARGET.PORT = PORT; - - ptr = strtok_s(NULL, " ", &Context); - sess = count; - - // Replace command tail with original (before conversion to upper case - - Context = Context + (OrigCmdBuffer - COMMANDBUFFER); - - goto checkattachandcall; - - - memcpy(Bufferptr, OKMSG, 3); - Bufferptr += 3; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - } - count--; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - Message.PORT = 0; - - ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT); - - if ((ret & 0xff00) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // See if "Attach and Call" (for VHF ports) - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr && strcmp(ptr, "S") == 0) - { - Session->STAYFLAG = TRUE; - ptr = strtok_s(NULL, " ", &Context); - } - - if (ptr) - { - // we have another param - - // if it is a single char it is a channel number for vhf attach - - if (strlen(ptr) == 1) - { - // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set - - if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - sess = ptr[0] - '@'; - - if (sess < 1 || sess > EXTPORT->MAXHOSTMODESESSIONS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Invalid Channel\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - ptr = strtok_s(NULL, " ", &Context); - - if (ptr && strcmp(ptr, "S") == 0) - { - Session->STAYFLAG = TRUE; - ptr = strtok_s(NULL, " ", &Context); - } - } - } - - if (ret & 0x8000) // Disconnecting - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Check Interlock. Only ports with a TNC record can be interlocked - - TNC = PORT->TNC; - - if (TNC) - { - // See if any interlocked ports are in use - - struct TNCINFO * OtherTNC; - int i; - int rxInterlock = TNC->RXRadio; - int txInterlock = TNC->TXRadio; - - if (rxInterlock || txInterlock) - { - for (i=1; i<33; i++) - { - OtherTNC = TNCInfo[i]; - - if (OtherTNC == NULL) - continue; - - if (OtherTNC == TNC) - continue; - - if (rxInterlock == OtherTNC->RXRadio || txInterlock == OtherTNC->TXRadio) // Same Group - { - if (OtherTNC->PortRecord->ATTACHEDSESSIONS[0]) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, interlocked port %d is in use\r", i); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - } - } - } - } - - - - - if (EXTPORT->ATTACHEDSESSIONS[sess]) - { - // In use - - Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK - - NewSess = SetupNewSession(Session, Bufferptr); - - if (NewSess == NULL) - return; - - // if a UZ7HO port, and the uplink is L2 or Uz7HO and multisession, - // invert SSID bits - - if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip1; - - if (EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession - goto noFlip1; - - if ((Session->L4CIRCUITTYPE & BPQHOST)) // host - goto noFlip1; - - if ((Session->L4CIRCUITTYPE & PACTOR)) - { - // incoming is Pactorlike - see if UZ7HO - - if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) - goto noFlip1; - else - NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip - } - else - - // Must be L2 uplink - flip - - NewSess->L4USER[6] ^= 0x1e; // Flip SSID -noFlip1: - - EXTPORT->ATTACHEDSESSIONS[sess] = NewSess; - - NewSess->KAMSESSION = sess; - - // Set paclen to lower of incoming and outgoing - - Paclen = Session->SESSPACLEN; // Incoming PACLEN - - if (Paclen == 0) - Paclen = 256; // 0 = 256 - - PortPaclen = PORT->PORTPACLEN; - - if (PortPaclen == 0) - PortPaclen = 256; // 0 = 256 - - if (PortPaclen < Paclen) - Paclen = PortPaclen; - - NewSess->SESSPACLEN = Paclen; - Session->SESSPACLEN = Paclen; - NewSess->L4STATE = 5; - NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR; - NewSess->L4TARGET.PORT = PORT; - -checkattachandcall: - - if (ptr) - { - // we have a call to connect to - - char Callstring[80]; - int len; - - Buffer = REPLYBUFFER; - Buffer->PORT = sess; - Buffer->PID = 0xf0; - - len = sprintf(Callstring,"C %s", ptr); - - ptr = strtok_s(NULL, " ", &Context); - - while (ptr) // if any other params (such as digis) copy them - { - if (strcmp(ptr, "S") == 0) - { - Session->STAYFLAG = TRUE; - } - else - len += sprintf(&Callstring[len], " %s", ptr); - - ptr = strtok_s(NULL, " ", &Context); - } - - Callstring[len++] = 13; - Callstring[len] = 0; - - Buffer->LENGTH = len + MSGHDDRLEN + 1; - memcpy(Buffer->L2DATA, Callstring, len); - C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); - - return; - } - - memcpy(Bufferptr, OKMSG, 3); - Bufferptr += 3; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; -} - -// SYSOP COMMANDS - -CMDX COMMANDS[] = -{ - "SAVENODES ",8, SAVENODES, 0, - "TELRECONFIG ",4, RECONFIGTELNET, 0, - "SAVEMH ",6, SAVEMHCMD, 0, - "REBOOT ",6, REBOOT, 0, - "RIGRECONFIG ",8 , RIGRECONFIG, 0, - "RESTART ",7,RESTART,0, - "RESTARTTNC ",10,RESTARTTNC,0, - "SENDNODES ",8,SENDNODES,0, - "EXTRESTART ",10, EXTPORTVAL, offsetof(EXTPORTDATA, EXTRESTART), - "TXDELAY ",3, PORTVAL, offsetof(PORTCONTROLX, PORTTXDELAY), - "MAXFRAME ",3, PORTVAL, offsetof(PORTCONTROLX, PORTWINDOW), - "RETRIES ",3, PORTVAL, offsetof(PORTCONTROLX, PORTN2), - "FRACK ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT1), - "RESPTIME ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT2), - "PPACLEN ",3,PORTVAL, offsetof(PORTCONTROLX, PORTPACLEN), - "QUALITY ",3,PORTVAL, offsetof(PORTCONTROLX, PORTQUALITY), - "PERSIST ",2,PORTVAL, offsetof(PORTCONTROLX, PORTPERSISTANCE), - "TXTAIL ",3,PORTVAL, offsetof(PORTCONTROLX, PORTTAILTIME), - "XMITOFF ",7,PORTVAL, offsetof(PORTCONTROLX, PORTDISABLED), - "DIGIFLAG ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIFLAG), - "DIGIPORT ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIPORT), - "MAXUSERS ",4,PORTVAL, offsetof(PORTCONTROLX, USERS), - "L3ONLY ",6,PORTVAL, offsetof(PORTCONTROLX, PORTL3FLAG), - "BBSALIAS ",4,PORTVAL, offsetof(PORTCONTROLX, PORTBBSFLAG), - "VALIDCALLS ",5,VALNODES,0, - "WL2KSYSOP ",5,WL2KSYSOP,0, - "STOPPORT ",4,STOPPORT,0, - "STARTPORT ",5,STARTPORT,0, - "STOPCMS ",7,STOPCMS,0, - "STARTCMS ",8,STARTCMS,0, - - "FINDBUFFS ",4,FINDBUFFS,0, - "KISS ",4,KISSCMD,0, - "GETPORTCTEXT",9,GetPortCTEXT, 0, - -#ifdef EXCLUDEBITS - - "EXCLUDE ",4,ListExcludedCalls,0, - -#endif - - "FULLDUP ",4,PORTVAL, offsetof(PORTCONTROLX, FULLDUPLEX), - "SOFTDCD ",4,PORTVAL, offsetof(PORTCONTROLX, SOFTDCDFLAG), - "OBSINIT ",7,SWITCHVAL,(size_t)&OBSINIT, - "OBSMIN ",6,SWITCHVAL,(size_t)&OBSMIN, - "NODESINT ",8,SWITCHVAL,(size_t)&L3INTERVAL, - "L3TTL ",5,SWITCHVAL,(size_t)&L3LIVES, - "L4RETRIES ",5,SWITCHVAL,(size_t)&L4N2, - "L4TIMEOUT ",5,SWITCHVALW,(size_t)&L4T1, - "T3 ",2,SWITCHVALW,(size_t)&T3, - "NODEIDLETIME",8,SWITCHVALW,(size_t)&L4LIMIT, - "LINKEDFLAG ",10,SWITCHVAL,(size_t)&LINKEDFLAG, - "IDINTERVAL ",5,SWITCHVAL,(size_t)&IDINTERVAL, - "MINQUAL ",7,SWITCHVAL,(size_t)&MINQUAL, - "FULLCTEXT ",6,SWITCHVAL,(size_t)&FULL_CTEXT, - "HIDENODES ",8,SWITCHVAL,(size_t)&HIDENODES, - "L4DELAY ",7,SWITCHVAL,(size_t)&L4DELAY, - "L4WINDOW ",6,SWITCHVAL,(size_t)&L4DEFAULTWINDOW, - "BTINTERVAL ",5,SWITCHVAL,(size_t)&BTINTERVAL, - "PASSWORD ", 8, PWDCMD, 0, - - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, - "************", 12, APPLCMD, 0, // Apppl 32 is internal Terminal - "*** LINKED ",10,LINKCMD,0, - "CQ ",2,CQCMD,0, - "CONNECT ",1,CMDC00,0, - "NC ",2,CMDC00,0, - "BYE ",1,BYECMD,0, - "QUIT ",1,BYECMD,0, - "INFO ",1,CMDI00,0, - "VERSION ",1,CMDV00,0, - "NODES ",1,CMDN00,0, - "LINKS ",1,CMDL00,0, - "LISTEN ",3,LISTENCMD,0, - "L4T1 ",2,CMDT00,0, - "PORTS ",1,CMDP00,0, - "PACLEN ",3,CMDPAC,0, - "IDLETIME ",4,CMDIDLE,0, - "ROUTES ",1,CMDR00,0, - "STATS ",1,CMDSTATS,0, - "USERS ",1,CMDS00,0, - "UNPROTO ",2,UNPROTOCMD,0, - "? ",1,CMDQUERY,0, - "DUMP ",4,DUMPCMD,0, - "MHU ",3,MHCMD,0, // UTC Times - "MHL ",3,MHCMD,0, // Local Times - "MHV ",3,MHCMD,0, - "MHEARD ",1,MHCMD,0, - "APRS ",2,APRSCMD,0, - "ATTACH ",1,ATTACHCMD,0, - "RADIO ",3,RADIOCMD,0, - "AXRESOLVER ",3,AXRESOLVER,0, - "AXMHEARD ",3,AXMHEARD,0, - "TELSTATUS ",3,SHOWTELNET,0, - "NRR ",1,NRRCMD,0, - "PING ",2,PING,0, - "AGWSTATUS ",3,SHOWAGW,0, - "ARP ",3,SHOWARP,0, - "NAT ",3,SHOWNAT,0, - "IPROUTE ",3,SHOWIPROUTE,0, - "..FLMSG ",7,FLMSG,0 -}; - -CMDX * CMD = NULL; - -int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(CMDX); - -char * ReplyPointer; // Pointer into reply buffer - -int DecodeNodeName(char * NodeName, char * ptr) -{ - // NodeName is TABLE ENTRY WITH AX25 CALL AND ALIAS - - // Copyies 20 byte 20 DECODED NAME IN FORM ALIAS:CALL to ptr - // Returns significant length of string - - int len; - char Normcall[10]; - char * alias = &NodeName[7]; - int n = 6; - char * start = ptr; - - memset(ptr, ' ', 20); - - len = ConvFromAX25(NodeName, Normcall); - - if (*(alias) > ' ') // Does alias start with a null or a space ? - { - while (*(alias) > ' ' && n--) - { - *ptr++ = *alias++; - } - *ptr++ = ':'; - } - - memcpy(ptr, Normcall, len); - ptr += len; - - return (int)(ptr - start); -} - -char * SetupNodeHeader(struct DATAMESSAGE * Buffer) -{ - char Header[20]; - int len; - - char * ptr = &Buffer->L2DATA[0]; - - len = DecodeNodeName(MYCALLWITHALIAS, Header); - - memcpy (ptr, Header, len); - ptr += len; - - (*ptr++) = HEADERCHAR; - (*ptr++) = ' '; - - return ptr; -} - -VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len) -{ - if (Len == (4 + sizeof(void *))) // Null Packet - { - ReleaseBuffer((UINT *)Buffer); - return; - } - - Buffer->LENGTH = Len; - - C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); - - PostDataAvailable(Session); -} - - -VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) -{ - // ignore frames with single NULL (Keepalive) - - if (Buffer->LENGTH == sizeof(void *) + 5 && Buffer->L2DATA[0] == 0) - { - ReleaseBuffer(Buffer); - return; - } - - if (Buffer->LENGTH > 100) - { -// Debugprintf("BPQ32 command too long %s", Buffer->L2DATA); - ReleaseBuffer(Buffer); - return; - } - -InnerLoop: - - InnerCommandHandler(Session, Buffer); - -// See if any more commands in buffer - - if (Session->PARTCMDBUFFER) - { - char * ptr1, * ptr2; - int len; - - Buffer = Session->PARTCMDBUFFER; - - // Check that message has a CR, if not save buffer and exit - - len = Buffer->LENGTH - (4 + sizeof(void *)); - ptr1 = &Buffer->L2DATA[0]; - - ptr2 = memchr(ptr1, 13, len); - - if (ptr2 == NULL) - return; - - Session->PARTCMDBUFFER = NULL; - - goto InnerLoop; - } -} - - -VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer) -{ - char * ptr1, * ptr2, *ptr3; - int len, oldlen, newlen, rest, n; - struct DATAMESSAGE * OldBuffer; - struct DATAMESSAGE * SaveBuffer; - char c; - - // If a partial command is stored, append this data to it. - - if (Session->PARTCMDBUFFER) - { - len = Buffer->LENGTH - (sizeof(void *) + 4); - ptr1 = &Buffer->L2DATA[0]; - - OldBuffer = Session->PARTCMDBUFFER; // Old Data - - if (OldBuffer == Buffer) - { - // something has gone horribly wrong - - Session->PARTCMDBUFFER = NULL; - return; - } - - oldlen = OldBuffer->LENGTH; - - newlen = len + oldlen; - - if (newlen > 200) - { - // Command far too long - ignore previous - - OldBuffer->LENGTH = oldlen = sizeof(void *) + 4; - } - - OldBuffer->LENGTH += len; - memcpy(&OldBuffer->L2DATA[oldlen - (sizeof(void *) + 4)], Buffer->L2DATA, len); - - ReleaseBuffer((UINT *)Buffer); - - Buffer = OldBuffer; - - Session->PARTCMDBUFFER = NULL; - } - - // Check that message has a CR, if not save buffer and exit - - len = Buffer->LENGTH - (sizeof(void *) + 4); - ptr1 = &Buffer->L2DATA[0]; - - // Check for sending YAPP to Node - - if (len == 2 && ptr1[0] == 5 && ptr1[1] == 1) - { - ptr1[0] = 0x15; // NAK - - ptr1[1] = sprintf(&ptr1[2], "Node doesn't support YAPP Transfers"); - - Buffer->LENGTH += ptr1[1]; - - C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer); - PostDataAvailable(Session); - return; - } - - - ptr2 = memchr(ptr1, ';', len); - - if (ptr2 == 0) - { - ptr2 = memchr(ptr1, 13, len); - - if (ptr2 == 0) - { - // No newline - - Session->PARTCMDBUFFER = Buffer; - return; - } - } - - ptr2++; - - rest = len - (int)(ptr2 - ptr1); - - if (rest) - { - // there are chars beyond the cr in the buffer - - // see if LF after CR - - if ((*ptr2) == 10) // LF - { - ptr2++; - rest--; - } - - if (rest) // May only have had LF - { - // Get a new buffer, and copy extra data to it. - - SaveBuffer = (struct DATAMESSAGE *)GetBuff(); - - if (SaveBuffer) //`Just ignore if no buffers - { - SaveBuffer->LENGTH = rest + MSGHDDRLEN + 1; - SaveBuffer->PID = 0xf0; - memcpy(&SaveBuffer->L2DATA[0], ptr2, rest); - Session->PARTCMDBUFFER = SaveBuffer; - } - } - } - - // GET PACLEN FOR THIS CONNECTION - - CMDPACLEN = Session->SESSPACLEN; - - if (CMDPACLEN == 0) - CMDPACLEN = PACLEN; // Use default if no Session PACLEN - - // If sesion is in UNPROTO Mode, send message as a UI message - - if (Session->UNPROTO) - { - DIGIMESSAGE Msg; - int Port = Session->UNPROTO; - int Len = Buffer->LENGTH - (MSGHDDRLEN -1); // Need PID - - // First check for UNPROTO exit - ctrl/z or /ex - - if (Buffer->L2DATA[0] == 26 || (Len == 6 && _memicmp(&Buffer->L2DATA[0], "/ex", 3) == 0)) // CTRL/Z or /ex - { - REPLYBUFFER = Buffer; - - Session->UNPROTO = 0; - memset(Session->UADDRESS, 0, 64); - - // SET UP HEADER - - Buffer->PID = 0xf0; - ptr1 = SetupNodeHeader(Buffer); - memcpy(ptr1, OKMSG, 3); - ptr1 += 3; - SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); - - return; - } - - memset(&Msg, 0, sizeof(Msg)); - - Msg.PORT = Port; - Msg.CTL = 3; // UI - memcpy(Msg.DEST, Session->UADDRESS, 7); - memcpy(Msg.ORIGIN, Session->L4USER, 7); - memcpy(Msg.DIGIS, &Session->UADDRESS[7], Session->UAddrLen - 7); - memcpy(&Msg.PID, &Buffer->PID, Len); - - Send_AX_Datagram(&Msg, Len, Port); // Len is Payload - CTL, PID and Data - -// SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO); - - ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply - - return; - } - - memset(COMMANDBUFFER, 32, 80); // Clear to spaces - - ptr1 = &Buffer->L2DATA[0]; - ptr2 = &COMMANDBUFFER[0]; - ptr3 = &OrigCmdBuffer[0]; - - memset(OrigCmdBuffer, 0, 80); - n = 80; - - while (n--) - { - c = *(ptr1++) & 0x7f; // Mask paritu - - if (c == 13 || c == ';') - break; // CR - - *(ptr3++) = c; // Original Case - - c = toupper(c); - *(ptr2++) = c; - } - - - // USE INPUT MESSAGE _BUFFER FOR REPLY - - REPLYBUFFER = Buffer; - - // SET UP HEADER - - Buffer->PID = 0xf0; - ptr1 = SetupNodeHeader(Buffer); - - ReplyPointer = ptr1; - - ALIASINVOKED = 0; // Clear "Invoked by APPL ALIAS flag" - - DoTheCommand(Session); // We also call DotheCommand when we need to reprocess - eg for alias handling -} - -VOID DoTheCommand(TRANSPORTENTRY * Session) -{ - struct DATAMESSAGE * Buffer = REPLYBUFFER; - char * ptr1, * ptr2; - int n; - - ptr1 = &COMMANDBUFFER[0]; // - - n = 10; - - while ((*ptr1 == ' ' || *ptr1 == 0) && n--) - ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) - - if (n == -1) - { - // Null command - - ReleaseBuffer((UINT *)Buffer); - return; - } - - ptr2 = ptr1; // Save - - - CMD = &COMMANDS[0]; - n = 0; - - for (n = 0; n < NUMBEROFCOMMANDS; n++) - { - int CL = CMD->CMDLEN; - - ptr1 = ptr2; - - CMDPTR = CMD; - - if (n == APPL1) // First APPL command - { - APPLMASK = 1; // FOR APPLICATION ATTACH REQUESTS - ALIASPTR = &CMDALIAS[0][0]; - } - - // ptr1 is input command - - if (memcmp(CMD->String, ptr1, CL) == 0) - { - // Found match so far - check rest - - char * ptr2 = &CMD->String[CL]; - - ptr1 += CL; - - if (*(ptr1) != ' ') - { - while(*(ptr1) == *ptr2 && *(ptr1) != ' ') - { - ptr1++; - ptr2++; - } - } - - if (*(ptr1) == ' ') - { - Session->BADCOMMANDS = 0; // RESET ERROR COUNT - - // SEE IF SYSOP COMMAND, AND IF SO IF PASSWORD HAS BEEN ENTERED - - if (n < PASSCMD) - { - //NEEDS PASSWORD FOR SYSOP COMMANDS - - if (Session->PASSWORD != 0xFFFF) - { - ptr1 = ReplyPointer; - - memcpy(ptr1, PASSWORDMSG, LPASSMSG); - ptr1 += LPASSMSG; - - SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); - return; - } - } -// VALNODESFLAG = 0; // NOT VALID NODES COMMAND - - ptr1++; // Skip space - - CMD->CMDPROC(Session, ReplyPointer, ptr1, CMD); - return; - } - } - - APPLMASK <<= 1; - ALIASPTR += ALIASLEN; - - CMD++; - - } - Session->BADCOMMANDS++; - - if (Session->BADCOMMANDS > 6) // TOO MANY ERRORS - { - ReleaseBuffer((UINT *)Buffer); - Session->STAYFLAG = 0; - CLOSECURRENTSESSION(Session); - return; - } - - ptr1 = ReplyPointer; - - memcpy(ptr1, CMDERRMSG, CMDERRLEN); - ptr1 += CMDERRLEN; - - SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer)); -} - - -VOID StatsTimer() -{ - struct PORTCONTROL * PORT = PORTTABLE; - int sum; - - while(PORT) - { - sum = PORT->SENDING / 11; - PORT->AVSENDING = sum; - - sum = (PORT->SENDING + PORT->ACTIVE) /11; - PORT->AVACTIVE = sum; - - PORT->SENDING = 0; - PORT->ACTIVE = 0; - - PORT = PORT->PORTPOINTER; - } -} - - - -extern struct AXIPPORTINFO * Portlist[]; - -#define TCPConnected 4 - - -VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // DISPLAY AXIP Resolver info - - int Port = 0, index =0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - struct AXIPPORTINFO * AXPORT; - char Normcall[11]; - char Flags[10]; - struct arp_table_entry * arp; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - AXPORT = Portlist[Port]; - - if (AXPORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Resolver info for Port %d\r", Port); - - while (index < AXPORT->arp_table_len) - { - arp = &AXPORT->arp_table[index]; - - if (arp->ResolveFlag && arp->error != 0) - { - // resolver error - Display Error Code - sprintf(AXPORT->hostaddr, "Error %d", arp->error); - } - else - { - if (arp->IPv6) - Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, AXPORT->hostaddr, TRUE); - else - Format_Addr((unsigned char *)&arp->destaddr.sin_addr, AXPORT->hostaddr, FALSE); - } - - ConvFromAX25(arp->callsign, Normcall); - - Flags[0] = 0; - - if (arp->BCFlag) - strcat(Flags, "B "); - - if (arp->TCPState == TCPConnected) - strcat(Flags, "C "); - - if (arp->AutoAdded) - strcat(Flags, "A"); - - if (arp->port == arp->SourcePort) - Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d = %-.42s %s\r", - Normcall, - arp->hostname, - arp->port, - AXPORT->hostaddr, - Flags); - - else - Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d<%d = %-.42s %s\r", - Normcall, - arp->hostname, - arp->port, - arp->SourcePort, - AXPORT->hostaddr, - Flags); - - index++; - } - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - // DISPLAY AXIP Mheard info - - int Port = 0, index = 0; - char * ptr, *Context; - struct PORTCONTROL * PORT = NULL; - struct AXIPPORTINFO * AXPORT; - int n = MHENTRIES; - char Normcall[11]; - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - Port = atoi(ptr); - - if (Port) - PORT = GetPortTableEntryFromPortNum(Port); - - if (PORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - AXPORT = Portlist[Port]; - - if (AXPORT == NULL) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Mheard for Port %d\r", Port); - - while (index < MaxMHEntries) - { - if (AXPORT->MHTable[index].proto != 0) - { - char Addr[80]; - - Format_Addr((unsigned char *)&AXPORT->MHTable[index].ipaddr6, Addr, AXPORT->MHTable[index].IPv6); - - Normcall[ConvFromAX25(AXPORT->MHTable[index].callsign, Normcall)] = 0; - - Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-15s %c %-6d %-25s%c\r", Normcall, - Addr, - AXPORT->MHTable[index].proto, - AXPORT->MHTable[index].port, - asctime(gmtime( &AXPORT->MHTable[index].LastHeard )), - (AXPORT->MHTable[index].Keepalive == 0) ? ' ' : 'K'); - - Bufferptr[-3] = ' '; // Clear CR returned by asctime - } - - index++; - } - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -#pragma pack() - -extern struct TNCINFO * TNCInfo[41]; - -extern char WL2KCall[10]; -extern char WL2KLoc[7]; - -BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); -BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); - -VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - - char LastUpdated[100]; - char Name[100] = ""; - char Addr1[100] = ""; - char Addr2[100] = ""; - char City[100] = ""; - char State[100] = ""; - char Country[100] = ""; - char PostCode[100] = ""; - char Email[100] = ""; - char Website[100] = ""; - char Phone[100] = ""; - char Data[100] = ""; - char LOC[100] = ""; - BOOL Exists = TRUE; - time_t LastUpdateSecs = 0; - char * ptr1, * ptr2; - - SOCKET sock; - - int Len; - char Message[2048]; - - if (WL2KCall[0] < 33) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Winlink reporting is not configured\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER) == 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to connect to WL2K Database\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (strstr(_REPLYBUFFER, "\"ErrorMessage\":")) - Exists = FALSE; - - GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Name); - GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Addr1); - GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Addr2); - GetJSONValue(_REPLYBUFFER, "\"City\":", City); - GetJSONValue(_REPLYBUFFER, "\"State\":", State); - GetJSONValue(_REPLYBUFFER, "\"Country\":", Country); - GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", PostCode); - GetJSONValue(_REPLYBUFFER, "\"Email\":", Email); - GetJSONValue(_REPLYBUFFER, "\"Website\":", Website); - GetJSONValue(_REPLYBUFFER, "\"Phones\":", Phone); - GetJSONValue(_REPLYBUFFER, "\"Comments\":", Data); - GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", LOC); - GetJSONValue(_REPLYBUFFER, "\"Timestamp\":", LastUpdated); - - ptr1 = strchr(LastUpdated, '('); - - if (ptr1) - { - ptr2 = strchr(++ptr1, ')'); - - if (ptr2) - { - *(ptr2 - 3) = 0; // remove millisecs - LastUpdateSecs = atoi(ptr1); - - FormatTime3(LastUpdated, LastUpdateSecs); - } - } - - if (_memicmp(CmdTail, "SET ", 4) == 0) - { - if (Exists) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Record already exists in WL2K Database\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Set New Values. Any other params are values to set, separated by | - -// ptr1 = strtok_s(&CmdTail[4], ",", &Context); - -// if (ptr1 == NULL) -// goto DoReplace; - -// strcpy(Name, ptr1); - -//DoReplace: - - Len = sprintf(Message, - "\"Callsign\":\"%s\"," - "\"GridSquare\":\"%s\"," - "\"SysopName\":\"%s\"," - "\"StreetAddress1\":\"%s\"," - "\"StreetAddress2\":\"%s\"," - "\"City\":\"%s\"," - "\"State\":\"%s\"," - "\"Country\":\"%s\"," - "\"PostalCode\":\"%s\"," - "\"Email\":\"%s\"," - "\"Phones\":\"%s\"," - "\"Website\":\"%s\"," - "\"Comments\":\"%s\",", - - WL2KCall, WL2KLoc, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); - - Debugprintf("Sending %s", Message); - - sock = OpenWL2KHTTPSock(); - - if (sock) - SendHTTPRequest(sock, "api.winlink.org", 80, - "/sysop/add", Message, Len, NULL); - - closesocket(sock); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Database Updated\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (Exists) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "\rWL2K SYSOP Info for %s\r", WL2KCall); - Bufferptr = Cmdprintf(Session, Bufferptr, "Grid Square: %s\r", LOC); - Bufferptr = Cmdprintf(Session, Bufferptr, "Name: %s\r", Name); - Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 1: %s\r", Addr1); - Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 2: %s\r", Addr2); - Bufferptr = Cmdprintf(Session, Bufferptr, "City: %s\r", City); - Bufferptr = Cmdprintf(Session, Bufferptr, "State: %s\r", State); - Bufferptr = Cmdprintf(Session, Bufferptr, "Country: %s\r", Country); - Bufferptr = Cmdprintf(Session, Bufferptr, "PostCode: %s\r", PostCode); - Bufferptr = Cmdprintf(Session, Bufferptr, "Email Address: %s\r", Email); - Bufferptr = Cmdprintf(Session, Bufferptr, "Website: %s\r", Website); - Bufferptr = Cmdprintf(Session, Bufferptr, "Phone: %s\r", Phone); - Bufferptr = Cmdprintf(Session, Bufferptr, "Additional Data: %s\r", Data); - Bufferptr = Cmdprintf(Session, Bufferptr, "Last Updated: %s\r", LastUpdated); - } - else - Bufferptr = Cmdprintf(Session, Bufferptr, "No SYSOP record for %s\r", WL2KCall); - - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - -VOID CloseKISSPort(struct PORTCONTROL * PortVector); -int OpenConnection(struct PORTCONTROL * PortVector); - -VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - - struct TNCINFO * TNC; - struct TCPINFO * TCP; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - TNC = TNCInfo[portno]; - - if (!TNC || !TNC->TCPInfo) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - TCP = TNC->TCPInfo; - - TCP->CMS = 0; - TCP->CMSOK = FALSE; -#ifndef LINBPQ - CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); - SetWindowText(TCP->hCMSWnd, "CMS Off"); -#endif - Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Disabled\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - - struct TNCINFO * TNC; - struct TCPINFO * TCP; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - TNC = TNCInfo[portno]; - - if (!TNC || !TNC->TCPInfo) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - TCP = TNC->TCPInfo; - TCP->CMS = 1; -#ifndef LINBPQ - CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); -#endif - CheckCMS(TNC); - - Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Enabled\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - struct KISSINFO * KISS; - - if (PORT->PORTSTOPCODE) - { - // Port has Close Routine - - PORT->PortStopped = TRUE; - - if (PORT->PORTSTOPCODE(PORT)) - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Close Failed\r"); - - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - if (PORT->PORTTYPE != 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - KISS = (struct KISSINFO *) PORT; - - if (KISS->FIRSTPORT != KISS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - CloseKISSPort(PORT); - PORT->PortStopped = TRUE; - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - - if (portno) - { - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - struct KISSINFO * KISS; - - if (PORT->PORTSTARTCODE) - { - // Port has Open Routine - - PORT->PortStopped = FALSE; - - if (PORT->PORTSTARTCODE(PORT)) - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - - if (PORT->PORTTYPE != 0) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - KISS = (struct KISSINFO *) PORT; - - if (KISS->FIRSTPORT != KISS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - if (OpenConnection(PORT), TRUE) - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); - else - Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); - - PORT->PortStopped = FALSE; - - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - } - } - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - - -#define FEND 0xC0 -int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count); - - -VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - char _REPLYBUFFER[1000] = ""; - char * ptr, * Context; - - int portno = 0; - int cmd = 0, val = 0; - struct PORTCONTROL * PORT = PORTTABLE; - int n = NUMBEROFPORTS; - - // Send KISS Command to TNC - - // Get port number - - ptr = strtok_s(CmdTail, " ", &Context); - - if (ptr) - { - portno = atoi (ptr); - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - { - cmd = atoi (ptr); - ptr = strtok_s(NULL, " ", &Context); - - if (ptr) - val = atoi (ptr); - } - } - - if (portno == 0 || cmd == 0) - { - strcpy(Bufferptr, BADMSG); - Bufferptr += (int)strlen(BADMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - while (n--) - { - if (PORT->PORTNUMBER == portno) - { - struct KISSINFO * KISS; - UCHAR ENCBUFF[16]; - unsigned char * ptr = ENCBUFF; - - if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - KISS = (struct KISSINFO *) PORT; - - if (KISS->FIRSTPORT != KISS) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - - // Send Command - - *(ptr++) = FEND; - *(ptr++) = KISS->OURCTRL | cmd; - *(ptr++) = (UCHAR)val; - *(ptr++) = FEND; - - PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q - - PORT->Session = Session; - PORT->LastKISSCmdTime = time(NULL); - - ASYSEND(PORT, ENCBUFF, 4); - - Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; - } - PORT = PORT->PORTPOINTER; - } - - - // Bad port - - strcpy(Bufferptr, BADPORT); - Bufferptr += (int)strlen(BADPORT); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - return; -} - - -VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - FindLostBuffers(); - -#ifdef WIN32 - Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to Debugview\r"); -#else - Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to syslog\r"); -#endif - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD) -{ - // Telnet Connection from FLMSG - CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link - ReleaseBuffer((UINT *)REPLYBUFFER); -} - -BOOL CheckExcludeList(UCHAR * Call) -{ - UCHAR * ptr1 = ExcludeList; - - while (*ptr1) - { - if (memcmp(Call, ptr1, 6) == 0) - return FALSE; - - ptr1 += 7; - } - - return TRUE; -} - - -void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) -{ - - UCHAR * ptr = ExcludeList; - char Normcall[10] = ""; - UCHAR AXCall[8] = ""; - - if (*CmdTail == ' ') - goto DISPLIST; - - if (*CmdTail == 'Z') - { - // CLEAR LIST - - memset(ExcludeList, 0, 70); - goto DISPLIST; - } - - ConvToAX25(CmdTail, AXCall); - - if (strlen(ExcludeList) < 70) - strcat(ExcludeList, AXCall); - -DISPLIST: - - while (*ptr) - { - Normcall[ConvFromAX25(ptr, Normcall)] = 0; - Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall); - ptr += 7; - } - - *(Bufferptr++) = '\r'; - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); -} - -BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr) -{ - if (Session->PASSWORD != 0xFFFF) - { - Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG); - SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); - - return FALSE; - } - - return TRUE; -} - - - - - - - diff --git a/Cmd.c b/Cmd.c index 44bf503..1e2955c 100644 --- a/Cmd.c +++ b/Cmd.c @@ -3676,6 +3676,13 @@ VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CM ptr = strtok_s(CmdTail, " ", &Context); + if (ptr == NULL || ptr[0] == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number needed eg MH 1\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + if (ptr) Port = atoi(ptr); @@ -4137,7 +4144,7 @@ VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX if (OtherTNC == TNC) continue; - if (rxInterlock == OtherTNC->RXRadio || txInterlock == OtherTNC->TXRadio) // Same Group + if (rxInterlock && rxInterlock == OtherTNC->RXRadio || txInterlock && txInterlock == OtherTNC->TXRadio) // Same Group { int n; diff --git a/CommonCode.c b/CommonCode.c index 7060c77..6eeb7aa 100644 --- a/CommonCode.c +++ b/CommonCode.c @@ -68,7 +68,11 @@ VOID WriteMiniDump(); void printStack(void); char * FormatMH(PMHSTRUC MH, char Format); void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode); +void SendDataToPktMap(char *Msg); + extern BOOL LogAllConnects; +extern BOOL M0LTEMap; + extern VOID * ENDBUFFERPOOL; @@ -2095,9 +2099,28 @@ DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot) return PORTVEC; } +int CanPortDigi(int Port) +{ + struct PORTCONTROL * PORTVEC = GetPortTableEntryFromPortNum(Port); + struct TNCINFO * TNC; + + if (PORTVEC == NULL) + return FALSE; + + TNC = PORTVEC->TNC; + + if (TNC == NULL) + return TRUE; + + if (TNC->Hardware == H_SCS || TNC->Hardware == H_TRK || TNC->Hardware == H_TRKM || TNC->Hardware == H_WINRPR) + return FALSE; + + return TRUE; +} + struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum) { - struct PORTCONTROL * PORTVEC=PORTTABLE; + struct PORTCONTROL * PORTVEC = PORTTABLE; do { @@ -2337,6 +2360,13 @@ BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) 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 ); @@ -3302,6 +3332,9 @@ VOID SendLocation() SendReportMsg((char *)&AXMSG.DEST, Len + 16); + if (M0LTEMap) + SendDataToPktMap(""); + return; } @@ -4088,10 +4121,10 @@ VOID SaveUIConfig() config_destroy(&cfg); } +int GetRegConfig(); + VOID GetUIConfig() { -#ifdef LINBPQ - char Key[100]; char CfgFN[256]; char Digis[100]; @@ -4118,7 +4151,13 @@ VOID GetUIConfig() 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; } @@ -4158,8 +4197,42 @@ VOID GetUIConfig() } } -#else + _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) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +int GetRegConfig() +{ int retCode, Vallen, Type, i; char Key[80]; char Size[80]; @@ -4234,14 +4307,9 @@ VOID GetUIConfig() SaveUIConfig(); -#endif - - _beginthread(UIThread, 0, NULL); - + return TRUE; } -#ifndef LINBPQ - INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { // This processes messages from controls on the tab subpages @@ -4749,6 +4817,540 @@ void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CM Debugprintf("CTEXT Read for ports %s\r", &PortList[1]); } +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", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + return sock; +} + +static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" + "Accept: application/json\r\n" +// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n" + "Content-Type: application/json\r\n" + "Host: %s:%d\r\n" + "Content-Length: %d\r\n" + //r\nUser-Agent: BPQ32(G8BPQ)\r\n" +// "Expect: 100-continue\r\n" + "\r\n"; + + +VOID SendWebRequest(SOCKET sock, char * Host, char * Request, char * Params, int Len, char * Return) +{ + int InputLen = 0; + int inptr = 0; + char Buffer[4096]; + char Header[256]; + char * ptr, * ptr1; + int Sent; + + sprintf(Header, HeaderTemplate, Request, Host, 80, Len, Params); + 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); + return; + } + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 4096 - inptr, 0); + + if (InputLen == -1 || InputLen == 0) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from Web Update recv()", Err); + return; + } + + // As we are using a persistant connection, can't look for close. Check + // for complete message + + inptr += InputLen; + + Buffer[inptr] = 0; + + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + // got header + + int Hddrlen = (int)(ptr - Buffer); + + ptr1 = strstr(Buffer, "Content-Length:"); + + if (ptr1) + { + // Have content length + + int ContentLen = atoi(ptr1 + 16); + + if (ContentLen + Hddrlen + 4 == inptr) + { + // got whole response + + if (strstr(Buffer, " 200 OK")) + { + if (Return) + { + memcpy(Return, ptr + 4, ContentLen); + Return[ContentLen] = 0; + } + else + Debugprintf("Map Database update ok"); + + } + else + { + strlop(Buffer, 13); + Debugprintf("Map Update Params - %s", Params); + Debugprintf("Map Update failed - %s", Buffer); + } + return; + } + } + else + { + ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); + + if (ptr1) + { + // Just accept anything until I've sorted things with Lee + Debugprintf("%s", ptr1); + Debugprintf("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]; + +void SendDataToPktMap(char *Msg) +{ + SOCKET sock; + 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 locked[] = " ! "; + int Percent = 0; + int Port = 0; + char Normcall[10]; + char Copy[20]; + + char * ptr = Params; + + 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"); + + //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"; + + 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 (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_WINMOR: + case H_V4: + + case H_MPSK: + case H_FLDIGI: + case H_UIARQ: + case H_ARDOP: + case H_VARA: + case H_KISSHF: + case H_FREEDATA: + + // TCP + + Mode = Modenames[TNC->Hardware]; + + 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) + { + 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, "Access", PORT->PORTDESCRIPTION); + } + + PORT = PORT->PORTPOINTER; + } + + ptr -= 3; + ptr += sprintf(ptr, "],\r\n"); + + // Neighbours + + 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++; + } + + ptr -= 3; + ptr += sprintf(ptr, "]}"); + +/* +{ + "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"}] + + sock = OpenHTTPSock("packetnodes.spots.radio"); + + if (sock == 0) + return; + + SendWebRequest(sock, "packetnodes.spots.radio", Request, Params, strlen(Params), Return); + closesocket(sock); +} + +// ="{\"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/FBBRoutines.c b/FBBRoutines.c index 2fae431..051afe6 100644 --- a/FBBRoutines.c +++ b/FBBRoutines.c @@ -472,7 +472,7 @@ ok: // Check Filters - if (CheckRejFilters(FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->BID, FBBHeader->MsgType)) + if (CheckRejFilters(FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->BID, FBBHeader->MsgType, FBBHeader->Size)) { memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; @@ -604,7 +604,7 @@ ok: char * To = strtok_s(NULL, seps, &Context); char * Type = strtok_s(NULL, seps, &Context); - if (From && To && ATBBS && Type && CheckRejFilters(From, To, ATBBS, NULL, *Type)) + if (From && To && ATBBS && Type && CheckRejFilters(From, To, ATBBS, NULL, *Type, FBBHeader->Size)) { memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header conn->FBBReplyChars[conn->FBBReplyIndex++] = '-'; diff --git a/HALDriver.c.bak b/HALDriver.c.bak deleted file mode 100644 index 31e03cf..0000000 --- a/HALDriver.c.bak +++ /dev/null @@ -1,1903 +0,0 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// -// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch -// -// Uses BPQ EXTERNAL interface -// - -#define _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_DEPRECATE - -#include "time.h" - -#include "CHeaders.h" -#include "tncinfo.h" - -#include "bpq32.h" - -#define HAL 1 - -#define SetMYCALL 0x13 -#define ConnectEnable 0x52 -#define ConnectDisable 0x42 -#define SetEAS 0x59 // Echo as Sent -#define SetTones 0xec -#define ClearOnDisc 0x57 - -static char ClassName[]="HALSTATUS"; - -static char WindowTitle[] = "HAL"; -static int RigControlRow = 185; - -#define SOH 0x01 // CONTROL CODES -#define ETB 0x17 -#define DLE 0x10 - -//int MaxStreams = 0; - -#ifndef LINBPQ -extern HFONT hFont; -#endif - -static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", - "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", - "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", - "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", - "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; - -struct TNCINFO * CreateTTYInfo(int port, int speed); -BOOL SetupConnection(int); -static BOOL WriteCommBlock(struct TNCINFO * TNC); -static void CheckRX(struct TNCINFO * TNC); -VOID HALPoll(int Port); -VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); -VOID ProcessTermModeResponse(struct TNCINFO * TNC); -static VOID DoTNCReinit(struct TNCINFO * TNC); -VOID DoTermModeTimeout(struct TNCINFO * TNC); -VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); -VOID ProcessHALCmd(struct TNCINFO * TNC); -VOID ProcessHALData(struct TNCINFO * TNC); -VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); -VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); - -BOOL HALConnected(struct TNCINFO * TNC, char * Call); -VOID HALDisconnected(struct TNCINFO * TNC); - -static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); -VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); -int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); -int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); - -VOID COMClearDTR(HANDLE fd); -VOID COMClearRTS(HANDLE fd); -int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); - - - -//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - -//char * Logs[4] = {"1", "2", "3", "4"}; - -//char BaseDir[]="c:"; - -static VOID CloseLogfile(int Flags) -{ -// CloseHandle(LogHandle[Flags]); -// LogHandle[Flags] = INVALID_HANDLE_VALUE; -} - -static VOID OpenLogfile(int Flags) -{ -/* -UCHAR FN[MAX_PATH]; - time_t T; - struct tm * tm; - - T = time(NULL); - tm = gmtime(&T); - - sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); - - LogHandle[Flags] = CreateFile(FN, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); - - return (LogHandle[Flags] != INVALID_HANDLE_VALUE); -*/ -} - -static void WriteLogLine(int Flags, char * Msg, int MsgLen) -{ -// int cnt; -// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); -} - - - -int ProcessLine(char * buf, int Port) -{ - UCHAR * ptr,* p_cmd; - char * p_ipad = 0; - char * p_port = 0; - unsigned short WINMORport = 0; - int BPQport; - int len=510; - struct TNCINFO * TNC; - char errbuf[256]; - - strcpy(errbuf, buf); - - ptr = strtok(buf, " \t\n\r"); - - if(ptr == NULL) return (TRUE); - - if(*ptr =='#') return (TRUE); // comment - - if(*ptr ==';') return (TRUE); // comment - - ptr = strtok(NULL, " \t\n\r"); - - if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig - { - BPQport = Port; - p_cmd = ptr; - } - else - if (_stricmp(buf, "PORT") != 0) // Using Old Config - { - // New config without a PORT or APPL - this is a Config Command - - strcpy(buf, errbuf); - strcat(buf, "\r"); - - BPQport = Port; - - TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); - - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - goto ConfigLine; - } - else - - { - - // Old Config from file - - BPQport=0; - BPQport = atoi(ptr); - - p_cmd = strtok(NULL, " \t\n\r"); - - if (Port && Port != BPQport) - { - // Want a particular port, and this isn't it - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - } - } - } - if(BPQport > 0 && BPQport < 33) - { - TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); - TNC->InitScript = malloc(1000); - TNC->InitScript[0] = 0; - - if (p_cmd != NULL) - { - if (p_cmd[0] != ';' && p_cmd[0] != '#') - TNC->ApplCmd=_strdup(p_cmd); - } - - // Read Initialisation lines - - while(TRUE) - { - if (GetLine(buf) == 0) - return TRUE; -ConfigLine: - strcpy(errbuf, buf); - - if (memcmp(buf, "****", 4) == 0) - return TRUE; - - ptr = strchr(buf, ';'); - if (ptr) - { - *ptr++ = 13; - *ptr = 0; - } - - if (_memicmp(buf, "WL2KREPORT", 10) == 0) - { - TNC->WL2K = DecodeWL2KReportLine(buf); - continue; - } - if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) - { - TNC->XONXOFF = TRUE; - continue; - } - - if (_memicmp(buf, "TONES", 5) == 0) - { - int tone1 = 0, tone2 = 0; - - ptr = strtok(&buf[6], " ,/\t\n\r"); - if (ptr) - { - tone1 = atoi(ptr); - ptr = strtok(NULL, " ,/\t\n\r"); - if (ptr) - { - tone2 = atoi(ptr); - ptr = &TNC->InitScript[TNC->InitScriptLen]; - - // Try putting into FSK mode first - - *(ptr++) = 0x84; - *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) - *(ptr++) = tone1 >> 8; - *(ptr++) = tone1 & 0xff; - *(ptr++) = tone2 >> 8; - *(ptr++) = tone2 & 0xff; - - TNC->InitScriptLen += 6; - - continue; - } - } - goto BadLine; - } - if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) - { - - ptr = strtok(&buf[12], " ,\t\n\r"); - if (ptr) - { - if (_stricmp(ptr, "CLOVER") == 0) - TNC->DefaultMode = Clover; - else if (_stricmp(ptr, "PACTOR") == 0) - TNC->DefaultMode = Pactor; - else if (_stricmp(ptr, "AMTOR") == 0) - TNC->DefaultMode = AMTOR; - else goto BadLine; - - continue; - } - goto BadLine; - } - } - BadLine: - WritetoConsole(" Bad config record "); - WritetoConsole(errbuf); - WritetoConsole("\r\n"); - } - - return (TRUE); -} - -static size_t ExtProc(int fn, int port,unsigned char * buff) -{ - int txlen = 0; - PMSGWITHLEN buffptr; - struct TNCINFO * TNC = TNCInfo[port]; - struct STREAMINFO * STREAM; - int Stream; - - if (TNC == NULL) - return 0; - - if (fn < 4 || fn > 5) - if (TNC->hDevice == 0) - return 0; // Port not open - - STREAM = &TNC->Streams[0]; - - switch (fn) - { - case 1: // poll - - while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q - { - buffptr = Q_REM(&TNC->PortRecord->UI_Q); - ReleaseBuffer(buffptr); - } - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (STREAM->ReportDISC) - { - STREAM->ReportDISC = FALSE; - buff[4] = 0; - - return -1; - } - } - - CheckRX(TNC); - HALPoll(port); - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (STREAM->PACTORtoBPQ_Q !=0) - { - int datalen; - - buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); - - datalen=buffptr->Len; - - buff[4] = 0; - buff[7] = 0xf0; - memcpy(&buff[8],buffptr->Data,datalen); // Data goes to +7, but we have an extra byte - datalen+=8; - - PutLengthinBuffer((PDATAMESSAGE)buff, datalen); - - // buff[5]=(datalen & 0xff); - // buff[6]=(datalen >> 8); - - ReleaseBuffer(buffptr); - - return (1); - } - } - - return 0; - - case 2: // send - - buffptr = GetBuff(); - - if (buffptr == 0) return (0); // No buffers, so ignore - - // Find TNC Record - - Stream = buff[4]; - - if (!TNC->TNCOK) - { - // Send Error Response - - buffptr->Len = 36; - memcpy(buffptr->Data, "No Connection to PACTOR TNC\r", 36); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - return 0; - } - - txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; - - buffptr->Len = txlen; - memcpy(buffptr->Data, &buff[8], txlen); - - C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); - - STREAM->FramesQueued++; - - return (0); - - - case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding - - Stream = (int)buff; - - if (STREAM->FramesQueued > 4) - return (1 | TNC->HostMode << 8); - - return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting - - case 4: // reinit - - return (0); - - case 5: // Close - - CloseCOMPort(TNCInfo[port]->hDevice); - return (0); - - case 6: // Scan Control - - return 0; // None Yet - - } - return 0; - -} - -static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) -{ - int Len = sprintf(Buff, "" - "HAL Status

HAL Status

"); - - Len += sprintf(&Buff[Len], ""); - - Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); - Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); - Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); - Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); - Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); - - Len = DoScanLine(TNC, Buff, Len); - - return Len; -} - - -UINT HALExtInit(EXTPORTDATA * PortEntry) -{ - char msg[500]; - struct TNCINFO * TNC; - int port; - char * ptr; - int len; - char Msg[80]; - HWND x; - - // - // Will be called once for each Pactor Port - // The COM port number is in IOBASE - // - - sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); - WritetoConsole(msg); - - port=PortEntry->PORTCONTROL.PORTNUMBER; - - ReadConfigFile(port, ProcessLine); - TNC = TNCInfo[port]; - - if (TNC == NULL) - { - // Not defined in Config file - - sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); - WritetoConsole(msg); - - return (int)ExtProc; - } - - TNC->Port = port; - - TNC->Hardware = H_HAL; - - if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) - TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; - - PortEntry->MAXHOSTMODESESSIONS = 1; // Default - - TNC->PortRecord = PortEntry; - - if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) - { - memcpy(TNC->NodeCall, MYNODECALL, 10); - } - else - { - ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); - } - - PortEntry->PORTCONTROL.PROTOCOL = 10; - PortEntry->PORTCONTROL.PORTQUALITY = 0; - - if (PortEntry->PORTCONTROL.PORTPACLEN == 0) - PortEntry->PORTCONTROL.PORTPACLEN = 100; - - ptr=strchr(TNC->NodeCall, ' '); - if (ptr) *(ptr) = 0; // Null Terminate - - if (TNC->DefaultMode) - TNC->CurrentMode = TNC->DefaultMode; - else - TNC->CurrentMode = Clover; - - TNC->PollDelay = 999999999; - - // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL - - len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); - len++; // We include the NULL - - memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); - TNC->InitScriptLen += len; - - PortEntry->PORTCONTROL.TNC = TNC; - - TNC->WebWindowProc = WebProc; - TNC->WebWinX = 510; - TNC->WebWinY = 280; - - TNC->WEB_COMMSSTATE = zalloc(100); - TNC->WEB_TNCSTATE = zalloc(100); - strcpy(TNC->WEB_TNCSTATE, "Free"); - TNC->WEB_MODE = zalloc(100); - TNC->WEB_TRAFFIC = zalloc(100); - TNC->WEB_BUFFERS = zalloc(100); - TNC->WEB_STATE = zalloc(100); - TNC->WEB_TXRX = zalloc(100); - TNC->WEB_LEDS = zalloc(100); - strcpy(TNC->WEB_LEDS, " X X X X X X"); - -#ifndef LINBPQ - - CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); - - x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); - x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); - - x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); - SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); - x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); - SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); - x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); - SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); - - TNC->ClientHeight = 233; - TNC->ClientWidth = 500; - - MoveWindows(TNC); -#endif - - OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); - - SendCmd(TNC, "\x09" , 1); // Reset - - WritetoConsole("\n"); - - return ((int)ExtProc); -} - - - -static VOID KISSCLOSE(int Port) -{ - struct TNCINFO * conn = TNCInfo[Port]; - - // drop DTR and RTS - - COMClearDTR(conn->hDevice); - COMClearRTS(conn->hDevice); - - // purge any outstanding reads/writes and close device handle - - CloseCOMPort(conn->hDevice); - - return; -} - - -static void CheckRX(struct TNCINFO * TNC) -{ - int Length, Len; - UCHAR * Xptr; - - // only try to read number of bytes in queue - - if (TNC->RXLen == 500) - TNC->RXLen = 0; - - Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); - - if (Len == 0) - return; - - TNC->RXLen += Len; - - Length = TNC->RXLen; - - // We need to konw whether data is received or echoed, so we can't split commands and data here. - // Pass everything to the Command Handler. It will check that there are enough bytes for the command, - // and wait for more if not. - - // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 - - // If USB version, we might get unescaped xon and xoff, which we must ignore - - if (TNC->XONXOFF) - { - Xptr = memchr(&TNC->RXBuffer, 0x11, Length); - - while(Xptr) - { - Debugprintf("XON Port %d", TNC->Port); - memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); - Xptr = memchr(&TNC->RXBuffer, 0x11, Length); - } - - Xptr = memchr(&TNC->RXBuffer, 0x13, Length); - - while(Xptr) - { - Debugprintf("XOFF Port %d", TNC->Port); - memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); - Xptr = memchr(&TNC->RXBuffer, 0x13, Length); - } - - Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape - - if (Xptr) - - // Make sure we have the escaped char as well - - if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char - return; - } - - ProcessHALBuffer(TNC, Length); - - TNC->RXLen = 0; - - return; - -} - - - -static BOOL WriteCommBlock(struct TNCINFO * TNC) -{ - WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); - return TRUE; -} - -VOID HALPoll(int Port) -{ - struct TNCINFO * TNC = TNCInfo[Port]; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - UCHAR * Poll = TNC->TXBuffer; - char Status[80]; - UCHAR TXMsg[1000]; - int datalen; - - if (TNC->Timeout) - { - TNC->Timeout--; - - if (TNC->Timeout) // Still waiting - return; - - // Timed Out - - TNC->TNCOK = FALSE; - TNC->HostMode = 0; - - sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected - { - STREAM->Connected = FALSE; // Back to Command Mode - STREAM->ReportDISC = TRUE; // Tell Node - } - } - - } - - // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence - - if (TNC->TNCOK) - if (!TNC->HostMode) - { - DoTNCReinit(TNC); - return; - } - - if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) - { - // New Attach - - int calllen; - char Msg[80]; - - STREAM->Attached = TRUE; - - STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; - - calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); - STREAM->MyCall[calllen] = 0; - - datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); - SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - // Stop Scanning - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Msg); - - SendCmd(TNC, "\x42", 1); // Connect Enable off - - return; - - } - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - - if (STREAM->Attached) - CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); - - if (TNC->NeedPACTOR) - { - TNC->NeedPACTOR--; - - if (TNC->NeedPACTOR == 0) - { - int datalen; - - UCHAR TXMsg[80]; - - datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); - SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL - - // Set Listen Mode - - switch (TNC->CurrentMode) - { - case Pactor: - - SendCmd(TNC, "\x84", 1); // FSK - SendCmd(TNC, "\x83", 1); // Select P-MODE Standby - SendCmd(TNC, "\x58", 1); // Listen - - break; - - case Clover: - - SendCmd(TNC, "\x80", 1); // Clover - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x41", 1); // No Statistics - SendCmd(TNC, "\x60\x09", 2); // Robust Retries - SendCmd(TNC, "\x61\x09", 2); // Normal Retries - - break; - } - - SendCmd(TNC, "\x52", 1); // ConnectEnable - - // Restart Scanning - - sprintf(Status, "%d SCANSTART 15", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Status); - - return; - } - } - -#define MAXHALTX 256 - - //for (Stream = 0; Stream <= MaxStreams; Stream++) - { - if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) - { - int datalen; - PMSGWITHLEN buffptr; - UCHAR * MsgPtr; - unsigned char TXMsg[500]; - - buffptr = (PMSGWITHLEN)STREAM->BPQtoPACTOR_Q; - datalen = buffptr->Len; - MsgPtr = buffptr->Data; - - if (STREAM->Connected) - { - if (TNC->SwallowSignon) - { - TNC->SwallowSignon = FALSE; - if (strstr(MsgPtr, "Connected")) // Discard *** connected - { - ReleaseBuffer(buffptr); - STREAM->FramesQueued--; - return; - } - } - - // Must send data in small chunks - the Hal has limited buffer space - - // If in IRS force a turnround - - if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) - { - if (TNC->TimeInRX++ > 15) - SendCmd(TNC, "\x87", 1); // Changeover to ISS - else - goto Poll; - } - - TNC->TimeInRX = 0; - - EncodeAndSend(TNC, MsgPtr, datalen); - buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); - ReleaseBuffer(buffptr); - WriteLogLine(2, MsgPtr, datalen); - - STREAM->BytesTXed += datalen; - STREAM->FramesQueued--; - - ShowTraffic(TNC); - - return; - } - else - { - buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); - STREAM->FramesQueued--; - - // Command. Do some sanity checking and look for things to process locally - - datalen--; // Exclude CR - MsgPtr[datalen] = 0; // Null Terminate - _strupr(MsgPtr); - - if (memcmp(MsgPtr, "RADIO ", 6) == 0) - { - sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); - if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40])) - { - ReleaseBuffer(buffptr); - } - else - { - buffptr->Len = sprintf((UCHAR *)buffptr->Data, "%s", &MsgPtr[40]); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - return; - } - - if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) - { - TNC->CurrentMode = Clover; - buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - MySetWindowText(TNC->xIDC_MODE, "Clover"); - strcpy(TNC->WEB_MODE, "Clover"); - - SendCmd(TNC, "\x80", 1); // Clover - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x41", 1); // No Statistics - - return; - } - - if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) - { - TNC->CurrentMode = Pactor; - buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - SendCmd(TNC, "\x84", 1); // FSK - SendCmd(TNC, "\x83", 1); // Select P-MODE Standby - SendCmd(TNC, "\x48", 1); // Listen Off - - return; - } - if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) - { - TNC->CurrentMode = AMTOR; - buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r"); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - return; - } - - if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect - { - memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); - - switch (TNC->CurrentMode) - { - case Pactor: - - SendCmd(TNC, "\x84", 1); // FSK - SendCmd(TNC, "\x83", 1); // Select P-MODE Standby - - datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); - - // DOnt set connecting till we get the 19 response so we can trap listen as a fail - break; - - case Clover: - - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect - - datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); - - break; - } - - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - SendCmd(TNC, TXMsg, datalen + 1); // Include NULL - - ReleaseBuffer(buffptr); - - return; - } - - if (memcmp(MsgPtr, "CLOVER ", 7) == 0) - { - memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); - - SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format - SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect - - datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); - SendCmd(TNC, TXMsg, datalen + 1); // Include NULL - - sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", - STREAM->MyCall, STREAM->RemoteCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - ReleaseBuffer(buffptr); - - return; - } - - if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect - { - SendCmd(TNC, "\x07", 1); // Normal Disconnect - TNC->NeedPACTOR = 50; - - STREAM->Connecting = FALSE; - STREAM->ReportDISC = TRUE; - ReleaseBuffer(buffptr); - - return; - } - - // Other Command ?? Treat as HEX string - - datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", - (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], - (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], - (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], - (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); - -// SendCmd(TNC, TXMsg, datalen); - ReleaseBuffer(buffptr); - TNC->InternalCmd = 0; - } - } - } -Poll: - // Nothing doing - send Poll (but not too often) - - TNC->PollDelay++; - - if (TNC->PollDelay < 20) - return; - - TNC->PollDelay = 0; - - if (TNC->TNCOK) - SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll - else - SendCmd(TNC, "\x09" , 1); // Reset - - TNC->Timeout = 100; - - return; -} - -static VOID DoTNCReinit(struct TNCINFO * TNC) -{ - // TNC Has Restarted, send init commands (can probably send all at once) - -// TNC->TXBuffer[0] = 0x1b; -// TNC->TXLen = 1; - - WriteCommBlock(TNC); - - SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); - - TNC->HostMode = TRUE; // Should now be in Host Mode - TNC->NeedPACTOR = 20; // Need to set Calls and start scan - - TNC->DataMode = RXDATA; // Start with RX Data - - SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll -// SendCmd(TNC, "\xc9" , 1); // Huffman Off - SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect - - SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries - -// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem - - TNC->Timeout = 50; - - return; - -} - -VOID ProcessHALData(struct TNCINFO * TNC) -{ - // Received Data just pass to Appl - - PMSGWITHLEN buffptr; - int Len = TNC->DataLen; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - TNC->DataLen = 0; - - if (TNC->DataMode == TXDATA) - { - STREAM->BytesAcked += Len; -// Debugprintf("Acked %d", Len); - - if (STREAM->BytesAcked > STREAM->BytesTXed) - Debugprintf("Too Much Acked"); - - if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) - { - // All sent - - if (STREAM->Disconnecting) - TidyClose(TNC, 0); - else - if (TNC->CurrentMode != Clover) - - // turn round link - - SendCmd(TNC, "\x0c" , 1); // Turnround - - } - } - else - { - if (TNC->DataMode == RXDATA) - { -// Debugprintf("RXed %d", Len); - buffptr = GetBuff(); - if (buffptr == NULL) - return; // No buffers, so ignore - - buffptr->Len = Len; // Length - - WriteLogLine(1, TNC->DataBuffer, Len); - - STREAM->BytesRXed += Len; - - memcpy(buffptr->Data, TNC->DataBuffer, Len); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - } - - ShowTraffic(TNC); - - return; -} - - - -VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) -{ - UCHAR Char; - UCHAR * inptr; - UCHAR * cmdptr; - UCHAR * dataptr; - BOOL CmdEsc, DataEsc; - - inptr = TNC->RXBuffer; - - cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; - dataptr = &TNC->DataBuffer[TNC->DataLen]; - CmdEsc = TNC->CmdEsc; - DataEsc = TNC->DataEsc; - - // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 - - // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 - - // Command Responses can be variable length - - // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives - - while(Length--) - { - Char = *(inptr++); - - if (CmdEsc) - { - CmdEsc = FALSE; - - if (TNC->XONXOFF && Char == 0x91) - { - // XON/XOFF escape. We ensured above that data follows so we can process it inline - - Length--; - Char = *(inptr++) - 0x20; - } - *(cmdptr++) = Char; - } - else if (DataEsc) - { - DataEsc = FALSE; - goto DataChar; - } - else -NotData: - if (Char == 0x80) // Next Char is Command - CmdEsc = TRUE; - else if (Char == 0x81) // Next Char is escaped data (80 or 81) - DataEsc = TRUE; - else - { - // This is a Data Char. We must process any Commands received so far, so we know the type of data - - DataChar: - - TNC->CmdLen = cmdptr - TNC->CmdBuffer; - ProcessHALCmd(TNC); - cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; - dataptr = &TNC->DataBuffer[TNC->DataLen]; - - *(dataptr++) = Char; // Normal Data - - // Now process any other data chars - - while(Length--) - { - Char = *(inptr++); - - if (TNC->XONXOFF && Char == 0x91) - { - // XON/XOFF escape within data. We ensured above that data follows so we - // can process it here - - Length--; - Char = *(inptr++) - 0x20; - } - - if (Char == 0x80 || Char == 0x81) - { - // Process any data we have, then loop back - - TNC->DataLen = dataptr - TNC->DataBuffer; - ProcessHALData(TNC); - - goto NotData; - } - *(dataptr++) = Char; // Normal Data - } - - // Used all data - - TNC->DataLen = dataptr - TNC->DataBuffer; - - ProcessHALData(TNC); - TNC->CmdEsc = CmdEsc; - TNC->DataEsc = DataEsc; - - return; - } - } - - // Save State - - TNC->CmdLen = cmdptr - TNC->CmdBuffer; - - TNC->CmdEsc = CmdEsc; - TNC->DataEsc = DataEsc; - - if (TNC->DataLen) - ProcessHALData(TNC); - - if (TNC->CmdLen) - ProcessHALCmd(TNC); -} - -VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) -{ - MySetWindowText(TNC->xIDC_STATE, Msg); - strcpy(TNC->WEB_STATE, Msg); -} - -VOID ProcessHALCmd(struct TNCINFO * TNC) -{ - char * Call; - int Stream = 0; - int Opcode; - int StatusByte; - int Leds; - int Len; - int Used; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - -CmdLoop: - - Opcode = TNC->CmdBuffer[0]; - Len = TNC->CmdLen; - - if (Len == 0) - return; - - TNC->TNCOK = TRUE; - TNC->Timeout = 0; - - sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); - MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); - - // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs - - switch(Opcode) - { - case 0x09: //Hardware Reset - equivalent to power on reset - - // Hardware has reset - need to reinitialise - - TNC->HostMode = 0; // Force Reinit - - Used = 1; - break; - - case 0x7a: // FSK Modes Status - - // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display - - if (Len < 2) return; // Wait for more - - StatusByte = TNC->CmdBuffer[1]; - - switch (StatusByte) - { - case 0x06: // FSK TX (RTTY) - case 0x07: // FSK RX (RTTY) - case 0x10: // AMTOR STANDBY (LISTEN ON) - case 0x11: // AMTOR STANDBY (LISTEN OFF) - case 0x12: // AMTOR FEC TX (AMTOR) - case 0x13: // AMTOR FEC RX (AMTOR) - case 0x14: // P-MODE FEC TX (P-MODE) - case 0x15: // FREE SIGNAL TX (AMTOR) - case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) - - // Diaplay Linke Status - - MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); - strcpy(TNC->WEB_MODE, status[StatusByte]); - - break; - - case 0x0C: // P-MODE STANDBY (LISTEN ON) - case 0x0D: // P-MODE STANDBY (LISTEN OFF) - - // if we were connecting, this means connect failed. - - MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); - strcpy(TNC->WEB_MODE, status[StatusByte]); - - if (STREAM->Connecting) - HALDisconnected(TNC); - - break; - - case 0x0E: // ISS (AMTOR/P-MODE) - - MySetWindowText(TNC->xIDC_TXRX,"ISS"); - strcpy(TNC->WEB_TXRX, "ISS"); - TNC->TXRXState = 'S'; - break; - - case 0x0F: // IRS (AMTOR/P-MODE) - - MySetWindowText(TNC->xIDC_TXRX,"IRS"); - strcpy(TNC->WEB_TXRX, "IRS"); - TNC->TXRXState = 'R'; - break; - - case 0x00: // IDLE (AMTOR/P-MODE) - case 0x01: // TFC (AMTOR/P-MODE) - case 0x02: // RQ (AMTOR/P-MODE) - case 0x03: // ERR (AMTOR/P-MODE) - case 0x04: // PHS (AMTOR/P-MODE) - case 0x05: // OVER (AMTOR/P-MODE) (not implemented) - - MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); - strcpy(TNC->WEB_MODE, status[StatusByte]); - - - -//$807A $8008 P-MODE100 (P-MODE) -//$807A $8009 P-MODE200 (P-MODE) -//$807A $800A HUFFMAN ON (P-MODE) -//$807A $800B HUFFMAN OFF (P-MODE) - ; - } - Used = 2; - break; - - - case 0x7d: // Get LED Status - - // We use Get LED Status as a Poll - - if (Len < 2) return; // Wait for more - - Leds = TNC->CmdBuffer[1]; - sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", - (Leds & 0x20)? 'X' : ' ', - (Leds & 0x10)? 'X' : ' ', - (Leds & 0x08)? 'X' : ' ', - (Leds & 0x04)? 'X' : ' ', - (Leds & 0x02)? 'X' : ' ', - (Leds & 0x01)? 'X' : ' '); - -// STBY CALL LINK ERROR TX RX - MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); - - Used = 2; - break; - - case 0x21: // Monitored FEC CCB - case 0x22: // Monitored ARQ CCB - - // As the reply is variable, make sure we have the terminating NULL - - if (memchr(TNC->CmdBuffer, 0, Len) == NULL) - return; // Wait for more - - Call = &TNC->CmdBuffer[1]; - Used = strlen(Call) + 2; // Opcode and Null - - UpdateMH(TNC, Call, '!', 0); - - break; - - case 0x27: // Clover ARQ LINK REQUEST status message - - //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). - - if (Len < 2) return; // Wait for more - - // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL - Used = 2; - break; - - case 0x2D: // FSK ARQ Link Request status message - - // $802D $8001 $8000 CLOVER Link Request (not implemented) - // $802D $8002 $8000 AMTOR CCIR-476 Link Request - // $802D $8003 $8000 AMTOR CCIR-625 Link Request - // $802D $8004 $8000 P-MODE Link Request - - if (Len < 3) return; // Wait for more - - // Don't need to do anything (but may save Session type later - - Used = 3; - break; - - - case 0x28: // Monitored Call - - // As the reply is variable, make sure we have the terminating NULL - - if (memchr(TNC->CmdBuffer, 0, Len) == NULL) - return; // Wait for more - - Call = &TNC->CmdBuffer[1]; - Used = strlen(Call) + 2; // Opcode and Null - - // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls - - break; - - - case 0x20: // Clover Linked with - Call Connected - case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. - case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . - case 0x2B: // P-MODE link to - - // As the reply is variable, make sure we have the terminating NULL - - if (memchr(TNC->CmdBuffer, 0, Len) == NULL) - return; // Wait for more - - Call = &TNC->CmdBuffer[1]; - Used = strlen(Call) + 2; // Opcode and Null - - HALConnected(TNC, Call); - - break; - - case 0x23: // Normal Disconnected - followed by $8000 - case 0x24: // Link failed (any of the link errors) - case 0x25: // Signal Lost (LOS) - - if (Len < 2) return; // Wait for more - - HALDisconnected(TNC); - - Used = 2; - break; - - - // Stream Switch Reports - we will need to do something with these if Echo as Sent is set - // or we do something with the secondary port - - case 0x30: // Switch to Receive Data characters - case 0x31: // Switch to Transmit Data characters - case 0x32: // Switch to RX data from secondary port - - TNC->DataMode = Opcode; - Used = 1; - break; - - case 0x33: // Send TX data to modem - case 0x34: // Send TX data to secondary port - - TNC->TXMode = Opcode; - Used = 1; - break; - - case 0x70: // Channel Spectra Data - // $807F $80xx $8030 Invalid or unimplemented command code - if (Len < 9) return; // Wait for more - - Used = 9; - break; - - case 0x71: // SelCall On/Off - - if (Len < 2) return; // Wait for more - - Used = 2; - break; - - case 0x72: // Channel Spectra Data - // $807F $80xx $8030 Invalid or unimplemented command code - if (Len < 15) return; // Wait for more - - Used = 15; - break; - - case 0x73: // Clover Link state - - if (Len < 2) return; // Wait for more - - StatusByte = TNC->CmdBuffer[1]; - - switch (StatusByte) - { - case 0x00: mySetWindowText(TNC, "Channel idle"); break; - case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; - case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; - case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; - case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; - case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; - case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; - case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; - case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; - case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; - case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; - case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; - case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; - case 0x8E: mySetWindowText(TNC, "TX idle"); break; - case 0x8F: mySetWindowText(TNC, "RX idle"); break; - case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; - case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; - case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; - case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; - } - - Used = 2; - break; - - case 0x75: // Clover waveform format - - if (Len < 5) return; // Wait for more - - Used = 5; - break; - - case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy - // $807F $80xx $8030 Invalid or unimplemented command code - // $807F $80xx $8031 Invalid parameter value - // $807F $80xx $8032 Not allowed when connected - // $807F $80xx $8033 Not allowed when disconnected - // $807F $80xx $8034 Not valid in this mode - // $807F $80xx $8035 Not valid in this code - // $807F $8096 $8036 EEPROM write error - - if (Len < 3) return; // Wait for more - - if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) - { - // Reject of XON/XOFF enable - -// TNC->XONXOFF = FALSE; -// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); - } - else - Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); - - Used = 3; - break; - - // Following are all immediate commands - response is echo of command - - case 0x6f: // XON/XOFF on - -// TNC->XONXOFF = TRUE; // And drop through -// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); - - case 0x19: // Call P-MODE to - case 0x10: // Robust Link to using MYCALL - case 0x11: // Normal Link to using MYCALL - - STREAM->Connecting = TRUE; - - case 0x00: // P Load LOD file - case 0x01: // P Load S28 file - case 0x02: //Check Unit Error Status - case 0x03: //F Check System Clock - case 0x04: //C Close PTT and transmit Clover waveform - case 0x05: //Open PTT and stop transmit test - case 0x06: //Immediate Abort (Panic Kill) - case 0x07: //Normal disconnect (wait for ACK) - case 0x08: //Software reset - restore all program defaults - case 0x0A: //Send CW ID - case 0x0B: //Close PTT and transmit Single Tone - case 0x0C: //F Normal OVER (AMTOR,P-MODE) - case 0x0D: //F Force RTTY TX (Baudot/ASCII) - case 0x0E: //F Go to RTTY RX (Baudot/ASCII) - case 0x0F: //Go to LOD/S28 file loader - case SetMYCALL: // Set MYCALL Response - - case 0x1E: // Set MYALTCALL Response - - case 0x41: - case 0x42: - case 0x46: - case 0x47: - case 0x48: - case 0x4d: - case 0x52: // Enable adaptive Clover format - case 0x54: // Enable adaptive Clover format - - case 0x56: // Expanded Link State Reports OFF/ON - case 0x57: // Clear buffers on disc - case 0x58: - case 0x59: - case 0x60: // Robust Mode Retries - case 0x61: // Normal Mode Retries - case 0x80: //Switch to CLOVER mode - case 0x81: //Select AMTOR Standby - case 0x82: //Select AMTOR FEC - case 0x83: //Select P-MODE Standby - case 0x84: //Switch to FSK modes - case 0x85: //Select Baudot - case 0x86: //Select ASCII - case 0x87: //Forced OVER (AMTOR, P-MODE) - case 0x88: //Forced END (AMTOR, P-MODE) - case 0x89: //Force LTRS shift - case 0x8A: //Force FIGS shift - case 0x8B: //Send MARK tone - case 0x8C: //Send SPACE tone - case 0x8D: //Send MARK/SPACE tones - case 0x8E: //Received first character on line - case 0x8F: //Close PTT only (no tones) - - case 0xC9: //Huffman Off/On - case 0xCC: - case 0xD9: //Close PTT only (no tones) - - case SetTones: - - Used = 1; - break; - - case 0x91: // ???? - -// if (Len < 2) return; // Wait for more - - Used = 1; - break; - - default: - - // We didn't recognise command, so don't know how long it is - disaster! - - Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); - TNC->CmdLen = 0; - - return; - } - - if (Used == Len) - { - // All used - most likely case - - TNC->CmdLen = 0; - return; - } - - // Move Command Down buffer, and reenter - - TNC->CmdLen -= Used; - - memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); - - goto CmdLoop; - - -} - - -VOID HALDisconnected(struct TNCINFO * TNC) -{ - struct STREAMINFO * STREAM = &TNC->Streams[0]; - - CloseLogfile(0); - CloseLogfile(1); - CloseLogfile(2); - - if ((STREAM->Connecting | STREAM->Connected) == 0) - { - // Not connected or Connecting. Probably response to going into Pactor Listen Mode - - return; - } - - if (STREAM->Connecting && STREAM->Disconnecting == FALSE) - { - PMSGWITHLEN buffptr; - - // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now - - buffptr = GetBuff(); - - if (buffptr) - { - buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall); - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - } - - STREAM->Connecting = FALSE; - STREAM->Connected = FALSE; // In case! - STREAM->FramesQueued = 0; - - sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - return; - } - - // Connected, or Disconnecting - Release Session - - STREAM->Connecting = FALSE; - STREAM->Connected = FALSE; // Back to Command Mode - STREAM->FramesQueued = 0; - - if (STREAM->Disconnecting == FALSE) - STREAM->ReportDISC = TRUE; // Tell Node - - STREAM->Disconnecting = FALSE; - - // Need to reset Pactor Call in case it was changed - - TNC->NeedPACTOR = 20; -} - -BOOL HALConnected(struct TNCINFO * TNC, char * Call) -{ - char Msg[80]; - PMSGWITHLEN buffptr; - struct STREAMINFO * STREAM = &TNC->Streams[0]; - char CallCopy[80]; - - strcpy(CallCopy, Call); - strcat(CallCopy, " "); // Some routines expect 10 char calls - - STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; - STREAM->ConnectTime = time(NULL); - - // Stop Scanner - - sprintf(Msg, "%d SCANSTOP", TNC->Port); - - Rig_Command( (TRANSPORTENTRY *) -1, Msg); - - ShowTraffic(TNC); - - TNC->DataMode = RXDATA; - - OpenLogfile(0); - OpenLogfile(1); - OpenLogfile(2); - - if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) - { - // Incoming Connect - - ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); - - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - if (TNC->CurrentMode != Clover) - SendCmd(TNC, "\x87", 1); // Changeover to ISS - - // If an autoconnect APPL is defined, send it - - if (TNC->ApplCmd) - { - buffptr = GetBuff(); - if (buffptr == 0) return TRUE; // No buffers, so ignore - - buffptr->Len = sprintf((UCHAR *)buffptr->Data, "%s\r", TNC->ApplCmd); - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - TNC->SwallowSignon = TRUE; - - return TRUE; - } - - if (FULL_CTEXT && HFCTEXTLEN == 0) - { - EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); - WriteLogLine(2, CTEXTMSG, CTEXTLEN); - - STREAM->BytesTXed += CTEXTLEN; - } - return TRUE; - } - - // Connect Complete - - buffptr = GetBuff(); - if (buffptr == 0) return TRUE; // No buffers, so ignore - - buffptr->Len = sprintf((UCHAR *)buffptr->Data, "*** Connected to %s\r", Call);; - - C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); - - STREAM->Connecting = FALSE; - STREAM->Connected = TRUE; // Subsequent data to data channel - - sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); - MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); - - UpdateMH(TNC, CallCopy, '+', 'O'); - - - return TRUE; -} - - -static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) -{ - // Send A Packet With DLE Encoding Encoding - - TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); - - WriteCommBlock(TNC); -} - -VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) -{ - // Send A Packet With Command Encoding (preceed each with 0x80 - - int i,txptr=0; - UCHAR * outbuff = TNC->TXBuffer; - - for (i=0; iTXLen = txptr; - WriteCommBlock(TNC); -} - -int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) -{ - int i, txptr = 0; - UCHAR c; - - // Escape x80 and x81 with x81 - -// outbuff[0] = 0x80; -// outbuff[1] = 0x33; // Send data to modem - - for (i=0;iNeedPACTOR = 30; -} - - - - diff --git a/HTTPcode.c b/HTTPcode.c index daeb195..30cf2bd 100644 --- a/HTTPcode.c +++ b/HTTPcode.c @@ -99,6 +99,8 @@ extern UCHAR LogDirectory[]; extern struct RIGPORTINFO * PORTInfo[34]; extern int NumberofPorts; +extern UCHAR ConfigDirectory[260]; + char * strlop(char * buf, char delim); VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); int CompareNode(const void *a, const void *b); @@ -1475,13 +1477,13 @@ VOID SaveConfigFile(SOCKET sock , char * MsgPtr, char * Rest, int LOCAL) MsgLen = (int)strlen(input + 8); - if (BPQDirectory[0] == 0) + if (ConfigDirectory[0] == 0) { strcpy(inputname, "bpq32.cfg"); } else { - strcpy(inputname,BPQDirectory); + strcpy(inputname,ConfigDirectory); strcat(inputname,"/"); strcat(inputname, "bpq32.cfg"); } @@ -3024,13 +3026,13 @@ doHeader: if (COOKIE ==FALSE) Key = DummyKey; - if (BPQDirectory[0] == 0) + if (ConfigDirectory[0] == 0) { strcpy(inputname, "bpq32.cfg"); } else { - strcpy(inputname,BPQDirectory); + strcpy(inputname,ConfigDirectory); strcat(inputname,"/"); strcat(inputname, "bpq32.cfg"); } diff --git a/HTTPcode.c.bak b/HTTPcode.c.bak deleted file mode 100644 index 209a80f..0000000 --- a/HTTPcode.c.bak +++ /dev/null @@ -1,4849 +0,0 @@ -/* -Copyright 2001-2022 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - - -//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - -#define _CRT_SECURE_NO_DEPRECATE - -#define DllImport - -#include "CHeaders.h" -#include - -#include "tncinfo.h" -#include "time.h" -#include "bpq32.h" -#include "telnetserver.h" - -// This is needed to link with a lib built from source - -#ifdef WIN32 -#define ZEXPORT __stdcall -#endif - -#include "zlib.h" - -#define CKernel -#include "httpconnectioninfo.h" - -extern int MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS, L3FRAMES; -extern int NUMBEROFNODES, MAXDESTS, L4CONNECTSOUT, L4CONNECTSIN, L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES; -extern int STATSTIME; -extern TRANSPORTENTRY * L4TABLE; -extern BPQVECSTRUC BPQHOSTVECTOR[]; -extern BOOL APRSApplConnected; -extern char VersionString[]; -VOID FormatTime3(char * Time, time_t cTime); -DllExport int APIENTRY Get_APPLMASK(int Stream); -VOID SaveUIConfig(); -int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); -VOID SetupUI(int Port); -VOID SendUIBeacon(int Port); -VOID GetParam(char * input, char * key, char * value); -VOID ARDOPAbort(struct TNCINFO * TNC); -VOID WriteMiniDump(); -BOOL KillTNC(struct TNCINFO * TNC); -BOOL RestartTNC(struct TNCINFO * TNC); -int GetAISPageInfo(char * Buffer, int ais, int adsb); -int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb); -unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); -char * stristr (char *ch1, char *ch2); -int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL); -char * GetStandardPage(char * FN, int * Len); -BOOL SHA1PasswordHash(char * String, char * Hash); -char * byte_base64_encode(char *str, int len); - -extern struct ROUTE * NEIGHBOURS; -extern int ROUTE_LEN; -extern int MAXNEIGHBOURS; - -extern struct DEST_LIST * DESTS; // NODE LIST -extern int DEST_LIST_LEN; -extern int MAXDESTS; // MAX NODES IN SYSTEM - -extern struct _LINKTABLE * LINKS; -extern int LINK_TABLE_LEN; -extern int MAXLINKS; -extern char * RigWebPage; -extern COLORREF Colours[256]; - -extern BOOL IncludesMail; -extern BOOL IncludesChat; - -extern BOOL APRSWeb; -extern BOOL RigActive; - -extern HKEY REGTREE; - -extern BOOL APRSActive; - -extern UCHAR LogDirectory[]; - -extern struct RIGPORTINFO * PORTInfo[34]; -extern int NumberofPorts; - -char * strlop(char * buf, char delim); -VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); -int CompareNode(const void *a, const void *b); -int CompareAlias(const void *a, const void *b); -void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen); -void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); -struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); -int SetupNodeMenu(char * Buff, int SYSOP); -int StatusProc(char * Buff); -int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); -int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); -VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE); - - -static struct HTTPConnectionInfo * SessionList; // active term mode sessions - -char Mycall[10]; - -char MAILPipeFileName[] = "\\\\.\\pipe\\BPQMAILWebPipe"; -char CHATPipeFileName[] = "\\\\.\\pipe\\BPQCHATWebPipe"; - -char Index[] = "%s's BPQ32 Web Server

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

Routes

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

Nodes %s

"; - -char NodeHddr[] = "
" -"" -"" -"
" -"

Nodes %s

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

Node Stats

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

Stats for Port %d

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

Beacon Configuration for Port %d

You need to be signed in to save changes

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

" -""; - - -char LinkHddr[] = "

Links

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

Sessions

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

BPQ32 Node %s Terminal Access

" -"

Please enter username and password to access the node

" -"" -"
%s%s%s
" -"" -"
User
Password
" -"

" -""; - - -char PassError[] = "

Sorry, User or Password is invalid - please try again

"; - -char BusyError[] = "

Sorry, No sessions available - please try later

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

BPQ32 Node %s

" -"
" -"

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

BPQ32 Node %s SYSOP Access

" -"

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

" -"

Please enter Callsign and Password to access the Node

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

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

BPQ32 Mail Server %s Access

" -"

Please enter Callsign and Password to access the BBS

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

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

BPQ32 Chat Server %s Access

" -"

Please enter Callsign and Password to access the Chat Server

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

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

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

" -"
"; - -static char EXCEPTMSG[80] = ""; - - -void UndoTransparency(char * input) -{ - char * ptr1, * ptr2; - char c; - int hex; - - if (input == NULL) - return; - - ptr1 = ptr2 = input; - - // Convert any %xx constructs - - while (1) - { - c = *(ptr1++); - - if (c == 0) - break; - - if (c == '%') - { - c = *(ptr1++); - if(isdigit(c)) - hex = (c - '0') << 4; - else - hex = (tolower(c) - 'a' + 10) << 4; - - c = *(ptr1++); - if(isdigit(c)) - hex += (c - '0'); - else - hex += (tolower(c) - 'a' + 10); - - *(ptr2++) = hex; - } - else if (c == '+') - *(ptr2++) = 32; - else - *(ptr2++) = c; - } - *ptr2 = 0; -} - - - - -VOID PollSession(struct HTTPConnectionInfo * Session) -{ - int state, change; - int count, len; - char Msg[400] = ""; - char Formatted[8192]; - char * ptr1, * ptr2; - char c; - int Line; - - // Poll Node - - SessionState(Session->Stream, &state, &change); - - if (change == 1) - { - int Line = Session->LastLine++; - free(Session->ScreenLines[Line]); - - if (state == 1)// Connected - Session->ScreenLines[Line] = _strdup("*** Connected
\r\n"); - else - Session->ScreenLines[Line] = _strdup("*** Disconnected
\r\n"); - - if (Line == 99) - Session->LastLine = 0; - - Session->Changed = TRUE; - } - - if (RXCount(Session->Stream) > 0) - { - int realLen = 0; - - do - { - GetMsg(Session->Stream, &Msg[0], &len, &count); - - // replace cr with
and space with   - - - ptr1 = Msg; - ptr2 = &Formatted[0]; - - if (Session->PartLine) - { - // Last line was incomplete - append to it - - realLen = Session->PartLine; - - Line = Session->LastLine - 1; - - if (Line < 0) - Line = 99; - - strcpy(Formatted, Session->ScreenLines[Line]); - ptr2 += strlen(Formatted); - - Session->LastLine = Line; - Session->PartLine = FALSE; - } - - while (len--) - { - c = *(ptr1++); - realLen++; - - if (c == 13) - { - int LineLen; - - strcpy(ptr2, "
\r\n"); - - // Write to screen - - Line = Session->LastLine++; - free(Session->ScreenLines[Line]); - - LineLen = (int)strlen(Formatted); - - // if line starts with a colour code, process it - - if (Formatted[0] == 0x1b && LineLen > 1) - { - int ColourCode = Formatted[1] - 10; - COLORREF Colour = Colours[ColourCode]; - char ColString[30]; - - memmove(&Formatted[20], &Formatted[2], LineLen); - sprintf(ColString, "", GetRValue(Colour), GetGValue(Colour), GetBValue(Colour)); - memcpy(Formatted, ColString, 20); - strcat(Formatted, ""); - LineLen =+ 28; - } - - Session->ScreenLineLen[Line] = LineLen; - Session->ScreenLines[Line] = _strdup(Formatted); - - if (Line == 99) - Session->LastLine = 0; - - ptr2 = &Formatted[0]; - realLen = 0; - - } - else if (c == 32) - { - memcpy(ptr2, " ", 6); - ptr2 += 6; - - // Make sure line isn't too long - // but beware of spaces expanded to   - count chars in line - - if ((realLen) > 100) - { - strcpy(ptr2, "
\r\n"); - - Line = Session->LastLine++; - free(Session->ScreenLines[Line]); - - Session->ScreenLines[Line] = _strdup(Formatted); - - if (Line == 99) - Session->LastLine = 0; - - ptr2 = &Formatted[0]; - realLen = 0; - } - } - else if (c == '>') - { - memcpy(ptr2, ">", 4); - ptr2 += 4; - } - else if (c == '<') - { - memcpy(ptr2, "<", 4); - ptr2 += 4; - } - else - *(ptr2++) = c; - - } - - *ptr2 = 0; - - if (ptr2 != &Formatted[0]) - { - // Incomplete line - - // Save to screen - - Line = Session->LastLine++; - free(Session->ScreenLines[Line]); - - Session->ScreenLines[Line] = _strdup(Formatted); - - if (Line == 99) - Session->LastLine = 0; - - Session->PartLine = realLen; - } - - // strcat(Session->ScreenBuffer, Formatted); - Session->Changed = TRUE; - - } while (count > 0); - } -} - - -VOID HTTPTimer() -{ - // Run every tick. Check for status change and data available - - struct HTTPConnectionInfo * Session = SessionList; // active term mode sessions - struct HTTPConnectionInfo * PreviousSession = NULL; - -// inf(); - - while (Session) - { - Session->KillTimer++; - - if (Session->Key[0] != 'T') - { - PreviousSession = Session; - Session = Session->Next; - continue; - } - - if (Session->KillTimer > 3000) // Around 5 mins - { - int i; - int Stream = Session->Stream; - - for (i = 0; i < 100; i++) - { - free(Session->ScreenLines[i]); - } - - SessionControl(Stream, 2, 0); - SessionState(Stream, &i, &i); - DeallocateStream(Stream); - - if (PreviousSession) - PreviousSession->Next = Session->Next; // Remove from chain - else - SessionList = Session->Next; - - free(Session); - - break; - } - - PollSession(Session); - - // if (Session->ResponseTimer == 0 && Session->Changed) - // Debugprintf("Data to send but no outstanding GET"); - - if (Session->ResponseTimer) - { - Session->ResponseTimer--; - - if (Session->ResponseTimer == 0 || Session->Changed) - { - SOCKET sock = Session->sock; - char _REPLYBUFFER[100000]; - int ReplyLen; - char Header[256]; - int HeaderLen; - int Last = Session->LastLine; - int n; - struct TNCINFO * TNC = Session->TNC; - struct TCPINFO * TCP = 0; - - if (TNC) - TCP = TNC->TCPInfo; - - if (TCP && TCP->WebTermCSS) - sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); - else - sprintf(_REPLYBUFFER, TermOutput, ""); - - for (n = Last;;) - { - strcat(_REPLYBUFFER, Session->ScreenLines[n]); - - if (n == 99) - n = -1; - - if (++n == Last) - break; - } - - ReplyLen = (int)strlen(_REPLYBUFFER); - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); - sendandcheck(sock, Header, HeaderLen); - sendandcheck(sock, _REPLYBUFFER, ReplyLen); - - Session->ResponseTimer = Session->Changed = 0; - } - } - PreviousSession = Session; - Session = Session->Next; - } -} - -struct HTTPConnectionInfo * AllocateSession(SOCKET sock, char Mode) -{ - time_t KeyVal; - struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo)); - int i; - - if (Session == NULL) - return NULL; - - if (Mode == 'T') - { - // Terminal - - for (i = 0; i < 20; i++) - Session->ScreenLines[i] = _strdup("Scroll to end
"); - - for (i = 20; i < 100; i++) - Session->ScreenLines[i] = _strdup("
\r\n"); - - Session->Stream = FindFreeStream(); - - if (Session->Stream == 0) - return NULL; - - SessionControl(Session->Stream, 1, 0); - } - - KeyVal = (int)sock * time(NULL); - - sprintf(Session->Key, "%c%012X", Mode, (int)KeyVal); - - if (SessionList) - Session->Next = SessionList; - - SessionList = Session; - - return Session; -} - -struct HTTPConnectionInfo * FindSession(char * Key) -{ - struct HTTPConnectionInfo * Session = SessionList; - - while (Session) - { - if (strcmp(Session->Key, Key) == 0) - return Session; - - Session = Session->Next; - } - - return NULL; -} - -void ProcessTermInput(SOCKET sock, char * MsgPtr, int MsgLen, char * Key) -{ - char _REPLYBUFFER[1024]; - int ReplyLen; - char Header[256]; - int HeaderLen; - int State; - struct HTTPConnectionInfo * Session = FindSession(Key); - int Stream; - - if (Session == NULL) - { - ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); - } - else - { - char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers - char * end = &MsgPtr[MsgLen]; - int Line = Session->LastLine++; - char * ptr1, * ptr2; - char c; - UCHAR hex; - - struct TNCINFO * TNC = Session->TNC; - struct TCPINFO * TCP = 0; - - if (TNC) - TCP = TNC->TCPInfo; - - if (TCP && TCP->WebTermCSS) - ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, TCP->WebTermCSS); - else - ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); - - - Stream = Session->Stream; - - input += 10; - ptr1 = ptr2 = input; - - // Convert any %xx constructs - - while (ptr1 != end) - { - c = *(ptr1++); - if (c == '%') - { - c = *(ptr1++); - if(isdigit(c)) - hex = (c - '0') << 4; - else - hex = (tolower(c) - 'a' + 10) << 4; - - c = *(ptr1++); - if(isdigit(c)) - hex += (c - '0'); - else - hex += (tolower(c) - 'a' + 10); - - *(ptr2++) = hex; - } - else if (c == '+') - *(ptr2++) = 32; - else - *(ptr2++) = c; - } - - end = ptr2; - - *ptr2 = 0; - - strcat(input, "
\r\n"); - - free(Session->ScreenLines[Line]); - - Session->ScreenLines[Line] = _strdup(input); - - if (Line == 99) - Session->LastLine = 0; - - *end++ = 13; - *end = 0; - - SessionStateNoAck(Stream, &State); - - if (State == 0) - { - char AXCall[10]; - SessionControl(Stream, 1, 0); - if (BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION == NULL) - { - //No L4 sessions free - - ReplyLen = sprintf(_REPLYBUFFER, "%s", NoSessions); - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); - send(sock, Header, HeaderLen, 0); - send(sock, _REPLYBUFFER, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); - return; - } - - ConvToAX25(Session->HTTPCall, AXCall); - ChangeSessionCallsign(Stream, AXCall); - if (Session->USER) - BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = Session->USER->Secure; - else - Debugprintf("HTTP Term Session->USER is NULL"); - } - - SendMsg(Stream, input, (int)(end - input)); - Session->Changed = TRUE; - Session->KillTimer = 0; - } - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); - send(sock, Header, HeaderLen, 0); - send(sock, _REPLYBUFFER, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); -} - - -void ProcessTermClose(SOCKET sock, char * MsgPtr, int MsgLen, char * Key, int LOCAL) -{ - char _REPLYBUFFER[8192]; - int ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); - char Header[256]; - int HeaderLen; - struct HTTPConnectionInfo * Session = FindSession(Key); - - if (Session) - { - Session->KillTimer = 99999; - } - - ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" - "\r\n", ReplyLen + (int)strlen(Tail)); - send(sock, Header, HeaderLen, 0); - send(sock, _REPLYBUFFER, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); -} - -int ProcessTermSignon(struct TNCINFO * TNC, SOCKET sock, char * MsgPtr, int MsgLen, int LOCAL) -{ - char _REPLYBUFFER[8192]; - int ReplyLen; - char Header[256]; - int HeaderLen; - char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers - char * user, * password, * Context, * Appl; - char NoApp[] = ""; - struct TCPINFO * TCP = TNC->TCPInfo; - - if (input) - { - int i; - struct UserRec * USER; - - UndoTransparency(input); - - if (strstr(input, "Cancel=Cancel")) - { - ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); - goto Sendit; - } - user = strtok_s(&input[9], "&", &Context); - password = strtok_s(NULL, "=", &Context); - password = strtok_s(NULL, "&", &Context); - - Appl = strtok_s(NULL, "=", &Context); - Appl = strtok_s(NULL, "&", &Context); - - if (Appl == 0) - Appl = NoApp; - - if (password == NULL) - { - ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); - goto Sendit; - } - - for (i = 0; i < TCP->NumberofUsers; i++) - { - USER = TCP->UserRecPtr[i]; - - if ((strcmp(password, USER->Password) == 0) && - ((_stricmp(user, USER->UserName) == 0 ) || (_stricmp(USER->UserName, "ANON") == 0))) - { - // ok - - struct HTTPConnectionInfo * Session = AllocateSession(sock, 'T'); - - if (Session) - { - char AXCall[10]; - ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, Session->Key, Session->Key, Session->Key); - if (_stricmp(USER->UserName, "ANON") == 0) - strcpy(Session->HTTPCall, _strupr(user)); - else - strcpy(Session->HTTPCall, USER->Callsign); - ConvToAX25(Session->HTTPCall, AXCall); - ChangeSessionCallsign(Session->Stream, AXCall); - BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; - Session->USER = USER; - - if (USER->Appl[0]) - SendMsg(Session->Stream, USER->Appl, (int)strlen(USER->Appl)); - } - else - { - ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); - } - break; - } - } - - if (i == TCP->NumberofUsers) - { - // Not found - - ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); - } - - } - -Sendit: - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); - send(sock, Header, HeaderLen, 0); - send(sock, _REPLYBUFFER, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); - - return 1; -} - -char * LookupKey(char * Key) -{ - if (strcmp(Key, "##MY_CALLSIGN##") == 0) - { - char Mycall[10]; - memcpy(Mycall, &MYNODECALL, 10); - strlop(Mycall, ' '); - - return _strdup(Mycall); - } - return NULL; -} - - -int ProcessSpecialPage(char * Buffer, int FileSize) -{ - // replaces ##xxx### constructs with the requested data - - char * NewMessage = malloc(100000); - char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; - int PrevLen; - int BytesLeft = FileSize; - int NewFileSize = FileSize; - char * StripPtr = ptr1; - - // strip comments blocks - - while (ptr4 = strstr(ptr1, ""); - if (ptr2) - { - PrevLen = (int)(ptr4 - ptr1); - memcpy(StripPtr, ptr1, PrevLen); - StripPtr += PrevLen; - ptr1 = ptr2 + 3; - BytesLeft = (int)(FileSize - (ptr1 - Buffer)); - } - } - - memcpy(StripPtr, ptr1, BytesLeft); - StripPtr += BytesLeft; - - BytesLeft = (int)(StripPtr - Buffer); - - FileSize = BytesLeft; - NewFileSize = FileSize; - ptr1 = Buffer; - ptr1[FileSize] = 0; - -loop: - ptr2 = strstr(ptr1, "##"); - - if (ptr2) - { - PrevLen = (int)(ptr2 - ptr1); // Bytes before special text - - ptr3 = strstr(ptr2+2, "##"); - - if (ptr3) - { - char Key[80] = ""; - int KeyLen; - char * NewText; - int NewTextLen; - - ptr3 += 2; - KeyLen = (int)(ptr3 - ptr2); - - if (KeyLen < 80) - memcpy(Key, ptr2, KeyLen); - - NewText = LookupKey(Key); - - if (NewText) - { - NewTextLen = (int)strlen(NewText); - NewFileSize = NewFileSize + NewTextLen - KeyLen; - // NewMessage = realloc(NewMessage, NewFileSize); - - memcpy(NewPtr, ptr1, PrevLen); - NewPtr += PrevLen; - memcpy(NewPtr, NewText, NewTextLen); - NewPtr += NewTextLen; - - free(NewText); - NewText = NULL; - } - else - { - // Key not found, so just leave - - memcpy(NewPtr, ptr1, PrevLen + KeyLen); - NewPtr += (PrevLen + KeyLen); - } - - ptr1 = ptr3; // Continue scan from here - BytesLeft = (int)(Buffer + FileSize - ptr3); - } - else // Unmatched ## - { - memcpy(NewPtr, ptr1, PrevLen + 2); - NewPtr += (PrevLen + 2); - ptr1 = ptr2 + 2; - } - goto loop; - } - - // Copy Rest - - memcpy(NewPtr, ptr1, BytesLeft); - NewMessage[NewFileSize] = 0; - - strcpy(Buffer, NewMessage); - free(NewMessage); - - return NewFileSize; -} - -int SendMessageFile(SOCKET sock, char * FN, BOOL OnlyifExists, int allowDeflate) -{ - int FileSize = 0, Sent, Loops = 0; - char * MsgBytes; - char MsgFile[512]; - FILE * hFile; - int ReadLen; - BOOL Special = FALSE; - int Len; - int HeaderLen; - char Header[256]; - char TimeString[64]; - char FileTimeString[64]; - struct stat STAT; - char * ptr; - char * Compressed = 0; - char Encoding[] = "Content-Encoding: deflate\r\n"; - char Type[64] = "Content-Type: text/html\r\n"; - -#ifdef WIN32 - - struct _EXCEPTION_POINTERS exinfo; - strcpy(EXCEPTMSG, "SendMessageFile"); - - __try { -#endif - - UndoTransparency(FN); - - if (strstr(FN, "..")) - { - FN[0] = '/'; - FN[1] = 0; - } - - if (strlen(FN) > 256) - { - FN[256] = 0; - Debugprintf("HTTP File Name too long %s", FN); - } - - if (strcmp(FN, "/") == 0) - if (APRSActive) - sprintf(MsgFile, "%s/HTML/index.html", BPQDirectory); - else - sprintf(MsgFile, "%s/HTML/indexnoaprs.html", BPQDirectory); - else - sprintf(MsgFile, "%s/HTML%s", BPQDirectory, FN); - - - // First see if file exists so we can override standard ones in code - - if (stat(MsgFile, &STAT) == 0 && (hFile = fopen(MsgFile, "rb"))) - { - FileSize = STAT.st_size; - - MsgBytes = zalloc(FileSize + 1); - - ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - // ft.QuadPart -= 116444736000000000; - // ft.QuadPart /= 10000000; - - // ctime = ft.LowPart; - - FormatTime3(FileTimeString, STAT.st_ctime); - } - else - { - // See if it is a hard coded file - - MsgBytes = GetStandardPage(&FN[1], &FileSize); - - if (MsgBytes) - { - if (FileSize == 0) - FileSize = strlen(MsgBytes); - - FormatTime3(FileTimeString, 0); - } - else - { - if (OnlyifExists) // Set if we dont want an error response if missing - return -1; - - Len = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); - send(sock, Header, Len, 0); - return 0; - } - } - - // if HTML file, look for ##...## substitutions - - if ((strcmp(FN, "/") == 0 || strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) - { - FileSize = ProcessSpecialPage(MsgBytes, FileSize); - FormatTime3(FileTimeString, time(NULL)); - - } - - FormatTime3(TimeString, time(NULL)); - - ptr = FN; - - while (strchr(ptr, '.')) - { - ptr = strchr(ptr, '.'); - ++ptr; - } - - if (_stricmp(ptr, "js") == 0) - strcpy(Type, "Content-Type: text/javascript\r\n"); - - if (_stricmp(ptr, "pdf") == 0) - strcpy(Type, "Content-Type: application/pdf\r\n"); - - if (allowDeflate) - { - Compressed = Compressit(MsgBytes, FileSize, &FileSize); - } - else - { - Encoding[0] = 0; - Compressed = MsgBytes; - } - - if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0) - strcpy(Type, "Content-Type: image\r\n"); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "%s%s" - "\r\n", FileSize, TimeString, FileTimeString, Type, Encoding); - - send(sock, Header, HeaderLen, 0); - - Sent = send(sock, Compressed, FileSize, 0); - - while (Sent != FileSize && Loops++ < 3000) // 100 secs max - { - if (Sent > 0) // something sent - { -// Debugprintf("%d out of %d sent", Sent, FileSize); - FileSize -= Sent; - memmove(Compressed, &Compressed[Sent], FileSize); - } - - Sleep(30); - Sent = send(sock, Compressed, FileSize, 0); - } - -// Debugprintf("%d out of %d sent %d loops", Sent, FileSize, Loops); - - - free (MsgBytes); - if (allowDeflate) - free (Compressed); - -#ifdef WIN32 - } -#include "StdExcept.c" - Debugprintf("Sending FIle %s", FN); -} -#endif - -return 0; -} - -VOID sendandcheck(SOCKET sock, const char * Buffer, int Len) -{ - int Loops = 0; - int Sent = send(sock, Buffer, Len, 0); - char * Copy = NULL; - - while (Sent != Len && Loops++ < 300) // 10 secs max - { - // Debugprintf("%d out of %d sent %d Loops", Sent, Len, Loops); - - if (Copy == NULL) - { - Copy = malloc(Len); - memcpy(Copy, Buffer, Len); - } - - if (Sent > 0) // something sent - { - Len -= Sent; - memmove(Copy, &Copy[Sent], Len); - } - - Sleep(30); - Sent = send(sock, Copy, Len, 0); - } - - if (Copy) - free(Copy); - - return; -} - -int RefreshTermWindow(struct TCPINFO * TCP, struct HTTPConnectionInfo * Session, char * _REPLYBUFFER) -{ - char Msg[400] = ""; - int HeaderLen, ReplyLen; - char Header[256]; - - PollSession(Session); // See if anything received - - if (Session->Changed) - { - int Last = Session->LastLine; - int n; - - if (TCP && TCP->WebTermCSS) - sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); - else - sprintf(_REPLYBUFFER, TermOutput, ""); - - for (n = Last;;) - { - strcat(_REPLYBUFFER, Session->ScreenLines[n]); - - if (n == 99) - n = -1; - - if (++n == Last) - break; - } - - Session->Changed = 0; - - ReplyLen = (int)strlen(_REPLYBUFFER); - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); - sendandcheck(Session->sock, Header, HeaderLen); - sendandcheck(Session->sock, _REPLYBUFFER, ReplyLen); - - return 1; - } - else - return 0; -} - -int SetupNodeMenu(char * Buff, int LOCAL) -{ - int Len = 0, i; - struct TNCINFO * TNC; - int top = 0, left = 0; - - char NodeMenuHeader[] = "%s's BPQ32 Web Server" - "" - - "" - "

BPQ32 Node %s

" - "

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

Ports

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

Call %s not found

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

Info for Node %s:%s

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

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

Neighbours

"); - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], - "" - ""); - - NRRoute = &Dest->NRROUTE[0]; - - Active = Dest->DEST_ROUTE; - - for (i = 1; i < 4; i++) - { - Neighbour = NRRoute->ROUT_NEIGHBOUR; - - if (Neighbour) - { - len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall); - Normcall[len] = 0; - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", - (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); - } - NRRoute++; - } - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Qual Obs Port Call
%c %d%d%d%s
"); - - goto SendResp; - - } - /* - - MOV ESI,OFFSET32 NODEROUTEHDDR - MOV ECX,11 - REP MOVSB - - LEA ESI,DEST_CALL[EBX] - CALL DECODENODENAME ; CONVERT TO ALIAS:CALL - REP MOVSB - - CMP DEST_RTT[EBX],0 - JE SHORT @f ; TIMER NOT SET - DEST PROBABLY NOT USED - - MOVSB ; ADD SPACE - CALL DORTT - - @@: - MOV AL,CR - STOSB - - MOV ECX,3 - MOV DH,DEST_ROUTE[EBX] ; CURRENT ACTIVE ROUTE - MOV DL,1 - - push ebx - - PUBLIC CMDN110 - CMDN110: - - MOV ESI,ROUT1_NEIGHBOUR[EBX] - CMP ESI,0 - JE CMDN199 - - - MOV AX,' ' - CMP DH,DL - JNE SHORT CMDN112 ; NOT CURRENT DEST - MOV AX,' >' - - CMDN112: - - STOSW - - PUSH ECX - - MOV AL,ROUT1_QUALITY[EBX] - CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS - - mov AL,' ' - stosb - - MOV AL,ROUT1_OBSCOUNT[EBX] - CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS - - mov AL,' ' - stosb - - MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT - CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS - - mov AL,' ' - stosb - - - PUSH EDI - CALL CONVFROMAX25 ; CONVERT TO CALL - POP EDI - - MOV ESI,OFFSET32 NORMCALL - REP MOVSB - - MOV AL,CR - STOSB - - ADD EBX,ROUTEVECLEN - INC DL ; ROUTE NUMBER - - POP ECX - DEC ECX - JNZ CMDN110 - - PUBLIC CMDN199 - CMDN199: - - POP EBX - - ; DISPLAY INP3 ROUTES - - MOV ECX,3 - MOV DL,4 - - PUBLIC CMDNINP3 - CMDNINP3: - - MOV ESI,INPROUT1_NEIGHBOUR[EBX] - CMP ESI,0 - JE CMDNINPEND - - MOV AX,' ' - CMP DH,DL - JNE SHORT @F ; NOT CURRENT DEST - MOV AX,' >' - - @@: - - STOSW - - PUSH ECX - - MOV AL, Hops1[EBX] - CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS - - mov AL,' ' - stosb - - MOVZX EAX, SRTT1[EBX] - - MOV EDX,0 - MOV ECX, 100 - DIV ECX - CALL CONV_5DIGITS - MOV AL,'.' - STOSB - MOV EAX, EDX - CALL PRINTNUM - MOV AL,'s' - STOSB - MOV AL,' ' - STOSB - - MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT - CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS - - mov AL,' ' - stosb - - PUSH EDI - CALL CONVFROMAX25 ; CONVERT TO CALL - POP EDI - - MOV ESI,OFFSET32 NORMCALL - REP MOVSB - - - MOV AL,CR - STOSB - - ADD EBX,INPROUTEVECLEN - INC DL ; ROUTE NUMBER - - POP ECX - LOOP CMDNINP3 - - CMDNINPEND: - - ret - - */ - - - if (_stricmp(NodeURL, "/Node/Routes.html") == 0) - { - struct ROUTE * Routes = NEIGHBOURS; - int MaxRoutes = MAXNEIGHBOURS; - int count; - char Normcall[10]; - char locked; - int NodeCount; - int Percent = 0; - int Iframes, Retries; - char Active[10]; - int Queued; - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); - - for (count=0; countNEIGHBOUR_CALL[0] != 0) - { - int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); - Normcall[len]=0; - - if ((Routes->NEIGHBOUR_FLAG & 1) == 1) - locked = '!'; - else - locked = ' '; - - NodeCount = COUNTNODES(Routes); - - if (Routes->NEIGHBOUR_LINK) - Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); - else - Queued = 0; - - Iframes = Routes->NBOUR_IFRAMES; - Retries = Routes->NBOUR_RETRIES; - - if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) - strcpy(Active, ">"); - else - strcpy(Active, " "); - - if (Iframes) - Percent = (Retries * 100) / Iframes; - else - Percent = 0; - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RouteLine, Active, Routes->NEIGHBOUR_PORT, Normcall, locked, - Routes->NEIGHBOUR_QUAL, NodeCount, Iframes, Retries, Percent, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, - Routes->NEIGHBOUR_TIME >> 8, Routes->NEIGHBOUR_TIME & 0xff, Queued, Routes->OtherendsRouteQual); - } - Routes+=1; - } - } - - if (_stricmp(NodeURL, "/Node/Links.html") == 0) - { - struct _LINKTABLE * Links = LINKS; - int MaxLinks = MAXLINKS; - int count; - char Normcall1[10]; - char Normcall2[10]; - char State[12] = "", Type[12] = "Uplink"; - int axState; - int cctType; - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", LinkHddr); - - for (count=0; countLINKCALL[0] != 0) - { - int len = ConvFromAX25(Links->LINKCALL, Normcall1); - Normcall1[len] = 0; - - len = ConvFromAX25(Links->OURCALL, Normcall2); - Normcall2[len] = 0; - - axState = Links->L2STATE; - - if (axState == 2) - strcpy(State, "Connecting"); - else if (axState == 3) - strcpy(State, "FRMR"); - else if (axState == 4) - strcpy(State, "Closing"); - else if (axState == 5) - strcpy(State, "Active"); - else if (axState == 6) - strcpy(State, "REJ Sent"); - - cctType = Links->LINKTYPE; - - if (cctType == 1) - strcpy(Type, "Uplink"); - else if (cctType == 2) - strcpy(Type, "Downlink"); - else if (cctType == 3) - strcpy(Type, "Node-Node"); - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], LinkLine, Normcall1, Normcall2, Links->LINKPORT->PORTNUMBER, - State, Type, 2 - Links->VER1FLAG ); - - Links+=1; - } - } - } - - if (_stricmp(NodeURL, "/Node/Users.html") == 0) - { - TRANSPORTENTRY * L4 = L4TABLE; - TRANSPORTENTRY * Partner; - int MaxLinks = MAXLINKS; - int count; - char State[12] = "", Type[12] = "Uplink"; - char LHS[50] = "", MID[10] = "", RHS[50] = ""; - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", UserHddr); - - for (count=0; count < MAXCIRCUITS; count++) - { - if (L4->L4USER[0]) - { - RHS[0] = MID[0] = 0; - - if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES - { - // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK - - if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END - { - // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION - // DISPLAY TO THE RIGHT FOR NOW - - strcpy(LHS, "(Closing) "); - DISPLAYCIRCUIT(L4, RHS); - goto CMDS50; - } - else - goto CMDS60; // WILL PROCESS FROM OTHER END - } - - if (L4->L4CROSSLINK == 0) - { - // Single Entry - - DISPLAYCIRCUIT(L4, LHS); - } - else - { - DISPLAYCIRCUIT(L4, LHS); - - Partner = L4->L4CROSSLINK; - - if (Partner->L4STATE == 5) - strcpy(MID, "<-->"); - else - strcpy(MID, "<~~>"); - - DISPLAYCIRCUIT(Partner, RHS); - } -CMDS50: - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], UserLine, LHS, MID, RHS); - } -CMDS60: - L4++; - } - } - /* - PUBLIC CMDUXX_1 - CMDUXX_1: - push EBX - push ESI - PUSH ECX - push EDI - - call _FINDDESTINATION - pop EDI - - jz SHORT NODE_FOUND - - push EDI ; NET/ROM not found - call CONVFROMAX25 ; CONVERT TO CALL - pop EDI - mov ESI,OFFSET32 NORMCALL - rep movsb - - jmp SHORT END_CMDUXX - - PUBLIC NODE_FOUND - NODE_FOUND: - - lea ESI,DEST_CALL[EBX] - call DECODENODENAME - - REP MOVSB - - PUBLIC END_CMDUXX - END_CMDUXX: - - POP ECX - pop ESI - pop EBX - ret - - }}} - */ - - else if (_stricmp(NodeURL, "/Node/Terminal.html") == 0) - { - if (COOKIE && Session) - { - // Already signed in as sysop - - struct UserRec * USER = Session->USER; - - struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); - - if (NewSession) - { - char AXCall[10]; - ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); - strcpy(NewSession->HTTPCall, USER->Callsign); - ConvToAX25(NewSession->HTTPCall, AXCall); - ChangeSessionCallsign(NewSession->Stream, AXCall); - BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; - Session->USER = USER; - NewSession->TNC = conn->TNC; - - - // if (Appl[0]) - // { - // strcat(Appl, "\r"); - // SendMsg(Session->Stream, Appl, strlen(Appl)); - // } - - } - else - { - ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); - } - } - else if (LOCAL) - { - // connected to 127.0.0.1 so sign in using node call - - struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); - - if (NewSession) - { - ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); - strcpy(NewSession->HTTPCall, MYNODECALL); - ChangeSessionCallsign(NewSession->Stream, MYCALL); - BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = TRUE; - NewSession->TNC = conn->TNC; - } - } - else - ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Context); - } - - else if (_stricmp(NodeURL, "/Node/Signon.html") == 0) - { - ReplyLen = sprintf(_REPLYBUFFER, NodeSignon, Mycall, Mycall, Context); - } - - else if (_stricmp(NodeURL, "/Node/Drivers") == 0) - { - int Bufferlen = SendMessageFile(sock, "/Drivers.htm", TRUE, allowDeflate); // return -1 if not found - - if (Bufferlen != -1) - return 0; // We've sent it - } - - else if (_stricmp(NodeURL, "/Node/OutputScreen.html") == 0) - { - struct HTTPConnectionInfo * Session = FindSession(Context); - - if (Session == NULL) - { - ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); - } - else - { - Session->sock = sock; // socket to reply on - ReplyLen = RefreshTermWindow(TCP, Session, _REPLYBUFFER); - - if (ReplyLen == 0) // Nothing new - { - // Debugprintf("GET with no data avail - response held"); - Session->ResponseTimer = 1200; // Delay response for up to a minute - } - else - { - // Debugprintf("GET - outpur sent, timer was %d, set to zero", Session->ResponseTimer); - Session->ResponseTimer = 0; - } - - Session->KillTimer = 0; - return 0; // Refresh has sent any available output - } - } - - else if (_stricmp(NodeURL, "/Node/InputLine.html") == 0) - { - struct TNCINFO * TNC = conn->TNC; - struct TCPINFO * TCP = 0; - - if (TNC) - TCP = TNC->TCPInfo; - - if (TCP && TCP->WebTermCSS) - ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, TCP->WebTermCSS); - else - ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, ""); - - } - - else if (_stricmp(NodeURL, "/Node/PTT") == 0) - { - struct TNCINFO * TNC = conn->TNC; - int x = atoi(Context); - } - - -SendResp: - - FormatTime3(TimeString, time(NULL)); - - strcpy(&_REPLYBUFFER[ReplyLen], Tail); - ReplyLen += (int)strlen(Tail); - - - if (allowDeflate) - { - Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); - } - else - { - Encoding[0] = 0; - Compressed = _REPLYBUFFER; - } - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" - "Date: %s\r\n%s\r\n", ReplyLen, TimeString, Encoding); - sendandcheck(sock, Header, HeaderLen); - sendandcheck(sock, Compressed, ReplyLen); - - if (allowDeflate) - free (Compressed); - } - return 0; - -#ifdef WIN32 - } -#include "StdExcept.c" -} -return 0; -#endif -} - -void ProcessHTTPMessage(void * conn) -{ - // conn is a malloc'ed copy to handle reused connections, so need to free it - - InnerProcessHTTPMessage((struct ConnectionInfo *)conn); - free(conn); - return; -} - -static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - - -VOID FormatTime3(char * Time, time_t cTime) -{ - struct tm * TM; - TM = gmtime(&cTime); - - sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], - TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); - -} - -// Sun, 06 Nov 1994 08:49:37 GMT - -int StatusProc(char * Buff) -{ - int i; - char callsign[12] = ""; - char flag[3]; - UINT Mask, MaskCopy; - int Flags; - int AppNumber; - int OneBits; - int Len = sprintf(Buff, "" - "Stream Status"); - - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], ""); - Len += sprintf(&Buff[Len], ""); - - for (i=1;i<65; i++) - { - callsign[0]=0; - - if (GetAllocationState(i)) - - strcpy(flag,"*"); - else - strcpy(flag," "); - - GetCallsign(i,callsign); - - Mask = MaskCopy = Get_APPLMASK(i); - - // if only one bit set, convert to number - - AppNumber = 0; - OneBits = 0; - - while (MaskCopy) - { - if (MaskCopy & 1) - OneBits++; - - AppNumber++; - MaskCopy = MaskCopy >> 1; - } - - Flags=GetApplFlags(i); - - if (OneBits > 1) - Len += sprintf(&Buff[Len], "" - "", - i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); - - else - Len += sprintf(&Buff[Len], "" - "", - i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); - - if ((i & 1) == 0) - Len += sprintf(&Buff[Len], ""); - - } - - Len += sprintf(&Buff[Len], "
    RX   TX   MON  App  Flg Callsign  Program    RX   TX   MON  App  Flg Callsign  Program
%d%s%d%d%d%x%x%s%s%d%s%d%d%d%d%x%s%s
"); - return Len; -} - -int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) -{ - int ReplyLen = 0; - char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers - char * user, * password, * Key; - char Header[256]; - int HeaderLen; - struct HTTPConnectionInfo *Sess; - - - if (input) - { - int i; - struct UserRec * USER; - - UndoTransparency(input); - - if (strstr(input, "Cancel=Cancel")) - { - ReplyLen = SetupNodeMenu(Reply, LOCAL); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" - "\r\n", (int)(ReplyLen + strlen(Tail))); - send(sock, Header, HeaderLen, 0); - send(sock, Reply, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); - } - user = strtok_s(&input[9], "&", &Key); - password = strtok_s(NULL, "=", &Key); - password = Key; - - for (i = 0; i < TCP->NumberofUsers; i++) - { - USER = TCP->UserRecPtr[i]; - - if (user && _stricmp(user, USER->UserName) == 0) - { - if (strcmp(password, USER->Password) == 0 && USER->Secure) - { - // ok - - Sess = *Session = AllocateSession(sock, 'N'); - Sess->USER = USER; - - ReplyLen = SetupNodeMenu(Reply, LOCAL); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" - "Set-Cookie: BPQSessionCookie=%s; Path = /\r\n\r\n", (int)(ReplyLen + strlen(Tail)), Sess->Key); - send(sock, Header, HeaderLen, 0); - send(sock, Reply, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); - - return ReplyLen; - } - } - } - } - - ReplyLen = sprintf(Reply, NodeSignon, Mycall, Mycall); - ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); - - HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(ReplyLen + strlen(Tail))); - send(sock, Header, HeaderLen, 0); - send(sock, Reply, ReplyLen, 0); - send(sock, Tail, (int)strlen(Tail), 0); - - return 0; - - - return ReplyLen; -} - - - - -int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) -{ - int ReplyLen = 0; - char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers - char * user, * password, * Key; - struct HTTPConnectionInfo * NewSession; - - if (input) - { - int i; - struct UserRec * USER; - - UndoTransparency(input); - - if (strstr(input, "Cancel=Cancel")) - { - ReplyLen = SetupNodeMenu(Reply, LOCAL); - return ReplyLen; - } - user = strtok_s(&input[9], "&", &Key); - password = strtok_s(NULL, "=", &Key); - password = Key; - - for (i = 0; i < TCP->NumberofUsers; i++) - { - USER = TCP->UserRecPtr[i]; - - if (user && _stricmp(user, USER->UserName) == 0) - { - if (strcmp(password, USER->Password) == 0 && (USER->Secure || WebMail)) - { - // ok - - NewSession = AllocateSession(Appl[0], 'M'); - - *Session = NewSession; - - if (NewSession) - { - - ReplyLen = 0; - strcpy(NewSession->Callsign, USER->Callsign); - } - else - { - ReplyLen = SetupNodeMenu(Reply, LOCAL); - ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); - } - return ReplyLen; - } - } - } - } - - ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); - ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); - - return ReplyLen; -} - - -int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) -{ - int ReplyLen = 0; - char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers - char * user, * password, * Key; - - if (input) - { - int i; - struct UserRec * USER; - - UndoTransparency(input); - - if (strstr(input, "Cancel=Cancel")) - { - ReplyLen = SetupNodeMenu(Reply, LOCAL); - return ReplyLen; - } - - user = strtok_s(&input[9], "&", &Key); - password = strtok_s(NULL, "=", &Key); - password = Key; - - - for (i = 0; i < TCP->NumberofUsers; i++) - { - USER = TCP->UserRecPtr[i]; - - if (user && _stricmp(user, USER->UserName) == 0) - { - if (strcmp(password, USER->Password) == 0 && USER->Secure) - { - // ok - - *Session = AllocateSession(Appl[0], 'C'); - - if (Session) - { - ReplyLen = 0; - } - else - { - ReplyLen = SetupNodeMenu(Reply, LOCAL); - ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); - } - return ReplyLen; - } - } - } - } - - ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); - ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); - - return ReplyLen; - -} - -#define SHA1_HASH_LEN 20 - -/* - -Copyright (C) 1998, 2009 -Paul E. Jones - -Freeware Public License (FPL) - -This software is licensed as "freeware." Permission to distribute -this software in source and binary forms, including incorporation -into other products, is hereby granted without a fee. THIS SOFTWARE -IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD -LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER -DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA -OR DATA BEING RENDERED INACCURATE. -*/ - -/* sha1.h - * - * Copyright (C) 1998, 2009 - * Paul E. Jones - * All Rights Reserved - * - ***************************************************************************** - * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ - ***************************************************************************** - * - * Description: - * This class implements the Secure Hashing Standard as defined - * in FIPS PUB 180-1 published April 17, 1995. - * - * Many of the variable names in the SHA1Context, especially the - * single character names, were used because those were the names - * used in the publication. - * - * Please read the file sha1.c for more information. - * - */ - -#ifndef _SHA1_H_ -#define _SHA1_H_ - -/* - * This structure will hold context information for the hashing - * operation - */ -typedef struct SHA1Context -{ - unsigned Message_Digest[5]; /* Message Digest (output) */ - - unsigned Length_Low; /* Message length in bits */ - unsigned Length_High; /* Message length in bits */ - - unsigned char Message_Block[64]; /* 512-bit message blocks */ - int Message_Block_Index; /* Index into message block array */ - - int Computed; /* Is the digest computed? */ - int Corrupted; /* Is the message digest corruped? */ -} SHA1Context; - -/* - * Function Prototypes - */ -void SHA1Reset(SHA1Context *); -int SHA1Result(SHA1Context *); -void SHA1Input( SHA1Context *, const unsigned char *, unsigned); - -#endif - -BOOL SHA1PasswordHash(char * lpszPassword, char * Hash) -{ - SHA1Context sha; - int i; - - SHA1Reset(&sha); - SHA1Input(&sha, lpszPassword, strlen(lpszPassword)); - SHA1Result(&sha); - - // swap byte order if little endian - - for (i = 0; i < 5; i++) - sha.Message_Digest[i] = htonl(sha.Message_Digest[i]); - - memcpy(Hash, &sha.Message_Digest[0], 20); - - return TRUE; -} - -int BuildRigCtlPage(char * _REPLYBUFFER) -{ - int ReplyLen; - - struct RIGPORTINFO * PORT; - struct RIGINFO * RIG; - int p, i; - - char Page[] = - "\r\n" - // "\r\n" - "Rigcontrol\r\n" - "" - "

Rigcontrol

\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - ""; - char RigLine[] = - "\r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n" - " \r\n"; - char Tail[] = - "
RadioFreqModeSTPorts
%s%s%s/1%c%c%s
\r\n" - "\r\n"; - - ReplyLen = sprintf(_REPLYBUFFER, "%s", Page); - - for (p = 0; p < NumberofPorts; p++) - { - PORT = PORTInfo[p]; - - for (i=0; i< PORT->ConfiguredRigs; i++) - { - RIG = &PORT->Rigs[i]; - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigLine, RIG->WEB_Label, RIG->WEB_FREQ, RIG->WEB_MODE, RIG->WEB_SCAN, RIG->WEB_PTT, RIG->WEB_PORTS, RIG->Interlock); - } - } - - ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", Tail); - return ReplyLen; -} - - -void SendRigWebPage() -{ - int i, n; - struct ConnectionInfo * sockptr; - struct TNCINFO * TNC; - struct TCPINFO * TCP; - - for (i = 0; i < 33; i++) - { - TNC = TNCInfo[i]; - - if (TNC && TNC->Hardware == H_TELNET) - { - TCP = TNC->TCPInfo; - - if (TCP) - { - for (n = 0; n <= TCP->MaxSessions; n++) - { - sockptr = TNC->Streams[n].ConnectionInfo; - - if (sockptr->SocketActive) - { - if (sockptr->HTTPMode && sockptr->WebSocks && strcmp(sockptr->WebURL, "RIGCTL") == 0) - { - char RigMsg[8192]; - int RigMsgLen = strlen(RigWebPage); - char* ptr; - - RigMsg[0] = 0x81; // Fin, Data - RigMsg[1] = 126; // Unmasked, Extended Len - RigMsg[2] = RigMsgLen >> 8; - RigMsg[3] = RigMsgLen & 0xff; - strcpy(&RigMsg[4], RigWebPage); - - // If secure session enable PTT button - - if (sockptr->WebSecure) - { - while (ptr = strstr(RigMsg, "hidden")) - memcpy(ptr, " ", 6); - } - - send(sockptr->socket, RigMsg, RigMsgLen + 4, 0); - } - } - } - } - } - } -} - -// Webmail web socket code - -int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); - -void ProcessWebmailWebSockThread(void * conn) -{ - // conn is a malloc'ed copy to handle reused connections, so need to free it - - struct ConnectionInfo * sockptr = (struct ConnectionInfo *)conn; - char * URL = sockptr->WebURL; - int Loops = 0; - int Sent; - struct HTTPConnectionInfo Dummy = {0}; - int ReplyLen = 0; - int InputLen = 0; - -#ifdef LINBPQ - - char _REPLYBUFFER[250000]; - - ReplyLen = ProcessWebmailWebSock(URL, _REPLYBUFFER); - - // Send may block - - Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); - - while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max - { - if (Sent > 0) // something sent - { - ReplyLen -= Sent; - memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); - } - - Sleep(30); - Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); - } - -#else - // Send URL to BPQMail via Pipe. Just need a dummy session, as URL contains session key - - HANDLE hPipe; - char Reply[250000]; - - - - hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, - 0, // exclusive access - NULL, // no security attrs - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL ); - - if (hPipe == (HANDLE)-1) - { - free(conn); - return; - } - - WriteFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); - WriteFile(hPipe, URL, strlen(URL), &InputLen, NULL); - - ReadFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); - ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); - - if (ReplyLen <= 0) - { - InputLen = GetLastError(); - } - - CloseHandle(hPipe); - - // ?? do we need a thread to handle write which may block - - Sent = send(sockptr->socket, Reply, ReplyLen, 0); - - while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max - { - // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); - - if (Sent > 0) // something sent - { - InputLen -= Sent; - memmove(Reply, &Reply[Sent], ReplyLen); - } - - Sleep(30); - Sent = send(sockptr->socket, Reply, ReplyLen, 0); - } -#endif - free(conn); - return; -} - -/* - * sha1.c - * - * Copyright (C) 1998, 2009 - * Paul E. Jones - * All Rights Reserved - * - ***************************************************************************** - * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ - ***************************************************************************** - * - * Description: - * This file implements the Secure Hashing Standard as defined - * in FIPS PUB 180-1 published April 17, 1995. - * - * The Secure Hashing Standard, which uses the Secure Hashing - * Algorithm (SHA), produces a 160-bit message digest for a - * given data stream. In theory, it is highly improbable that - * two messages will produce the same message digest. Therefore, - * this algorithm can serve as a means of providing a "fingerprint" - * for a message. - * - * Portability Issues: - * SHA-1 is defined in terms of 32-bit "words". This code was - * written with the expectation that the processor has at least - * a 32-bit machine word size. If the machine word size is larger, - * the code should still function properly. One caveat to that - * is that the input functions taking characters and character - * arrays assume that only 8 bits of information are stored in each - * character. - * - * Caveats: - * SHA-1 is designed to work with messages less than 2^64 bits - * long. Although SHA-1 allows a message digest to be generated for - * messages of any number of bits less than 2^64, this - * implementation only works with messages with a length that is a - * multiple of the size of an 8-bit character. - * - */ - -/* - * Define the circular shift macro - */ -#define SHA1CircularShift(bits,word) \ - ((((word) << (bits)) & 0xFFFFFFFF) | \ - ((word) >> (32-(bits)))) - -/* Function prototypes */ -void SHA1ProcessMessageBlock(SHA1Context *); -void SHA1PadMessage(SHA1Context *); - -/* - * SHA1Reset - * - * Description: - * This function will initialize the SHA1Context in preparation - * for computing a new message digest. - * - * Parameters: - * context: [in/out] - * The context to reset. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1Reset(SHA1Context *context) -{ - context->Length_Low = 0; - context->Length_High = 0; - context->Message_Block_Index = 0; - - context->Message_Digest[0] = 0x67452301; - context->Message_Digest[1] = 0xEFCDAB89; - context->Message_Digest[2] = 0x98BADCFE; - context->Message_Digest[3] = 0x10325476; - context->Message_Digest[4] = 0xC3D2E1F0; - - context->Computed = 0; - context->Corrupted = 0; -} - -/* - * SHA1Result - * - * Description: - * This function will return the 160-bit message digest into the - * Message_Digest array within the SHA1Context provided - * - * Parameters: - * context: [in/out] - * The context to use to calculate the SHA-1 hash. - * - * Returns: - * 1 if successful, 0 if it failed. - * - * Comments: - * - */ -int SHA1Result(SHA1Context *context) -{ - - if (context->Corrupted) - { - return 0; - } - - if (!context->Computed) - { - SHA1PadMessage(context); - context->Computed = 1; - } - - return 1; -} - -/* - * SHA1Input - * - * Description: - * This function accepts an array of octets as the next portion of - * the message. - * - * Parameters: - * context: [in/out] - * The SHA-1 context to update - * message_array: [in] - * An array of characters representing the next portion of the - * message. - * length: [in] - * The length of the message in message_array - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1Input( SHA1Context *context, - const unsigned char *message_array, - unsigned length) -{ - if (!length) - { - return; - } - - if (context->Computed || context->Corrupted) - { - context->Corrupted = 1; - return; - } - - while(length-- && !context->Corrupted) - { - context->Message_Block[context->Message_Block_Index++] = - (*message_array & 0xFF); - - context->Length_Low += 8; - /* Force it to 32 bits */ - context->Length_Low &= 0xFFFFFFFF; - if (context->Length_Low == 0) - { - context->Length_High++; - /* Force it to 32 bits */ - context->Length_High &= 0xFFFFFFFF; - if (context->Length_High == 0) - { - /* Message is too long */ - context->Corrupted = 1; - } - } - - if (context->Message_Block_Index == 64) - { - SHA1ProcessMessageBlock(context); - } - - message_array++; - } -} - -/* - * SHA1ProcessMessageBlock - * - * Description: - * This function will process the next 512 bits of the message - * stored in the Message_Block array. - * - * Parameters: - * None. - * - * Returns: - * Nothing. - * - * Comments: - * Many of the variable names in the SHAContext, especially the - * single character names, were used because those were the names - * used in the publication. - * - * - */ -void SHA1ProcessMessageBlock(SHA1Context *context) -{ - const unsigned K[] = /* Constants defined in SHA-1 */ - { - 0x5A827999, - 0x6ED9EBA1, - 0x8F1BBCDC, - 0xCA62C1D6 - }; - int t; /* Loop counter */ - unsigned temp; /* Temporary word value */ - unsigned W[80]; /* Word sequence */ - unsigned A, B, C, D, E; /* Word buffers */ - - /* - * Initialize the first 16 words in the array W - */ - for(t = 0; t < 16; t++) - { - W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; - W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; - W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; - W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); - } - - for(t = 16; t < 80; t++) - { - W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); - } - - A = context->Message_Digest[0]; - B = context->Message_Digest[1]; - C = context->Message_Digest[2]; - D = context->Message_Digest[3]; - E = context->Message_Digest[4]; - - for(t = 0; t < 20; t++) - { - temp = SHA1CircularShift(5,A) + - ((B & C) | ((~B) & D)) + E + W[t] + K[0]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = SHA1CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 20; t < 40; t++) - { - temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = SHA1CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 40; t < 60; t++) - { - temp = SHA1CircularShift(5,A) + - ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = SHA1CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 60; t < 80; t++) - { - temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = SHA1CircularShift(30,B); - B = A; - A = temp; - } - - context->Message_Digest[0] = - (context->Message_Digest[0] + A) & 0xFFFFFFFF; - context->Message_Digest[1] = - (context->Message_Digest[1] + B) & 0xFFFFFFFF; - context->Message_Digest[2] = - (context->Message_Digest[2] + C) & 0xFFFFFFFF; - context->Message_Digest[3] = - (context->Message_Digest[3] + D) & 0xFFFFFFFF; - context->Message_Digest[4] = - (context->Message_Digest[4] + E) & 0xFFFFFFFF; - - context->Message_Block_Index = 0; -} - -/* - * SHA1PadMessage - * - * Description: - * According to the standard, the message must be padded to an even - * 512 bits. The first padding bit must be a '1'. The last 64 - * bits represent the length of the original message. All bits in - * between should be 0. This function will pad the message - * according to those rules by filling the Message_Block array - * accordingly. It will also call SHA1ProcessMessageBlock() - * appropriately. When it returns, it can be assumed that the - * message digest has been computed. - * - * Parameters: - * context: [in/out] - * The context to pad - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1PadMessage(SHA1Context *context) -{ - /* - * Check to see if the current message block is too small to hold - * the initial padding bits and length. If so, we will pad the - * block, process it, and then continue padding into a second - * block. - */ - if (context->Message_Block_Index > 55) - { - context->Message_Block[context->Message_Block_Index++] = 0x80; - while(context->Message_Block_Index < 64) - { - context->Message_Block[context->Message_Block_Index++] = 0; - } - - SHA1ProcessMessageBlock(context); - - while(context->Message_Block_Index < 56) - { - context->Message_Block[context->Message_Block_Index++] = 0; - } - } - else - { - context->Message_Block[context->Message_Block_Index++] = 0x80; - while(context->Message_Block_Index < 56) - { - context->Message_Block[context->Message_Block_Index++] = 0; - } - } - - /* - * Store the message length as the last 8 octets - */ - context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; - context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; - context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; - context->Message_Block[59] = (context->Length_High) & 0xFF; - context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; - context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; - context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; - context->Message_Block[63] = (context->Length_Low) & 0xFF; - - SHA1ProcessMessageBlock(context); -} - - - - - diff --git a/HanksRT.c b/HanksRT.c index d78c610..cfc6440 100644 --- a/HanksRT.c +++ b/HanksRT.c @@ -75,6 +75,7 @@ char ChatWelcomeMsg[1000]; char Position[81] = ""; char PopupText[260] = ""; int PopupMode = 0; +int chatPaclen = 236; char RtKnown[MAX_PATH] = "RTKnown.txt"; char RtUsr[MAX_PATH] = "STUsers.txt"; @@ -97,6 +98,7 @@ int ChatTmr = 0; BOOL NeedStatus = FALSE; + char Verstring[80]; static void node_dec(CHATNODE *node); @@ -597,7 +599,9 @@ VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffe Buffer = BufferB; #else - int left = 65536; + size_t left = 65536; + size_t clen = len; + UCHAR * BufferBP = BufferB; struct user_t * icu = conn->u.user; @@ -605,22 +609,22 @@ VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffe { if (icu->iconv_toUTF8 == NULL) { - icu->iconv_toUTF8 = iconv_open("UTF-8", icu->Codepage); + icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", icu->Codepage); if (icu->iconv_toUTF8 == (iconv_t)-1) - icu->iconv_toUTF8 = iconv_open("UTF-8", "CP1252"); + icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); } iconv(icu->iconv_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine - iconv(icu->iconv_toUTF8, &Buffer, &len, (char ** __restrict__)&BufferBP, &left); + iconv(icu->iconv_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); } else { if (link_toUTF8 == NULL) - link_toUTF8 = iconv_open("UTF-8", "CP1252"); + link_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); iconv(link_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine - iconv(link_toUTF8, &Buffer, &len, (char ** __restrict__)&BufferBP, &left); + iconv(link_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); } len = 65536 - left; Buffer = BufferB; @@ -1121,12 +1125,12 @@ void rduser(USER *user) // Open an iconv decriptor for each conversion if (user->Codepage[0]) - user->iconv_toUTF8 = iconv_open("UTF-8", user->Codepage); + user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", user->Codepage); else user->iconv_toUTF8 = (iconv_t)-1; if (user->iconv_toUTF8 == (iconv_t)-1) - user->iconv_toUTF8 = iconv_open("UTF-8", "CP1252"); + user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); if (user->Codepage[0]) @@ -1135,7 +1139,7 @@ void rduser(USER *user) user->iconv_fromUTF8 = (iconv_t)-1; if (user->iconv_fromUTF8 == (iconv_t)-1) - user->iconv_fromUTF8 = iconv_open("CP1252", "UTF-8"); + user->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); #endif } } @@ -1936,7 +1940,7 @@ void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) { UCHAR BufferB[4096]; - // Text is UTF-8 internally. If use doen't want UTF-8. convert to Node's locale + // Text is UTF-8 internally. If user doen't want UTF-8. convert to Node's locale if (circuit->u.user->rtflags & u_noUTF8) { @@ -1957,9 +1961,9 @@ void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) BufferB[blen + 2] = 0; #else - int left = 4096; + size_t left = 4096; UCHAR * BufferBP = BufferB; - int len = strlen(buf) + 1; + size_t len = strlen(buf) + 1; struct user_t * icu = circuit->u.user; if (icu->iconv_fromUTF8 == NULL) @@ -1967,7 +1971,7 @@ void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) icu->iconv_fromUTF8 = iconv_open(icu->Codepage, "UTF-8"); if (icu->iconv_fromUTF8 == (iconv_t)-1) - icu->iconv_fromUTF8 = iconv_open("CP1252", "UTF-8"); + icu->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); } iconv(icu->iconv_fromUTF8, NULL, NULL, NULL, NULL); // Reset State Machine @@ -3863,7 +3867,7 @@ int ChatConnected(int Stream) if (conn->rtcflags == p_linkini) { - conn->paclen = 236; + conn->paclen = chatPaclen; // Run first line of connect script @@ -3883,6 +3887,9 @@ int ChatConnected(int Stream) if (paclen == 0) paclen = 256; + if (paclen > chatPaclen) + paclen = chatPaclen; + conn->paclen = paclen; strlop(callsign, ' '); // Remove trailing spaces @@ -4163,12 +4170,19 @@ BOOL GetChatConfig(char * ConfigName) ChatApplNum = GetIntValue(group, "ApplNum"); MaxChatStreams = GetIntValue(group, "MaxStreams"); + chatPaclen = GetIntValue(group, "chatPaclen"); GetStringValue(group, "OtherChatNodes", OtherNodesList); GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); GetStringValue(group, "MapPosition", Position); GetStringValue(group, "MapPopup", PopupText); PopupMode = GetIntValue(group, "PopupMode"); + if (chatPaclen == 0) + chatPaclen = 236; + + if (chatPaclen < 60) + chatPaclen = 60; + return EXIT_SUCCESS; } @@ -4187,6 +4201,7 @@ VOID SaveChatConfigFile(char * ConfigName) SaveIntValue(group, "ApplNum", ChatApplNum); SaveIntValue(group, "MaxStreams", MaxChatStreams); + SaveIntValue(group, "chatPaclen", chatPaclen); SaveStringValue(group, "OtherChatNodes", OtherNodesList); SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); diff --git a/L2Code.c b/L2Code.c index 1b4d8f7..37a8aff 100644 --- a/L2Code.c +++ b/L2Code.c @@ -965,6 +965,11 @@ VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESS L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + // We need to save APPLMASK and ALIASPTR so following SABM connects to application + + LINK->APPLMASK = APPLMASK; + LINK->ALIASPTR = ALIASPTR; + PUT_ON_PORT_Q(PORT, Buffer); return; } @@ -1089,6 +1094,9 @@ VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * if (LINK->L2STATE == 1) // Sent XID? { + APPLMASK = LINK->APPLMASK; + ALIASPTR = LINK->ALIASPTR; + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM return; } @@ -1351,7 +1359,7 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe { Msg->PID = 0xf0; - memcpy(Msg->L2DATA, APPL->APPLCMD, 12); + memcpy(Msg->L2DATA, ALIASPTR, 12); Msg->L2DATA[12] = 13; Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR @@ -2412,6 +2420,10 @@ CheckPF: LINK->LAST_F_TIME = REALTIMETICKS; } + else + if (LINK->L2ACKREQ == 0) // Resptime is zero so send RR now + SEND_RR_RESP(LINK, 0); + } diff --git a/LinBPQ.c b/LinBPQ.c index ba6d20f..8cda726 100644 --- a/LinBPQ.c +++ b/LinBPQ.c @@ -28,6 +28,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses //#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" #else #include +#include #ifndef MACBPQ #ifndef FREEBSD #include @@ -75,6 +76,8 @@ void SaveAIS(); void initAIS(); void DRATSPoll(); +extern uint64_t timeLoadedMS; + BOOL IncludesMail = FALSE; BOOL IncludesChat = FALSE; @@ -174,8 +177,15 @@ int _MYTIMEZONE = 0; /* #define F_PWD 0x1000 */ -UCHAR BPQDirectory[260]; -UCHAR LogDirectory[260]; +extern UCHAR BPQDirectory[260]; +extern UCHAR LogDirectory[260]; +extern UCHAR ConfigDirectory[260]; + +// overrides from params +UCHAR LogDir[260] = ""; +UCHAR ConfigDir[260] = ""; +UCHAR DataDir[260] = ""; + BOOL GetConfig(char * ConfigName); VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); @@ -704,8 +714,26 @@ void ConTermPoll() return; } - +#include "getopt.h" + +static struct option long_options[] = +{ + {"logdir", required_argument, 0 , 'l'}, + {"configdir", required_argument, 0 , 'c'}, + {"datadir", required_argument, 0 , 'd'}, + {"help", no_argument, 0 , 'h'}, + { NULL , no_argument , NULL , no_argument } +}; + +char HelpScreen[] = + "Usage:\n" + "Optional Paramters\n" + "-l path or --logdir path Path for log files\n" + "-c path or --configdir path Path to Config file bpq32.cfg\n" + "-d path or --datadir path Path to Data Files\n" + "-v Show version and exit\n"; + int Redirected = 0; int main(int argc, char * argv[]) @@ -715,7 +743,6 @@ int main(int argc, char * argv[]) ConnectionInfo * conn; struct stat STAT; PEXTPORTDATA PORTVEC; - UCHAR LogDir[260]; #ifdef WIN32 @@ -755,13 +782,64 @@ int main(int argc, char * argv[]) if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) Redirected = 1; + timeLoadedMS = GetTickCount(); + #endif - printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); - printf("%s\n", VerCopyright); + printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); + printf("%s\n", VerCopyright); - if (argc > 1 && _stricmp(argv[1], "-v") == 0) - return 0; + + // look for optarg format parameters + + { + int val; + UCHAR * ptr1; + UCHAR * ptr2; + int c; + + while (1) + { + int option_index = 0; + + c = getopt_long(argc, argv, "l:c:d:hv", long_options, &option_index); + + // Check for end of operation or error + + if (c == -1) + break; + + // Handle options + switch (c) + { + case 'h': + + printf(HelpScreen); + exit (0); + + case 'l': + strcpy(LogDir, optarg); + printf("cc %s\n", LogDir); + break; + + case 'c': + strcpy(ConfigDir, optarg); + break; + + case 'd': + strcpy(DataDir, optarg); + break; + + + case '?': + /* getopt_long already printed an error message. */ + break; + + case 'v': + return 0; + } + } + } sprintf(RlineVer, "LinBPQ%d.%d.%d", Ver[0], Ver[1], Ver[2]); @@ -777,22 +855,41 @@ int main(int argc, char * argv[]) #ifdef WIN32 GetCurrentDirectory(256, BPQDirectory); - GetCurrentDirectory(256, LogDirectory); #else getcwd(BPQDirectory, 256); - getcwd(LogDirectory, 256); #endif - Consoleprintf("Current Directory is %s\n", BPQDirectory); - for (i = 1; i < argc; i++) + strcpy(ConfigDirectory, BPQDirectory); + strcpy(LogDirectory, BPQDirectory); + + Consoleprintf("Current Directory is %s", BPQDirectory); + + if (LogDir[0]) + { + strcpy(LogDirectory, LogDir); + } + if (DataDir[0]) + { + strcpy(BPQDirectory, DataDir); + Consoleprintf("Working Directory is %s", BPQDirectory); + } + if (ConfigDir[0]) + { + strcpy(ConfigDirectory, ConfigDir); + Consoleprintf("Config Directory is %s", ConfigDirectory); + } + + for (i = optind; i < argc; i++) { if (_memicmp(argv[i], "logdir=", 7) == 0) { strcpy(LogDirectory, &argv[i][7]); + Consoleprintf("Log Directory is %s\n", LogDirectory); break; } } + Consoleprintf("Log Directory is %s", LogDirectory); // Make sure logs directory exists @@ -801,7 +898,13 @@ int main(int argc, char * argv[]) #ifdef WIN32 CreateDirectory(LogDir, NULL); #else - mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + printf("Making Directory %s\n", LogDir); + i = mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + if (i == -1 && errno != EEXIST) + { + perror("Couldn't create log directory\n"); + return 0; + } chmod(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); #endif @@ -885,7 +988,7 @@ int main(int argc, char * argv[]) #endif - for (i = 1; i < argc; i++) + for (i = optind; i < argc; i++) { if (_stricmp(argv[i], "chat") == 0) IncludesChat = TRUE; @@ -936,7 +1039,7 @@ int main(int argc, char * argv[]) // Start Mail if requested by command line or config - for (i = 1; i < argc; i++) + for (i = optind; i < argc; i++) { if (_stricmp(argv[i], "mail") == 0) IncludesMail = TRUE; @@ -1166,7 +1269,7 @@ int main(int argc, char * argv[]) DoHouseKeeping(FALSE); } } - for (i = 1; i < argc; i++) + for (i = optind; i < argc; i++) { if (_stricmp(argv[i], "tidymail") == 0) DeleteRedundantMessages(); @@ -1799,6 +1902,8 @@ struct TNCINFO * TNC; #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 0 + + int clock_gettime(int clk_id, struct timespec *t){ mach_timebase_info_data_t timebase; mach_timebase_info(&timebase); @@ -1813,7 +1918,8 @@ int clock_gettime(int clk_id, struct timespec *t){ #endif #endif -int GetTickCount() + +uint64_t GetTickCount() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); diff --git a/MailNode.ncb b/MailNode.ncb new file mode 100644 index 0000000000000000000000000000000000000000..233befff45c945125bccba192eefeda254a7e9f2 GIT binary patch literal 13732864 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
ZK_9bi69*0crN~%yq_g%*;7)jt+?q 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 EVHGILzy;j@(-#w-Lxe+67oM zA0Hb*<8`6fmYG|7v9>dInUQviSZ6a$G&yWbyjEUkJJF4Glhm#e{{^$ z;`EYs$8*)e(SvXV+bzF0Lr!CHT@e}`&zLpNSH~4|oToms3_j&?A3T~zv_r4xj1Rrb z&p|ro$XqVR`KRH-^)Pd*DRx}O>SyYW&KA89*{IC*@io5P%9*6x2Tb-P!)*c_y9{<2+;SVrB8rXYPI$WFGVx zYym&XA!s|C<=8j z=k3!2+Jgtb3coH$+9bMJGsXS<@BNLcD9(O|C;#w)r9^RR?r5>thX121o}|`{8jLOY z|GU%hUpQ}JVzUY#V?I^f%LU#aczNO8Pvt4as|ff0 zC=V-ML-^g$J;R=?cpc&1Pi3Lv^@J}s%MsqBcq8F=iQVlQ#j#Al-<|lt#;165;oh%h zq~aI?@#pYzFyY=W=TpT;2)_ltqzOBU;$wv0j3>yTfmQq>;oe_lv*Htkd;gxN6~9dQjpjy# zs}!FsoX^EU)28@T;e6r@^0MO7h4Xnj=;#!`R`~TYOHWgr$Kwr4_~8A4ir*ysTEpW8 zD9-z*y@S`)Q*o9rBy{on>8$we!o8nRTgBNva7OVfYNGfO;oc9auHtOpd}j4h{9jq| zyM=o{r_zewE8P1<6;b>F;g>+_#ayrWL&C>HB7lsb_@l!4%pdFzT%3gVvqtzukZfQ< zQv7M*1R;wj<1VHJRXWLx~_+etDK?w^mJk;U=b ztIVgVSYWRNPYdbkmj=%eeDFMYR{A9U()ip}E&P9O7Ka6jyaqmy(L3P3uui4_8LT$; z5`;f--VB`nXOMgCc!c{Er~etA*RvNR{6ca1pTQ&3@^>3#J`MR47Cy)hLMZcQ;3o(l zXa^$Pqw*;se1IK*@J+?(rw^&#_D3l5a>$4N{(P@pUxYIM2A(dwkL`m{#-+e32=8rs zBb0F|@JhnZx920gTb;ME@N?`r2xYtq>8lF&&kx9W6?k>wrEDpLGA;#PQ+Nr>`;+k} z@Y=$Q+u{giJPEw6@DnV{Nyd%98wf9o->eSfL*OR~_x_GD4g}srcwzj?b?EPbHy2*W z7D6cfI`Edl3)+GRrT+%rS~xvkuwrI=4*XQ%`7PUp^vA&43(sfSj-=lOeui-WT!-|p zz&i`iBkgpiD*tZ6bKBeq$12`Kc*?Rrbyoab;og7!XU^{-|MP@zGMj)dV-Oz5;1OI=^{uMae8GRGZ#{Zuw&UP8UXW~7@`w92&nRrcc zwr})k(UWaaoc#fBg~bQ&08^a(hnkDd`2S(W+28zA_h`?G4;J1L&sjpcQ+$ZXRaJbb@G7FW|EUc!3*|6OcsZ=3q4QFFxbQ-vKcbIWm2T)C*iOdQ8XH;s zy?^-5G$TV?`=?z6b_2GBVIRM5#tp4k-#=3EW9?%;+aKZb1swr$wnz;+1RVQjytcoi%X zW8P84$A$PUAzU58?IEO2{9D91ct;$-v89KwUI<%Z3(vJ*zU!) z3frUDp1`&i+eU0myE%l6FGlv1MIFMX(DLRu)$2uj$cixmdcCP+TUf5IllXQUd_vnm z5~G~QW9~BpGks-;*2j-^i!te9LtbbGZoKIKLRK?l?(YW~eVIhS*9|*VeQ3 zZ3ElTHnJz-?a3$GCbp^VU{AMa*p9Z7?QFZ)u6RdzH``tOZ%5#$-jKTaW|{tw?FT}; zJy`4#L$M->pW7dW)zTQOu=q{^zMth1JQXt$E5^&P%Dfya)5%!PUWa?*yJlu#rA;rv zo3Wy&kIP)#CH;@)LqkEYv^#=d-ks36EQgQF-QqcSpLnT#3ZJSk;Fm;CIC{bTV1B}V zHb;q_Vq4-((5-B1lvi7Os%?jNtsnWW&bFiMXgkJ^wUpXfs$(ImQ-WdqDtPnshV;Wy zrOd}>RVyfY4D7Q1$2h1Q_ z1-Y^|UJz-Rv@B%chp>Dg&22filNL0E4CR+xh@Xji{Mh^o55p5|ZPZgc)CzSSJi}~L z6?aSx3cuZI8nNzhK7LzdY9lSRSj_t_^AF_5*qI`s^WLsNYZ-&J2sKVT;ttakc~g(Y zbW6=M;fT(%W@Czecu#kKONp9Sc`>f&dW1}!*L(rx8R_dLX}N}Bd=yqLw}uq?WQEOK zMw^dNYt%FH+a~iI^ox|@c(>iK?<4UDEn;h;Wwu2-FxOqKC#E=zi@Z-tUD+^-iOfA=t=yGw2kJLJ%D|Hy@36IJ%N3Jy@CCK zJ%W9Ly@LIMJ%c@-eV)CZ{hmFaeV@Ib{huR1cY8KIaetwffwgu4>fme91+n%gigt|r z22&YVP&u@y+ukdAAN9P;5RB)8=%pEcA6y?`qu#)rb=jjJ@q z$C;v*D@C^3%i-fY8f#^0>Uhpsratb1dQE#VMv3<~5_ugky)9SpEZqV2pZFhBGV~0!pqt<|{C8@- zThM=BK);XM2tCI!fub(^VYZ;R=upfO-m88bMh#kg`A)Q1m;-Lcs5KX3|9texJIqpy zQ}lGb52I!F7Q$A-l`L1eTmf@+%#|}&)m(9NHO`efSMglo|D6`j-!o$?d(9*)J=$67 zs|+<{Y*B+T;_NeTpf<|lTV2#&J()o{hjJF>Jj$7rb17$2&ZnGFn_{f&51q>Z=wk*# zS2M^`=fm@`^->SX_!#)$QuoW6zt&VlKkj3xE9aT#nCj@iv{3MhmK(0U*{au?4QA6{ zYS-~-NGlM}MxXLti1Tdm-1adH<;=f=O_uVbFzwX?ao@wx7X3_qso}aAod9d)J@{~x zW?Sc&6u0w2Qu@`Ujh`f?&{{^FE;5!-pTo0GGNmw@Tx6$(@7JM3JD_}MH{d@EHIv$UX}KWadE_st<)(o4T4;{UO}n3UP# zkJ2G^r>u>}qV=GBbOy#lzNdjRSzcQS{iFqE8S0K%I|DHee_{Hf{at`>95*>O^Fx`6 zV>CY>$cJx%pietL99=ov$E}XDK0g&PrdGnIGCo!CsftfEe5&J9Lq=PUx3wTQ)wXr; z`8$1=TFdjH(d363&pwdB`C*^-z1sI{-?InVD{)suMh zrI06EuAgu zA+K(Rwmqh~$uxirLrW;XEi_Faxy0|7JOLi{73?H=!Xk;iaW8qfy3djK$Y(Clo@d zD3?xwV~RLe5?gDg`e$TAIrEYmW`G97~~LoF_^0=ipEjXmR+niBS5)Dfl| zwB}Xlz3jQ{wd}E8HmZG;F|P!so>OpKslU03q-~ydd)oAA@7F8IRey8UI3K0X`zVR4 zpcROwp z2WG$8+Xb`;h4P-~Z(jxQpleo&Qf! z1M$@~?t=ekowE8a#pC4T64 z6h}A1A6Em$ELEJB1}|8w6rk5r{7m6oMPLk3oT^DYBa9Inze}a~dBVB+z}j2!e!}ZR zD}|X-@xj8mYQem#_%Pv=ij2u%y`*PgbaQs3@DuHc;Ab&?x___PXhf}1Jk;04!YN@v zGFSOe5Kid}b5O{?Eb^znjm5lP3%s?;|8j7rdB}f?@S0dzz_z9GpC+7=9$I_IzXI~V zRyd_VSnXB**Mldprpn|Y|C@wUa)dOb^1nqmrAp|LLjHK;i=8W+5+`N@mH#~OB&3&2 z9`dKYDGAL;Wu#A2`7aetDHcB9iZ2&l!HQj7@s+~;d;0dVF@^je6rOI=k;o@d;Q*0e7$i0UcZ+Ve^$7+%Rj65bHcq{{@PGJ^-(@A3io#T`14V8LtBW zS-7|FpQG|YXTg1W`~E|r99p3qXs3^ix9@ji`cTd}z;kFG@~2-)4wK8$pXF4QKm8SQ zm^AT=X`wjFJqOkh%x_aTZ#$fqej_=If4^h{bzb_7q-h?`OFxh_$TCTU)sfzXahUxr z4f;!qu)I1i$AvU#&JN?2N-ECrC=EI_mQx|cIS!^lANmt`ZpAtNra=owKcns8`a9$L zn+f;tXLL*-%B=->PUtg!1QH&upMDxSq0u`C{xj#1z}pG;?`M?xCGgXQQ#XS56RGs{ z$H)o&#D4G(!}Z|pP*|;y`}Z?`Ec28fZ_fhHrFqDo{usGLHw?cWl|TI}a$z;`Ir874 zIQ=DZ$!Z(+e8mR}_wPwuqd5H*a$yz8ezr>S3x)glC$3O@jBx+{#KnqVB>YYDCi1yW z@d?8Hdlhd~e3EegUd0)TPZs{Nc^UcK(*gg5_H(sx|2*@jXJqji!k>no3_3lQ|ETbL%soIwRsN5I z=K;;+A^)d@dq3*$Sw4Yp5bph@e^vZh;odJ=%0HxkPPq4r-mTKVD14b&hOFOK{1xF# z%~EiwuaM7n;oe_b>MQU!g?oSLM^!$%gnNJK4T|p(?)|0tuC4gKJ`wKyrHh7gyA0*_ zg>dgLy^QHYz3cI*Rj{~1zohc730?p+ zlZX6I6h75V<@JPoZa_W_gikS3!1?a6xEvaR7X;1ZA)ltgCyO8T$?7^=2>1TdBSZeP zk$)@U-d{SW%D)YGq2Na?JmlYAxc8$js`5WWxc8&}k>gBA-$l6hqyAFyvxR#<>OG2| zC*1o{?^L{>aPLR`qT+*vdq3(8iVqj={iq*Pe3WqSM}4p27YX-%)XNl~Al&;=-=_HG z!o45$EXAh?_kPsV6rU#C`%%j{9Lncf;ogsWyh=Y)xc6rssrYQ+-cNX-;AUi@q2~y*LsYZ657?hXjcyk z_kM#_nLhO2hrkQN#=#+5cqoU*grANl?T}RE|D^Etc&ZXU0U`efk^eg3r{RtM=&vgO z4dD1Ksw^Jz-z@x8an1aN^K9VTgtw93^~j;l`?B!x`&}yicHu4X{2}BY#ore0{jon) z{5|0(<4HrjOHA>Pgr5W*72YtV_-Des|Kw|m?-TBS_iT&e`-RuXvz-5jz4rk3s<__$ zXV3m}=)FllG^rw0R8T3RSW!{21UwW$u%luFMG)+WCL(r)*h|#dHTDwhvBa(s!H&IS z^#6X=%;pRy7?XQ{_dd^k{+=(x%zpRmHGNH6vqt*U68u}k`{=Y~99P8j{%Ck_ouJhE zbK>7?S*(e43v4L-iOT-8EH`|=4EfE~mGi+e6sR|Gdu7PUqVF-5Z&@|N*{j#eZbGNb z@O!oLrZv6|Mb-Cy1-Pyn3k9Swe+$n>+XF(V2jVf3XR{cG- z#x7HYwUOLAB+vWHy;gX?@V;tz$l{$Tzg6+7@gQ#hsbBoPP=zMxNW=IgKb?AMEV-BV zjb(~3H;}t3etgH#lX5n|G^|Zwo!Ne0QSWt?QdY z5AA;w)-Z+n--g9EgjK|sXX;9~y(`OoUG)n8ZsdK7`hT#V=gBON z?+)H_bpM^h;Ngzl~_-?(>Rm= ziS;ZwHc#RW;21xA@d_ffrjPKGu~-thgq3AS49 zG(X5=+9df*o9sS*?XoWNe%v~6>P%WM(t7ki5f9Syf2&-{C-h8Tq;&pCCe5QOgb&#+ z@Yl!mj(Cx7cy00B3gJKf!guW^XzyNz?{L#*s@93B`AA*Gfq`p&XJ)#ULl*j1fxEJQy-s{UE~$@O9Dr~_)NZ5O&ehrb&CrReXEN6!nI-T<~b(x{2OxCqo{Ll?^W~@&$k3rzCiR!DD|Z zzfu}S-mWXGsu*Q|i^8gk6R$62B=~7@ryLr}orXiaz=V)Bt{u!1`z4MY3iji{duQmLSA$Ee(!W~^-Rrk}0d5C(OF^>Mzw#IzpoQ9Y!H z5)5@X)ZdL_t>q@dOR4ciL zDKF=0^>~5W z!yQs1xW=tHH&Wj;*Dnx8>d~oB$+5;Z%IYY!lzK`{<@^yRkT`?HDJ0GzA-~L7Bu*n8 zWo>AIYA&#vCs9s)fvuXd}Xzg z(-_oqT4HtWqt>wYSV*c@%BeRt3C}{llbv>U;oC_vz9rS__^9|%2IhPFa=-nyDhC6dqUKJiB#qHK!xI4R@g2JN}8(t4;`&=G1i^lbCh zMhcnr)+*s3uh~oTLA_OP`%9i_W64Zi9b56Cw#QFy|KyM|!!;p8<9}zlg7$j$z}aVK zZ~l9e7zn3<;J@LF2htAon)I7U-~@^8Aj?g-y)$;CD6J4fD9$@N;8n(wdQ?CnM3i=4qijsY2m`-~TbQC#5mp^u}i8>lv~w}$JN+qko< z#+<#>1JL&RM)~kfjnljtQ$N)`qce`XXFqKmcrlV$NPi&-hm;(Wc=sx2XvM|IbaUlT zCq3`@3**uv6=CH0m8-FaBUT+@5kb;z;cdeL^^c+bdV zN_;=xb1%B}K@bTRs-#E?{B#)E_lW6Po!VI0O- zoH9(u;LM!pEy(pyYC|-lP0vq{PWlISG|7=2OtutPpA6|@TobbvZ9{o63u4C8Ld>4> zTc8$PT{VUMR($T_kif&$BtzC1*Z8b<%WfU+x7>ai5@Lkw2L)++CW1D|&@_wd zbazLUc7|L6Z;oYEMKTVi>f^=QSdRj1iG@Y=#JvDi}+zjXEO zG&1tp>^ky&Ym@)vd&Vv;GH#=)sVDjaO8PrVFNXdf`5xj=jYp=6udj1R9rAU^dG4H| zu8q`}-%CysnK<6ARIMLvC%&OTFM!k<6#nkm=LBt}-A!cBK%Y0MB+yGWdqDydv~E@& z^JJv^M|90Fd0O7K%_?G^Y?xgdT^nWF#PQ|@cCQlsbZGU=>I3Hu?Vna#Z~%BsMi=HEdVF!Y|0;Xn)_>wZF1e=dAG($&E#i88Q;0MXbYE zA892*TVV2$xW2NTa9n$3?8W2i>$X(>b(HHr;o`r}!;u{!Wd~}U^l&W0S54x8&;~nX zLU>QSkqaURgRbdSdQ$U_bMS_CDjrEqzWc)NZQ5Th=0;(56nY`XXy~vG>q47ZYYllr zAIp4+kPcYW&xWyoSi?C{ImZePvrlH8iUh zvA#xyNQja@FWZ?6+FQuCy)w>3@MhzTo1mZ#zY8k@7ub3LypLXB%_@L;HtHC0K&}Lt z6XaUfjtjA&cMS0VlA4 zNn!#C65Px`kY*-stL@wXDC=~{hTukjL&wNDhP#gC;jCO}hl68TQNt!%X%W_wYib5F z@LD$JTB=djx;Vm8#e=e;MpJ`_siso%s9%g(83%B_b)s4g=?=AdX^e3*J5eb{D_tBN zq#|2n^{6U&43G0usQ<_)wGBM_#aff*)phu0P2`Ow>d95Ig^O zjNv}_!kgef=EF=mbC=*TJ|`Nk!K3`wOz59t_z(UEuXVridWilP82+jMRQO9V+;+-` za|~bQR|$Ut|1sS2kNO-BPV))g5PBLO21Ngok@BqAEGq{ zpWSBo3co`5B}uq<2(RW6JcfI(;qUl&TQ07X8(Z1{8jIpG^6_*;hG?r#@f z9`j*C<-@y%-{x-<-WLBc|KFFh%q4ithtCYZ#osFb%@g`x8UAPgXW=yy{5!*M_BRXv zxk3+-&OZ&m$=@XWs|5EM;oj(P6#ij?a~`eC{mCOIy*$CuXItiO@HYs5CBf07Tjs9! z$XhQ<@WzH;=dTm~P=cd3xXfMSuMyra=FcGIPaDIp_E!tP8~-tXIA2-aC3s9%XTz`Z zSIPga34PA7R(DtWD}@h>^tTZGzJ_1nuMoa8q0jl>8ZN;j{lSJ`?k|`BixT>rSF7Rv z=>I5u=SY9J=x<~AQomIAz6t&9gx7Ql9_f!Ze2HHo|Jx<>_b~iIf1&Wc2|mv7Q~W8y zyCgUxyqfN0j~sui1fOd7N&Y0^4HNtz!{__?!mB6vbi?QQdBT5&ImUP%VfcyuMB(2g z_%Vjh^>c-Pl;E=spX28Ue<#7`8GeF4LHIKX&bhIgZnmE-{ILY*+*nOF%g+-2K!Trb z_)I@j_?-#9$nazRvBLkH;G8F`>5lQo2){PLFEadSf3)z+6Z}%ckMc(ezc9g<8a~6% z5WZW2bDpfGJIr%_qaxOaJyjn#Pgc`S_tS+>#eb~Ne-d6R#bZ8mZmgC&)E^3+g#I0d zAL0)YzHNeYZmgC&*dHu>Sb{%f_(7g?Cdj=M<+#l71O0)*`y}|&h9BS$5Z)!hpErDe zzrXPE1b@l!X`b^fEfV~7!>9VG!l%c4n52Ao+wlGTe!?5zKjyd9$o>ldG5*!{T-POdq+i$Y@qWDQ#}oRT53B3;@_Wg?Il)^RzNg<) z_N574ZulO457}oXcxS`M_%X7NO7QN6@9uY(JvPC68NQq6ywj)z?_>C`eplHe5_}`W zNBhyTH%stM4d2D@B71`b=Ui7^x3k|_c6oviF?=V#lk64=zNO)#{3zL9GdPR+wyoj) zeSg`P#{8eD{D*E`*U$Hp{T}{f{u94?DIV$UZFpbbSN59;{R+eTc+!1qq(58qsZaG> zZ{J(?%?W+#YyA|D^l5MP-G+Wc*=Hp5;S2R$FHe3WFIS`sK2_iK^gU&tm*DWh`mVdD z+>T9f_-}pJ&3BW%e}bQCcvs(5_G2;rI{A^EX?PdkMfL>z$NV`<&kbCH$M~OX`1*c* z*?TAS&o{i2?<9M0f?sNQd*5Dm{{+9>@N!=+yL*CPYj``~PIkuxztQlvzOC#Q3C{SY zfotR2$ZnM2cN*T>x0YR&;P)8b%D0mJ&F?Dn`98y2_!hGNmf#N<-o(@X9!c=W3~%Hc z$$ly3=OX3j(}p+n4Q1bs|Cpc8>bapy@R*-38{WV-kbOf!pK)hHSI^gz{c)s!zUVWq zZRqNH_{6+~{z^SJO7Tda@pmIv$Jdd4Y(oDF!)yE6vUgALZw;^IYsuaw!GAQormrb` z^90wVL-cF-8p8V~c**eU9zMsJhoU}nUaFBR^JT)@B{=i0My{H#CcH(0H!!^9OTrr` zcvHhQ4HEuS%%3ZiKg=f^Ij=wU5BQJy-&%O%6p#7S!SKvy(BB*XQJ;CUlV+Y;a_B5$p5_w zzNO)xXP*ndEy1@o{Il#c;nyel_J)6&eJXrOg70GZCmH?W`3XM8@KxC=;R_Rdtl=ML zoUcAL!6z90p`FLB%cQq_h}K?xHpTD{GWy|T@gMVls_-T*!DD_NZ20@x`|`hGLjQ2X z-^<<;-a691OZ1O6{N3zb;oBtij}_k3C3vKNg5fK&74pAHLjOd=muJg`Te(O7Cma5@ zjXO9yRg~9hhQF1)CI2fEe1YL_WN!$6D#6b({Pm1+&0`6EuHi3duL{2}!7n!ah3xOb zZ%^>c4Sy!%y#3(`e!byOWSr06E5UCv{ITpY;kzaHErvguasGdY1i!=ZN3utRk4*5p z4SzU$Son|xzt8XoGxQ4#O7I5_e;|86_^LfC^X(DCmu8HA-%s$r8h%N3iSV}*{0YM^ z&Mp>yYl1&x`1#rS!vB=ue>42t>|EhjC-@77FUlC#U!34C8GcT7j_{t;D=nBkuX^>W z;iqNHCl17atha9nZ{`C2{vMU-`iJ2sWGBe~vkCr=;j^>Z!tY7&cMU&2J6`xT3I2iM z$7RO}KOwx+eHfhNIV7 z?Ya^#{cQM9(+Ac+!P!)9rahh^^4~VWGsDrhEq;*T-f(nrYuzZpbHjsvte?iLDc@4b z@Sq>-+XSy>c+ih^dL^D!H$3QDIyAv+8jb{w)^!uSj^RP?(QXM|-|(RKXh?!LG`yi? zC$zeo;7tql+^K3Eac@zuWTXpIr^` z=Gv)UD6dn659y7xh;P`%@Nj?Nwh5iChKGCnW+!yO*OQbL>!4>vIzNg|H^YM-jx7>8 z-3K&d;LL!|++!!`FN{q0`gwpnu}=gbsL1Np>6~|F1{OiJm_Osy{q2Ea_nt*xZml^O1uVm z8=V=asG3GPHAJV6;k!zjPvi3h?`wFt4{B8&#N57-+t(m7pI$Idt(RUO+fqpjPLkwS*u^!Vc!G{`te|Ep{XDj=!vSEf_l(AlQ zT!Ifb{14e5gbz>f5r&_hoi6-b_(lwOE5loxezD^dd~3sl-ni`&e5B!xr1w;^feF5i z;b@H4`b>gvYj{n`xJYwrC4VT}&hV!tJ)`ksf^TnlxED81@Er^f_u_t8*_;kTO}tTPgPPs49=?4O*J;J-6G-19au!N(dN^db2K zA7^;bZ`3WpD+~|!UOrvf?#jj+j!cNww-bDV;aln6aOGQqPc%H}DOs4{lMLsCjP_X) zd|$(Zo{yC@`dAMq8y@ae_F8%I%<4@4g|t^ne#uM1&g&eNmri2uhRN(9Gf`%W%>FXR z${a6qlFT_W=gC|ybF0jqGSA8UAoEWdr)gm>QzlbWrj|@ynR;1Y@utBtoE_zC;`Vay zBXgk4Q8MVVS|odk%ndU4$#5R;@3J}H$TzFx{#J%>s)=EPasIBrT1Kpw>`i6Bw@a`I zavvfyQ|6Qe$8U-gmn($bD}(zP+3(5x6rWq~7t+Bw#P#JKB(tZ?VKQgRTrI=-zgF7B z+(2eCnH^>DKTh_MGAGGgDszL(BQh_`d?n+jsehCq+`=#Z@WcCs@!d1B-2&Dj!YY@=n^4({I=3_9~r(MA$w05=n+QM zch~4$8fNLh5LQ)xEreg$Q{^k0{3AZ-Q6S8!b%3x=lfR2(?vQy#=6#u;Wqv7-CmbZ& zGUWd}*_6RT*_X=PDnnV^FPrcRzxcxs@4Frx-t8g#AelKbe~=+A1$LL7Uj$F`xOj(u z&P|sN(Vl?JHZuFjOp}?ZbkB{?=g9uG_k?+k{M{onTkRli6KR_wEzii$Ycik65SO22 zH#juJdl%VN-%Syg#{XtLQ(vn3dsujr>9NgeJ`=Bvg>%lA^R(N`4K^#m&Wzao z3HD;dif_xbk)M9oDLxn94AV31zG@pMpWdy|*jn#t&j-m)(*`dfZN+yB5;|4+LRDG? zZ}{`Sp_$TnJE2{bxBRcrZgO}fe=2Bq7B)bJdWOCO>Jj=ewiX6|wDat8SH%|!tKMtf zeyifCUpph6d|T)nqHez$!~94#=_@eWHF2qbL}>3rWfy*TlpDVVR`{JN{63|Lv=m_$ z&xA?cNZ%FDygR1Kcg1t@-Pxjzn{cm@`yQE>WImMvYdb^rTJ;yKuiR<<;T>^Dj}Gsr z>KPt}8=AC-b>)9moIEYkQ?;zB;`lAnfM4?Ca)p~>FU5F#BOARmD2(5ZgZ+H~m0 z)A)4Jd(PQ!FB_d6wB5brK0xN!<=@2H2L$cC4t@9G@0c7EZ< z^fm}b*UwS1p|MbQRoWj4TNmBN$NoCa-QWd_Zcgr(o77~fM@;6p`SVf;_}z>2!^o@k_LTUDj5RGY1_ zoy>+Z8_QgzGU0is%+B$d{&J$+RqIbxoH{{&xh}s|@zigT{=!c&x1rB0<}pVqe9~56 z@FVK-V&UWieP-eJvFNwJ3cqhfzwq25&sxSj11s{Z+6imt8Sm2e)j{ux%a*ctl{rLa zUc~4Vp#yeY2LpjL)>wb=B9HrK9}h(Gk%vaP8lTkXc^k-6xqzb#%&;e zFZXi!p?#etcfm(5lKUB%0&h82^-yLTnYl7&%g~3clKqoR`dxz)*M3*^+k<(p@Tk{C znHA4PolW0SUwP&oyn=dBd{;d4j&jGm8R>r@`*Rt-|6VrpW9A4t&g`1V6n&80b1b@D zgyDxY7w$g7wvjnVhB8Z14^Rj8)N}EEs@z5R)K&7LpmRj@hrLkd z7MXiw8l4or$L=HpzJ=^jG8Hn1%gmEGOJ<48RWg*>ow6U7c~ge6$E=c@xMNn!UF&4+ zhsv~(A@93K8}~1ne)6-k%pNlP$s8kdsto@Bxo_ot%Zr39mN{sI+KtTlau zDeiQ>KXJb)H}qD@?yve*#DzIyK@W_5`NvA(#LpAiGuh^TsNcFpIF-JcTds3KS7vwn z$Aq*~r9?NwWbJ|Nr+u?&Yi?4Vu3h0(+Bxqg>6$|%aX44fnpaBV^gUl!^6h;j6W>pA z^aFGDb-k#(oQ?FX-Qw<`6as`+e<+vyaZ+JMsozG8_#hKY}bx z7s(Ixlk5_5P{>XpZ-p!ta$3l6A>W1U*96I_?I)Q)`vy2!&Kc~H9KQ%n0Q{O72!>k1-MuE+WDQksvXPCz1Mg7yZk-+ z-Rs#gCnd`>WF>JO>(NXk&+dxss_Yu=4?m(k<;ODe`gQGszonh?<&sADToMdlO6uWj zNlKI?)lnu%kQ$O6X(EZ7Hj?6LC&{1op3)hmH1A?*CcUI}FG&aPFNwkfC53pfO5i9- zSso+F%$btrJW&#+^CgvfvLsnAv~sylvcoq>-uPz8G`}Lb>^CIK{kG)VYgkzg$bFx1 z=1N(keD10ooUA;Y>RJ6H^%XkvH%FzXN+Y?c?vk|X<&gyB-6Z)&QxWbulF`|}EckmN zdpR57NBWPNg}nVldr&`{4x^Ux`L0U(LltnY60T=uOt`%zJ4P8#kc?mvhv_QCD^z-` zJhEDZ($x>lk%z@SM>X#&l^3OVMRr|wXNCj-Z=O)SK3pR&+_fabgJdD@@uGfs{2H0k zuPQ>R-RQrX5L_+uH<|`l+idJeWERCs?8h4NO>X_1Ul;qaUqhMi=-AnX{ut>?9-D-V zq#!#ewYI1u8(N9>wK841z8GRbvm%@$;fx9AQtIaQbgJihHB!>k&yS8R`Bcd)jE-rm znX@Os`@0>x?$n&2k}V&d-4mbo)4k%yXGrMbtG%BmdenT{C^ZS)Lu%l92C1etORKFZ z`LQ~d7f7nXwTe<>FP)mvOE+X8kA@qc2aOM*zoK?HApf>qc(ckk&e=`JSI?|}_N{#3 z{@gds+3m*NPtA;Ti7awbCO>X>rijMLM@yajS@#^ zSBw}v=qAN7-BYc7l44K&Kq3%nK{V{ZGa4!9F7zMx5B*1KjX(JMd4s&6TH_9_IiI0^ zJbN?yC_CQ2?*HMRY9B(mT}>9*BSLv3Ls|;g>)BIl@};lmY#M04eE0H@r}S&I`46=f z6WV-(eE0r=VkgPd?;hiQrpYn#Z7u)znCDGBx_IPSr(fT4E2TMTwaAB_>p}Hg|5**8 zcPe_OU#cy)aFR!-_t4-&eTBEAyyQOBOIqXZ*1w^@hkSp@m*q$q@a8z3j=9Mr6ULht z)W2*|5%S_3UqdLRK+K=>o8%`e$9OJy5qft>`CrX6)Af4gkB>K0EYX7jaM{7^-R z_cgw6K73-J)F;0#e$ig1pZjIkDJ<#+yl$n3v(?FaP6*$X@@jdZf#+apPgv}~@yNLl zqk0*VOXSKF-7z~L*6HclQ5h0q_&7hiwIcYq()Y>P?+5LXqkm^#xlYZV)$bMk0+)Z^ z#$lQ9xG*vaT#h;pr-Osh3gLEejWAB@C8=$GNPG7Y_uz+Qd4G*!hH5l0Qe&Bd15}RT zDo1f)92Z7%jONDc6u}hnv%t}2>&(lA8rNN}@!T!y4=UT1Q!h}oFJzs_iOn>!Lv9QA zWZhYgj2me_Cu=)CptKt#-YKKN>CkdRt+_=#j8kv&O6(g?@UK*audny7s63$4B447S zf;wjbJH%RfOZJy+iQLoL5;y|F<>bL*f+oCmKbgKZZE;RL=GFr*#Q$ z;2+56@CxWQV(Z{W6UoIAw>-5pSLoC*UMJcUg0b^?N2wNNDF~$e$A0a*YG-Xd6<= zSk7zQSd6^U&Vrwb*@pSXvDLlg(bNL^soy9^Hw$j&7OZGO`#_IuA90?nS;njk2@w43 zAi0kdD%RcF(A&ZnZKa{DSN@?!v3x#JwqG5b*86ULwXVI6{>mA+2?rSh4w zzFjRXS35(t2VbKdQW3Zwa+fQ|3E}c=BY&qy>WeP|kMvn*#gTH}$wue9uHk^3cH;Y^ zeCLV5)g@n`8828jUoX}a<_5@*5a-rekGy$OJDQIVzW?mcjjp$Sg@VFwH}%WNlR@q8 zt|Ian#yF>CoNEF<#4oEJdZG6+S}HA+Q9CV@ntqrs^s~^mkS2sOcU>=A!!M8>28ACW z=MLSPepy9uUFVlg2=5!IPkXK zkjKKkv)U%It+-DU&qhucN=vf?Rn~B3W}QV(cVI<$yTpIfI=D{DzELe~Ab+7x4t)GQ zjQ~Q=95{MijRR(=ju8r)m}bO!^`?7AJ@87CX64;9@o!FG5Q7$;vw^tgdi0bKmk%|> z@2XMHWIxsKqu!HmkmE+zNIkhZ6T^8K&N?!FhELJ{`0Y~>X8af0*x?6_I=j{l6yD1Y z^IcV*S}KtA=+JDW+R&J+Yk7G4fw(SGI>h8o*EP1;A@a3Lc9rs-_VAQsvHvdl?JfK; zzm?y{bGiZlWga;{+S4)aI*lQb0s_Cw&dcFCz$0;wYgNV>d0ee>q;qgBcJIWT`ayF^ zLD@vLAKo<0kT4_tVAq|FX6rn!gkXFSd-8M0Q25U6X zGe0NRtaBuDjpR9g9?dxGNX!pWtK6+3(7iCuahTEkw0?MBPdzF+=V%}Fzx2DDg5#te zr}<{XC2W?6n*{L1#zZ=n0=mcssmr& zX8+7M(E?h-qtgIa?;L3=TsOMvdW+B1@`+3z?!zU?SG^+mnCFpb!fzkfAclRgMk<`6 z!0*ReO(2u?FKq{;|?gtaE&? z#PJ>D7j#t*lN%kRXdc?dbkUAee^ZS6iqQ`%QLI9F8|lD5o7pN<%0k5r0Rv8aq##&pY+BpBdcgXjlZm4UQx63&zPY%86zn>6XE%RZqCLiQE zS-mkq^@ zkWa(iUs7ppRI0cqYhH)k9Te|yNJxX$l3e0N6~X;Sg?7W!g^R{p2~jD<3*+3Q=qX5=?ptBb7igi#-3XI2(RDSd>x ztLLOMuGxN%X2I3gv?+kS=Py&N;Wy>d2--huA7hp0?PDEYKR;D(Y032!lhDg=nIpvw zfA~Z_J=(Q{_S)6XXUJoc#u}2sxEolM);o12z^%QF<#$w zmBzx`W6pNax&Hwf=W&SfGucqjnLa|K&FJi(DDiY`wsAfpml8F58k^}HVJ-!3_SI^F zja=a%jnqGo|3_plH;rP~+Gvz=s3qBc#*F1z(EHd)njg{aRjDO-uue75XK~AV=(qMX zJ;Cdm>%oQ3)oGark~OYk#Lf!s3XO5!SB@7oGo7n`t9qIzbEBFz_pf8~p+;K4ZKhL_ zt=3w{?IiBeRcB7a>gk3yJL<2w&`xn}cUN1_E=CsPRkoAVuTN3QR$VzeOf&j)y|-u~ zYp?q*v3cp0;*HlxLqu5rWzFVx|B|h!y<_WWpQ^XW^D^mHfiJOw7RG2*`@at&f7r?G z=01qEq?zZ`J*5uso2q(G-5Hoqk9F)4H$C!(_cG4w;hF_=tA%qwG-+d?5F}|yv{c%D-8)zNo z3`K@kkJK)j){mA>GGvDFd6TOfdFrO>vpIoG%vlXXsvCEUtb2|I2YjFCf3Aq%`JWX6 zSj!w8!_a4^g53$`vLlsN-kzWRHPYWtYfhXf1ARE-j3cfuvXL<_`{w8$?j~%SR?Nei zdEfwRubWq{ocA{#aJ71!;)kA;@MHBfte>t_U-UPPQwi;_c9s`c6aTl05bLzQ%xxQb zDR|3mVm?S9wZZGy z>#1y8#dx3oHea*%!LCnN^#pJ_`dQioysp^yC~AH&8wh(&x#}mS#8_nR_z=g}{Kv}1 z4nnWhis^s4s`X1NSqH?Lve+FEOT140wdPxd%DC+7Sf+1eoWCcJ`ujqA@N{X%y~O|1 zzp1(eU+gSCwL|u1tPxYQBeOT-*RN;elvmK?j3+1Uq14BZQ<^}RY0f@Q;p3)99~W!Q zMtPUWOAi$_G*0l{{=Q2^aGmVY@Y_UK=yw_A)D?Fs)>;@vR<5xyj=WI|w}irIsdt;u zVt?DNsq?&8-$CS&Z+%4s5X}v^aFX_74pQm=k zTxFW0wopGf9nHIv=Nvq)HvYWm>h95%05-~_T?5xt&-oEj_*b18S-yt9aoV3Uq8<*+ zmsbSeH+XdXb`bt6uIf5;u?EeF5aQ4%+fMX}!+Dymp+Aa{+38s2 zchwVb*zMUY*1cKoFNz)GcE-qzkkKUprCofP;s;mV$ot1&sOCxO$X}RA$+c� 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

FJ94{^InjPA@C21^rOM2@L!Ft+;>(0g0x{;QV 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%0DXhaQ9-D~iMKo7J9XH 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