From 39da8ffc5254acec7b9ce51f50b5b0e783dc7b7d Mon Sep 17 00:00:00 2001 From: John Wiseman Date: Thu, 2 Mar 2023 09:33:47 +0000 Subject: [PATCH] 6.0.23.51 --- APRSCode.c | 52 ++- ARDOP.c | 4 +- BBSUtilities.c | 631 ++++++++++++++++++---------- BPQMail.c | 4 +- BPQMail.vcproj.HPLAPTOP.johnw.user | 65 +++ Bpq32.c | 4 + HFCommon.c | 8 +- HTTPcode.c | 4 +- L4Code.c | 13 +- MailNode.vcproj.HPLAPTOP.johnw.user | 65 +++ TelnetV6.c | 26 +- UZ7HODrv.c | 13 + Versions.h | 6 +- bpqmail.h | 6 +- debug/bpq32.pdb | Bin 1584128 -> 1584128 bytes 15 files changed, 644 insertions(+), 257 deletions(-) create mode 100644 BPQMail.vcproj.HPLAPTOP.johnw.user create mode 100644 MailNode.vcproj.HPLAPTOP.johnw.user diff --git a/APRSCode.c b/APRSCode.c index a54371b..74a17ba 100644 --- a/APRSCode.c +++ b/APRSCode.c @@ -4431,7 +4431,7 @@ static VOID GPSDConnect(void * unused) // Request data - send(TCPSock, "?WATCH={\"enable\":true,\"nmea\":true}", 34, 0); + send(TCPSock, "?WATCH={\"enable\":true,\"nmea\":true}\r\n", 36, 0); } else { @@ -4459,7 +4459,7 @@ static VOID GPSDConnect(void * unused) timeout.tv_usec = 0; // We should get messages more frequently that this ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); - + if (ret == SOCKET_ERROR) { goto Lost; @@ -4472,7 +4472,11 @@ static VOID GPSDConnect(void * unused) { char Buffer[65536]; int len = recv(TCPSock, Buffer, 65500, 0); - + char TCPMsg[8192]; + + char * ptr; + char * Lastptr; + if (len == 0) { closesocket(TCPSock); @@ -4484,15 +4488,45 @@ static VOID GPSDConnect(void * unused) { Buffer[len] = 0; - if (Buffer[0] == '$' && memcmp(&Buffer[3], "RMC", 3) == 0) - if (Check0183CheckSum(Buffer, len)) - DecodeRMC(Buffer, len); + ptr = Lastptr = Buffer; + Buffer[len] = 0; + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr -1; + + if (Len > 8100) + return; + + memcpy(TCPMsg, Lastptr, Len); + TCPMsg[Len++] = 13; + TCPMsg[Len++] = 10; + TCPMsg[Len] = 0; + + if (!Check0183CheckSum(TCPMsg, Len)) + { + Debugprintf("Checksum Error %s", TCPMsg); + } + else + { + if (memcmp(&TCPMsg[3], "RMC", 3) == 0) + DecodeRMC(TCPMsg, Len); + } + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } + } } - } - if (FD_ISSET(TCPSock, &errorfs)) - { + if (FD_ISSET(TCPSock, &errorfs)) + { Lost: #ifdef LINBPQ printf("GPSD Connection lost\n"); diff --git a/ARDOP.c b/ARDOP.c index 36c344a..f7d7c9e 100644 --- a/ARDOP.c +++ b/ARDOP.c @@ -955,7 +955,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) Buffer = &buffptr->DEST[0]; // Raw Frame Buffer[datalen] = 0; - *ptr++ = '^'; // delimit fram ewith ^ + *ptr++ = '^'; // delimit frame with ^ // Frame has ax.25 format header. Convert to Text @@ -992,7 +992,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) memcpy(ptr, Buffer, datalen); ptr += datalen; - *ptr++ = '^'; // delimit fram ewith ^ + *ptr++ = '^'; // delimit frame with ^ ARDOPSendData(TNC, FECMsg, (int)(ptr - FECMsg)); TNC->FECPending = 1; diff --git a/BBSUtilities.c b/BBSUtilities.c index c161cb6..2cbbafc 100644 --- a/BBSUtilities.c +++ b/BBSUtilities.c @@ -121,6 +121,7 @@ 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); config_t cfg; @@ -2108,7 +2109,7 @@ BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type } else { - if (_stricmp(BID, Calls[0])) + if (_stricmp(BID, Calls[0]) == 0) return TRUE; } @@ -2201,7 +2202,7 @@ BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) } else { - if (_stricmp(BID, Calls[0])) + if (_stricmp(BID, Calls[0]) == 0) return TRUE; } @@ -6627,7 +6628,7 @@ BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) Forwardit: - if (Msg->Defered) // = response received + if (Msg->Defered > 0) // = response received { Msg->Defered--; Debugprintf("Message %d deferred", Msg->number); @@ -8129,10 +8130,27 @@ InBand: // 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")) + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) { + // If connected to SYNC, save IP address and port + char * Cmd; + if (strcmp(Buffer, "*** CONNECTED TO SYNC ") != 0) + { + 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; @@ -8484,6 +8502,10 @@ 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; } @@ -9091,7 +9113,6 @@ VOID * _zalloc_dbg(size_t len, int type, char * file, int line) return ptr; } - struct MsgInfo * FindMessageByNumber(int msgno) { int m=NumberofMessages; @@ -9115,6 +9136,25 @@ struct MsgInfo * FindMessageByNumber(int msgno) 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]; @@ -10357,7 +10397,7 @@ int Connected(int Stream) else n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); - // Send SID and Prompt + // Send SID and Prompt (Unless Sync) if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks @@ -10387,12 +10427,12 @@ int Connected(int Stream) 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; @@ -10421,31 +10461,44 @@ int Connected(int Stream) if (conn->RadioOnlyMode) nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); - - nodeprintf(conn, BBSSID, "BPQ-", - Ver[0], Ver[1], Ver[2], Ver[3], - BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", - BLOCKED ? "FW": "", WL2KRO ? "" : "J"); -// if (user->flags & F_Temp_B2_BBS) -// nodeprintf(conn,";PQ: 66427529\r"); + if (!(conn->BBSFlags & SYNCMODE)) + { - // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + nodeprintf(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", + BLOCKED ? "FW": "", WL2KRO ? "" : "J"); + + // if (user->flags & F_Temp_B2_BBS) + // nodeprintf(conn,";PQ: 66427529\r"); + + // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + } } if ((user->Name[0] == 0) & AllowAnon) strcpy(user->Name, user->Call); - if (user->Name[0] == 0) + if (!(conn->BBSFlags & SYNCMODE)) { - conn->Flags |= GETTINGUSER; - BBSputs(conn, NewUserPrompt); + if (user->Name[0] == 0) + { + conn->Flags |= GETTINGUSER; + BBSputs(conn, NewUserPrompt); + } + else + SendWelcomeMsg(Stream, conn, user); } else - SendWelcomeMsg(Stream, conn, user); + { + // Seems to be a timing problem - see if this fixes it + + Sleep(500); + } RefreshMainWindow(); - + return 0; } } @@ -11379,7 +11432,10 @@ VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) conn->BBSFlags |= SYNCMODE; conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + Sleep(500); + BBSputs(conn, "OK\r"); + Flush(conn); return; } @@ -13556,11 +13612,72 @@ BOOL ProcessReqDir(struct MsgInfo * Msg) 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 @@ -13695,7 +13812,7 @@ void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); - sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", + 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); @@ -13713,55 +13830,22 @@ void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer return; } - if (memcmp(Buffer, "TR RMS_Location_", 16) == 0) + if (memcmp(Buffer, "TR ", 2) == 0) { - // I think this is an xml message giving location of station + // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len - BIDRec * BID; - char *ptr1, *ptr2, *context; + char * Command; + char * BIDptr; - // TR RMS_Location_OH6IJ3_YIBB50HCCQUS 200 367 0 True - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - ptr1 = strtok_s(&Buffer[16], " ", &context); // MID - - // What to do with call bit?? - - ptr1 = strlop(ptr1, '_'); - - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncCompressedLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncXMLLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncMsgLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - - BID = LookupBID(ptr1); - - if (BID) - { - BBSputs(conn, "Rejected - Duplicate BID\r"); - return; - } - conn->TempMsg = zalloc(sizeof(struct MsgInfo)); - - BBSputs(conn, "OK\r"); - return; - - } - - - if (memcmp(Buffer, "TR AddMessage_", 14) == 0) - { BIDRec * BID; char *ptr1, *ptr2, *context; // TR AddMessage_1145_G8BPQ 727 1202 440 True WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - ptr1 = strtok_s(&Buffer[14], " ", &context); // MID + + 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); @@ -13770,89 +13854,35 @@ void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer conn->SyncMsgLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); - BID = LookupBID(ptr1); + // If addmessage need to check bid doesn't exist - if (BID) + if (strcmp(Command, "AddMessage") == 0) { - BBSputs(conn, "Rejected - Duplicate BID\r"); - return; + 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, "TR RequestSync_", 15) == 0) - { - char *ptr1, *ptr2, *context; - - // TR RequestSync_G8BPQ_14 224 417 0 True - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - ptr1 = strtok_s(&Buffer[15], " ", &context); // MID - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncCompressedLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncXMLLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncMsgLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - - conn->TempMsg = zalloc(sizeof(struct MsgInfo)); - - BBSputs(conn, "OK\r"); - return; - } - - if (memcmp(Buffer, "TR Delivered_", 13) == 0) - { - char *ptr1, *ptr2, *context; - - // TR RequestSync_G8BPQ_14 224 417 0 True - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - ptr1 = strtok_s(&Buffer[13], " ", &context); // MID - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncCompressedLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncXMLLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncMsgLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - - conn->TempMsg = zalloc(sizeof(struct MsgInfo)); - - BBSputs(conn, "OK\r"); - return; - } - - if (memcmp(Buffer, "TR Remove_", 10) == 0) - { - char *ptr1, *ptr2, *context; - - // TR RequestSync_G8BPQ_14 224 417 0 True - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - ptr1 = strtok_s(&Buffer[10], " ", &context); // MID - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncCompressedLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncXMLLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncMsgLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - - conn->TempMsg = zalloc(sizeof(struct MsgInfo)); - - BBSputs(conn, "OK\r"); - return; - } - - if (strcmp(Buffer, "BYE\r") == 0) + if (memcmp(Buffer, "BYE\r", 4) == 0) { WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->CloseAfterFlush = 20; // 2 Secs conn->BBSFlags &= ~SYNCMODE; return; } @@ -13866,41 +13896,14 @@ void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer return; } - // Data + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); - if ((conn->TempMsg->length + len) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); - - conn->TempMsg->length += len; - - if (conn->TempMsg->length >= conn->SyncCompressedLen) - { - // Complete - decompress it - - conn->BBSFlags |= FBBCompressed; - Decode(conn, 1); - - BBSputs(conn, "OK\r"); - return; - } - return; + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; } - - - BOOL ProcessReqFile(struct MsgInfo * Msg) { char FN[128]; @@ -14059,6 +14062,232 @@ VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) 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 @@ -14089,8 +14318,7 @@ int ReformatSyncMessage(CIRCUIT * conn) // 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 @@ -14117,17 +14345,19 @@ int ReformatSyncMessage(CIRCUIT * conn) // WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); - // display the xml for testing + // display the message for testing + conn->MailBuffer[conn->TempMsg->length] = 0; + +// OutputDebugString(conn->MailBuffer); memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); xml[conn->SyncXMLLen] = 0; - Debugprintf(xml); - if (conn->SyncMsgLen == 0) { // No message, Just xml. Looks like a status report + ProcessSyncXML(conn, xml); return 0; } @@ -14251,8 +14481,6 @@ Loop: else Input = ptr + 2; - // Part Starts with header (content-type, etc), but skip for now - // Will check for quoted printable p1 = Msgptr; @@ -14341,15 +14569,13 @@ Loop2: } Msgptr = ptr = Input; i++; - } + continue; } // See if more parts - - } - else - ptr++; + ptr++; } + ptr++; } @@ -14549,7 +14775,6 @@ char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) // 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); @@ -14567,52 +14792,6 @@ char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) free(Encoded); free(MailBuffer); - - - -/* -Date: Sat, 04 Feb 2023 11:19:00 +0000 -From: G8BPQ -Subject: Sync Test 5 -To: GM8BPQ -Message-ID: E4P6YIYGQ347 -X-Source: G8BPQ -X-Location: 52.979167N, 1.125000W (GRID SQUARE) -X-RMS-Originator: G8BPQ -X-RMS-Path: G8BPQ@2023-02-04-11:19:29 -X-Relay: G8BPQ -MIME-Version: 1.0 -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="boundaryHjgswg==" - ---boundaryHjgswg== -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Message 5 with attachments - ---boundaryHjgswg== -Content-Disposition: attachment; name="new1.html"; - filename="new1.html" -Content-Type: attachment; name="new1.html" -Content-Transfer-Encoding: base64 - -PCFET0NUWVBFIGh0bWw+DQo8aHRtbD4NCg0KDQogIDxoZWFkPg0KICAgIDx0aXRsZT4NCiAgICAg -IENvbnNwaXJhY3kgVGhlb3JpZXMNCiAgICA8L3RpdGxlPg0KICA8L2hlYWQ+DQogIDxib2R5Pg0K -ICAgIDxkaXYgc3R5bGU9J3RleHQtYWxpZ246IGNlbnRlcjsnPjxkaXYgc3R5bGU9J2Rpc3BsYXk6 -IGlubGluZS1ibG9jazsnPjxzcGFuIHN0eWxlPSdkaXNwbGF5OmJsb2NrOyB0ZXh0LWFsaWduOiBs -ZWZ0Oyc+DQoJICBhYWFhYWE8YnI+DQogICAgICBiYjxicj4NCiAgICAgIGNjY2NjY2NjYyBjY2Nj -Y2NjY2NjY2NjPGJyPg0KCSAgPC9zcGFuPg0KICAgICAgPC9kaXY+DQogICAgPC9kaXY+DQogIDwv -Ym9keT4NCjwvaHRtbD4= ---boundaryHjgswg==-- - - -*/ - - - - - return Buffer; } diff --git a/BPQMail.c b/BPQMail.c index 402e25c..344dc5f 100644 --- a/BPQMail.c +++ b/BPQMail.c @@ -1105,7 +1105,9 @@ // 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 callsihn (33) -// Improvements to RMS Relay SYNC mode ( +// Improvements to RMS Relay SYNC mode (47) +// Fix BID Hold and Reject filters + #include "bpqmail.h" diff --git a/BPQMail.vcproj.HPLAPTOP.johnw.user b/BPQMail.vcproj.HPLAPTOP.johnw.user new file mode 100644 index 0000000..f361c86 --- /dev/null +++ b/BPQMail.vcproj.HPLAPTOP.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/Bpq32.c b/Bpq32.c index 8a3c3b9..0308f62 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1144,6 +1144,10 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Add PHG to APRS beacons (45) // Dont send DM to stations in exclude list(45) // Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) + #define CKernel diff --git a/HFCommon.c b/HFCommon.c index efe1e9e..21b7376 100644 --- a/HFCommon.c +++ b/HFCommon.c @@ -61,7 +61,7 @@ int RestartTNC(struct TNCINFO * TNC); char * GetChallengeResponse(char * Call, char * ChallengeString); VOID __cdecl Debugprintf(const char * format, ...); -VOID FromLOC(char * Locator, double * pLat, double * pLon); +int FromLOC(char * Locator, double * pLat, double * pLon); BOOL ToLOC(double Lat, double Lon , char * Locator); int GetPosnFromAPRS(char * Call, double * Lat, double * Lon); @@ -780,8 +780,10 @@ IdTag (random alphanumeric, 12 chars) if (LOC[0] && ADIF->LOC[0]) { - FromLOC(LOC, &myLat, &myLon); - FromLOC(ADIF->LOC, &Lat, &Lon); + if (FromLOC(LOC, &myLat, &myLon) == 0) // Basic checks on LOCs + return TRUE; + if (FromLOC(ADIF->LOC, &Lat, &Lon) == 0) + return TRUE; Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE); intBearing = (int)Bearing(Lat, Lon, myLat, myLon); diff --git a/HTTPcode.c b/HTTPcode.c index 488b366..eaf894b 100644 --- a/HTTPcode.c +++ b/HTTPcode.c @@ -2623,7 +2623,7 @@ doHeader: 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); + 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); @@ -4359,7 +4359,7 @@ void ProcessWebmailWebSockThread(void * conn) { if (Sent > 0) // something sent { - InputLen -= Sent; + ReplyLen -= Sent; memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); } diff --git a/L4Code.c b/L4Code.c index 21db8a7..20430dd 100644 --- a/L4Code.c +++ b/L4Code.c @@ -1295,11 +1295,20 @@ VOID CONNECTREQUEST(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, UINT Appl char BPQPARAMS[10]; // Extended Connect Params from BPQ Node int CONERROR; int Index; + char xxx[16] = ""; memcpy(BPQPARAMS, &L4T1, 2); // SET DEFAULT T1 IN CASE NOT FROM ANOTHER BPQ NODE - BPQPARAMS[2] = 0; // 'SPY' NOT SET - + BPQPARAMS[2] = 0; // 'SPY' NOT SET + + ConvFromAX25(&L3MSG->L4DATA[1], xxx); + + if (CheckExcludeList(&L3MSG->L4DATA[1]) == 0) + { + SendConNAK(LINK, L3MSG); + return; + } + if (FINDCIRCUIT(L3MSG, &L4, &Index)) { // SESSION EXISTS - ASSUME RETRY AND SEND ACK diff --git a/MailNode.vcproj.HPLAPTOP.johnw.user b/MailNode.vcproj.HPLAPTOP.johnw.user new file mode 100644 index 0000000..f361c86 --- /dev/null +++ b/MailNode.vcproj.HPLAPTOP.johnw.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/TelnetV6.c b/TelnetV6.c index 67c4f68..61ed0fd 100644 --- a/TelnetV6.c +++ b/TelnetV6.c @@ -2577,6 +2577,7 @@ nosocks: if (P2[0]) { + sockptr->CMSSession = FALSE; STREAM->Connecting = TRUE; STREAM->ConnectionInfo->SyncMode = TRUE; TCPConnect(TNC, TCP, STREAM, P2, atoi(P3), TRUE); @@ -3205,6 +3206,7 @@ int Socket_Accept(struct TNCINFO * TNC, SOCKET SocketId, int Port) TNC->Streams[n].FramesQueued = 0; sockptr->HTTPMode = FALSE; + sockptr->SyncMode = FALSE; sockptr->DRATSMode = FALSE; sockptr->FBBMode = FALSE; sockptr->RelayMode = FALSE; @@ -4273,6 +4275,9 @@ int DataSocket_ReadSync(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, S strcpy(sockptr->Callsign, call); + call --; + *(call) = ' '; + sockptr->UserPointer = &SyncUser; SendtoNode(TNC, sockptr->Number, TCP->SyncAPPL, (int)strlen(TCP->SyncAPPL)); @@ -5469,7 +5474,8 @@ int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCK sockptr->FBBMode = TRUE; sockptr->RelayMode = FALSE; sockptr->ClientSession = FALSE; - + sockptr->SyncMode = FALSE; + if (TCP->CMS) SaveCMSHostInfo(TNC->Port, TNC->TCPInfo, sockptr->CMSIndex); @@ -5500,7 +5506,10 @@ int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCK if (sockptr->SyncMode) { - buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to SYNC\r"); + char Addr[256]; + Tel_Format_Addr(sockptr, Addr); + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to SYNC %s:%d\r", Addr, htons(sockptr->sin.sin_port)); send(sockptr->socket, sockptr->Signon, (int)strlen(sockptr->Signon), 0); } else @@ -6195,12 +6204,12 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S // Resolve Name if needed - destaddr.sin_family = AF_INET; - destaddr.sin_port = htons(Port); + sockptr->sin.sin_family = AF_INET; + sockptr->sin.sin_port = htons(Port); - destaddr.sin_addr.s_addr = inet_addr(Host); + sockptr->sin.sin_addr.s_addr = inet_addr(Host); - if (destaddr.sin_addr.s_addr == INADDR_NONE) + if (sockptr->sin.sin_addr.s_addr == INADDR_NONE) { struct hostent * HostEnt; @@ -6219,7 +6228,7 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S struct in_addr addr; addr.s_addr = *(u_long *) HostEnt->h_addr_list[i++]; } - memcpy(&destaddr.sin_addr.s_addr, HostEnt->h_addr, 4); + memcpy(&sockptr->sin.sin_addr.s_addr, HostEnt->h_addr, 4); } ioctl (sockptr->socket, FIONBIO, ¶m); @@ -6245,7 +6254,7 @@ int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * S } - if (connect(sockptr->socket,(struct sockaddr *) &destaddr, sizeof(destaddr)) == 0) + if (connect(sockptr->socket,(struct sockaddr *) &sockptr->sin, sizeof(destaddr)) == 0) { // // Connected successful @@ -6941,6 +6950,7 @@ int DoRefreshWebMailIndex(); int RefreshWebMailIndex() { DoRefreshWebMailIndex(); + return 0; } #else diff --git a/UZ7HODrv.c b/UZ7HODrv.c index d17e409..0f8e6b9 100644 --- a/UZ7HODrv.c +++ b/UZ7HODrv.c @@ -266,6 +266,19 @@ void RegisterAPPLCalls(struct TNCINFO * TNC, BOOL Unregister) memset(AGW->TXHeader.callfrom, 0, 10); strcpy(AGW->TXHeader.callfrom, Appl); send(TNC->TCPSock,(const char FAR *)&AGW->TXHeader,AGWHDDRLEN,0); + + memcpy(Appl, APPL->APPLALIAS_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (Appl[0]) + { + memset(AGW->TXHeader.callfrom, 0, 10); + strcpy(AGW->TXHeader.callfrom, Appl); + send(TNC->TCPSock,(const char FAR *)&AGW->TXHeader,AGWHDDRLEN,0); + } } } } diff --git a/Versions.h b/Versions.h index 6a4d343..42c9d9f 100644 --- a/Versions.h +++ b/Versions.h @@ -10,14 +10,14 @@ #endif -#define KVers 6,0,23,46 -#define KVerstring "6.0.23.46\0" +#define KVers 6,0,23,51 +#define KVerstring "6.0.23.51\0" #ifdef CKernel #define Vers KVers #define Verstring KVerstring -#define Datestring "January 2023" +#define Datestring "March 2023" #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerCopyright "Copyright © 2001-2023 John Wiseman G8BPQ\0" #define VerDesc "BPQ32 Switch\0" diff --git a/bpqmail.h b/bpqmail.h index d868bf8..ef2e78d 100644 --- a/bpqmail.h +++ b/bpqmail.h @@ -274,6 +274,8 @@ typedef struct ConnectionInfo_S int SyncCompressedLen; int SyncXMLLen; int SyncMsgLen; + char * SyncHost; // Saved so can send "request sync" + int SyncPort; UCHAR * SyncMessage; // Compressed SYNC message to send // These are used to detect CRLF split over a packet boundary @@ -296,7 +298,9 @@ typedef struct ConnectionInfo_S #define SENDBODY 128 #define WAITPROMPT 256 // Waiting for prompt after message #define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response -#define SENDINGSYNCMSG 1024 // Sent messagr to SYNC, waiting response +#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response +#define REQUESTINGSYNC 2048 +#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message // BBSFlags Equates diff --git a/debug/bpq32.pdb b/debug/bpq32.pdb index 0af0ae0da6fa3ad67b81f1dfe40c60ec81fefae1..5d42a82820d6aa6862411d961ed35a59413ee456 100644 GIT binary patch delta 4409 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 delta 4585 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 DX-_^`