2013 lines
46 KiB
C
2013 lines
46 KiB
C
/*
|
|
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
|
|
//
|
|
// FBB Forwarding Routines
|
|
|
|
#include "bpqmail.h"
|
|
|
|
int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress);
|
|
void MQTTMessageEvent(void* message);
|
|
|
|
int MaxRXSize = 99999;
|
|
int MaxTXSize = 99999;
|
|
|
|
struct FBBRestartData ** RestartData = NULL;
|
|
int RestartCount = 0;
|
|
|
|
struct B2RestartData ** B2RestartRecs = NULL;
|
|
int B2RestartCount = 0;
|
|
|
|
extern char ProperBaseDir[];
|
|
|
|
VOID FBBputs(CIRCUIT * conn, char * buf)
|
|
{
|
|
// Sends to user and logs
|
|
|
|
int len = (int)strlen(buf);
|
|
|
|
WriteLogLine(conn, '>', buf, len -1, LOG_BBS);
|
|
|
|
QueueMsg(conn, buf, len);
|
|
|
|
if (conn->BBSFlags & NEEDLF)
|
|
QueueMsg(conn, "\n", 1);
|
|
}
|
|
|
|
|
|
VOID ProcessFBBLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len)
|
|
{
|
|
struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block
|
|
int i;
|
|
int Index = 0; // Message Type Index for Stats
|
|
char * ptr;
|
|
char * Context;
|
|
char seps[] = " \r";
|
|
int RestartPtr;
|
|
char * Respptr;
|
|
BOOL AllRejected = TRUE;
|
|
char * MPS;
|
|
char * ROChar;
|
|
|
|
if (conn->Flags & GETTINGMESSAGE)
|
|
{
|
|
ProcessMsgLine(conn, user, Buffer, len);
|
|
if (conn->Flags & GETTINGMESSAGE)
|
|
|
|
// Still going
|
|
return;
|
|
|
|
SetupNextFBBMessage(conn);
|
|
return;
|
|
}
|
|
|
|
if (conn->Flags & GETTINGTITLE)
|
|
{
|
|
ProcessMsgTitle(conn, user, Buffer, len);
|
|
return;
|
|
}
|
|
|
|
// Should be FA FB F> FS FF FQ
|
|
|
|
if (Buffer[0] == ';') // winlink comment or BPQ Type Select
|
|
{
|
|
if (memcmp(Buffer, "; MSGTYPES", 7) == 0)
|
|
{
|
|
char * ptr;
|
|
|
|
conn->SendB = conn->SendP = conn->SendT = FALSE;
|
|
|
|
ptr = strchr(&Buffer[10], 'B');
|
|
|
|
if (ptr)
|
|
{
|
|
conn->SendB = TRUE;
|
|
conn->MaxBLen = atoi(++ptr);
|
|
if (conn->MaxBLen == 0) conn->MaxBLen = 99999999;
|
|
}
|
|
|
|
ptr = strchr(&Buffer[10], 'T');
|
|
|
|
if (ptr)
|
|
{
|
|
conn->SendT = TRUE;
|
|
conn->MaxTLen = atoi(++ptr);
|
|
if (conn->MaxTLen == 0) conn->MaxTLen = 99999999;
|
|
}
|
|
ptr = strchr(&Buffer[10], 'P');
|
|
|
|
if (ptr)
|
|
{
|
|
conn->SendP = TRUE;
|
|
conn->MaxPLen = atoi(++ptr);
|
|
if (conn->MaxPLen == 0) conn->MaxPLen = 99999999;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Other ; Line - Ignore
|
|
|
|
return;
|
|
}
|
|
|
|
if (Buffer[0] != 'F')
|
|
{
|
|
if (strstr(Buffer, "*** Profanity detected") || strstr(Buffer, "*** Unknown message sender"))
|
|
{
|
|
// Winlink Check - hold message
|
|
|
|
if (conn->FBBMsgsSent)
|
|
HoldSentMessages(conn, user);
|
|
}
|
|
|
|
if (conn->BBSFlags & DISCONNECTING)
|
|
return; // Ignore if disconnect aleady started
|
|
|
|
BBSputs(conn, "*** Protocol Error - Line should start with 'F'\r");
|
|
Flush(conn);
|
|
Sleep(500);
|
|
conn->BBSFlags |= DISCONNECTING;
|
|
Disconnect(conn->BPQStream);
|
|
|
|
return;
|
|
}
|
|
|
|
switch (Buffer[1])
|
|
{
|
|
case 'F':
|
|
|
|
// Request Reverse
|
|
|
|
if (conn->FBBMsgsSent)
|
|
FlagSentMessages(conn, user);
|
|
|
|
if (!FBBDoForward(conn)) // Send proposal if anthing to forward
|
|
{
|
|
FBBputs(conn, "FQ\r");
|
|
|
|
conn->BBSFlags |= DISCONNECTING;
|
|
|
|
// LinFBB needs a Disconnect Here
|
|
|
|
if (conn->BPQBBS)
|
|
return; // BPQ will close when it sees FQ. Close collisions aren't good!
|
|
|
|
if ((conn->SessType & Sess_PACTOR) == 0)
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
else
|
|
conn->CloseAfterFlush = 20; // PACTOR/WINMOR drivers support deferred disc so 5 secs should be enough
|
|
}
|
|
return;
|
|
|
|
case 'S':
|
|
|
|
// Proposal response
|
|
|
|
Respptr=&Buffer[2];
|
|
|
|
for (i=0; i < conn->FBBIndex; i++)
|
|
{
|
|
FBBHeader = &conn->FBBHeaders[i];
|
|
|
|
if (FBBHeader->MsgType == 'P')
|
|
Index = PMSG;
|
|
else if (FBBHeader->MsgType == 'B')
|
|
Index = BMSG;
|
|
else if (FBBHeader->MsgType == 'T')
|
|
Index = TMSG;
|
|
|
|
Respptr++;
|
|
|
|
if (*Respptr == 'E')
|
|
{
|
|
// Rejected
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "Proposal %d Rejected by far end", i + 1);
|
|
}
|
|
|
|
if ((*Respptr == '-') || (*Respptr == 'N') || (*Respptr == 'R') || (*Respptr == 'E')) // Not wanted
|
|
{
|
|
user->Total.MsgsRejectedOut[Index]++;
|
|
|
|
// Zap the entry
|
|
|
|
if (conn->Paclink || conn->RMSExpress || conn->PAT) // Not using Bit Masks
|
|
{
|
|
// Kill Messages sent to paclink/RMS Express unless BBS FWD bit set
|
|
|
|
// What if WLE retrieves P message that is queued to differnet BBS?
|
|
// if we dont kill it will be offered again
|
|
|
|
if (FBBHeader->FwdMsg->type == 'P' || (check_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber) == 0))
|
|
FlagAsKilled(FBBHeader->FwdMsg, FALSE);
|
|
}
|
|
|
|
clear_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber);
|
|
set_fwd_bit(FBBHeader->FwdMsg->forw, user->BBSNumber);
|
|
|
|
FBBHeader->FwdMsg->Locked = 0; // Unlock
|
|
|
|
// Shouldn't we set P messages as Forwarded
|
|
// (or will check above have killed it if it is P with other FWD bits set)
|
|
// Maybe better to be safe !!
|
|
|
|
if (FBBHeader->FwdMsg->type == 'P' || memcmp(FBBHeader->FwdMsg->fbbs, zeros, NBMASK) == 0)
|
|
{
|
|
FBBHeader->FwdMsg->status = 'F'; // Mark as forwarded
|
|
FBBHeader->FwdMsg->datechanged=time(NULL);
|
|
}
|
|
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine));
|
|
|
|
conn->UserPointer->ForwardingInfo->MsgCount--;
|
|
|
|
SaveMessageDatabase();
|
|
continue;
|
|
}
|
|
|
|
// FBB uses H for HOLD, but I've never seen it. RMS Express sends H for Defer.
|
|
|
|
|
|
if (*Respptr == '=' || *Respptr == 'L' || (*Respptr == 'H' && conn->RMSExpress)) // Defer
|
|
{
|
|
// Remove entry from forwarding block
|
|
|
|
FBBHeader->FwdMsg->Defered = 4; // Don't retry for the next few forward cycles
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine));
|
|
continue;
|
|
}
|
|
|
|
conn->RestartFrom = 0; // Assume Restart from
|
|
|
|
if ((*Respptr == '!') || (*Respptr == 'A'))
|
|
{
|
|
// Restart
|
|
|
|
char Num[10];
|
|
char *numptr=&Num[0];
|
|
|
|
Respptr++;
|
|
|
|
while (isdigit(*Respptr))
|
|
{
|
|
*(numptr++) = *(Respptr++);
|
|
}
|
|
*numptr = 0;
|
|
|
|
conn->RestartFrom = atoi(Num);
|
|
|
|
*(--Respptr) = '+'; // So can drop through
|
|
}
|
|
|
|
// FBB uses H for HOLD, but I've never seen it. RMS Express sends H for Defer. RMS use trapped above
|
|
|
|
if ((*Respptr == '+') || (*Respptr == 'Y') || (*Respptr == 'H'))
|
|
{
|
|
struct tm * tm;
|
|
time_t now;
|
|
char * MsgBytes;
|
|
|
|
conn->FBBMsgsSent = TRUE; // Messages to flag as complete when next command received
|
|
AllRejected = FALSE;
|
|
|
|
if (conn->BBSFlags & FBBForwarding)
|
|
{
|
|
if (conn->BBSFlags & FBBB2Mode)
|
|
SendCompressedB2(conn, FBBHeader);
|
|
else
|
|
SendCompressed(conn, FBBHeader->FwdMsg);
|
|
}
|
|
else
|
|
{
|
|
nodeprintf(conn, "%s\r\n", FBBHeader->FwdMsg->title);
|
|
|
|
MsgBytes = ReadMessageFile(FBBHeader->FwdMsg->number);
|
|
|
|
if (MsgBytes == 0)
|
|
{
|
|
MsgBytes = _strdup("Message file not found\r\n");
|
|
FBBHeader->FwdMsg->length = (int)strlen(MsgBytes);
|
|
}
|
|
|
|
now = time(NULL);
|
|
|
|
tm = gmtime(&now);
|
|
|
|
nodeprintf(conn, "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,
|
|
FBBHeader->FwdMsg->number, BBSName, HRoute, RlineVer);
|
|
|
|
if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message - put blank line after header
|
|
BBSputs(conn, "\r\n");
|
|
|
|
QueueMsg(conn, MsgBytes, FBBHeader->FwdMsg->length);
|
|
free(MsgBytes);
|
|
|
|
user->Total.MsgsSent[Index]++;
|
|
user->Total.BytesForwardedOut[Index] += FBBHeader->FwdMsg->length;
|
|
|
|
nodeprintf(conn, "%c\r\n", 26);
|
|
}
|
|
continue;
|
|
}
|
|
BBSputs(conn, "*** Protocol Error - Invalid Proposal Response'\r");
|
|
}
|
|
|
|
conn->FBBIndex = 0; // ready for next block;
|
|
conn->FBBChecksum = 0;
|
|
|
|
|
|
if (AllRejected && (conn->RMSExpress || conn->PAT))
|
|
{
|
|
// RMS Express and PAT don't send FF or proposal after rejecting all messages
|
|
|
|
FBBputs(conn, "FF\r");
|
|
}
|
|
|
|
return;
|
|
|
|
case 'Q':
|
|
|
|
if (conn->FBBMsgsSent)
|
|
FlagSentMessages(conn, user);
|
|
|
|
conn->BBSFlags |= DISCONNECTING;
|
|
|
|
Disconnect(conn->BPQStream);
|
|
return;
|
|
|
|
case 'A': // Proposal
|
|
case 'B': // Proposal
|
|
|
|
if (conn->FBBMsgsSent)
|
|
FlagSentMessages(conn, user); // Mark previously sent messages
|
|
|
|
if (conn->DoReverse == FALSE) // Dont accept messages
|
|
return;
|
|
|
|
// Accumulate checksum
|
|
|
|
for (i=0; i< len; i++)
|
|
{
|
|
conn->FBBChecksum+=Buffer[i];
|
|
}
|
|
|
|
// Parse Header
|
|
|
|
// Find free line
|
|
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
FBBHeader = &conn->FBBHeaders[i];
|
|
|
|
if (FBBHeader->Format == 0)
|
|
break;
|
|
}
|
|
|
|
if (i == 5)
|
|
{
|
|
BBSputs(conn, "*** Protocol Error - Too Many Proposals\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
}
|
|
|
|
//FA P GM8BPQ G8BPQ G8BPQ 2209_GM8BPQ 8
|
|
|
|
FBBHeader->Format = Buffer[1];
|
|
|
|
ptr = strtok_s(&Buffer[3], seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam;
|
|
|
|
if (strlen(ptr) != 1) goto badparam;
|
|
|
|
FBBHeader->MsgType = *ptr;
|
|
|
|
if (FBBHeader->MsgType == 'P')
|
|
Index = PMSG;
|
|
else if (FBBHeader->MsgType == 'B')
|
|
Index = BMSG;
|
|
else if (FBBHeader->MsgType == 'T')
|
|
Index = TMSG;
|
|
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam;
|
|
strlop(ptr, '-'); // Remove any (illegal) ssid
|
|
|
|
if (strlen(ptr) > 6 ) goto badparam;
|
|
|
|
strcpy(FBBHeader->From, ptr);
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam;
|
|
|
|
if (strlen(ptr) > 40 ) goto badparam;
|
|
|
|
strcpy(FBBHeader->ATBBS, ptr);
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam;
|
|
|
|
if (strlen(ptr) > 6)
|
|
{
|
|
// Temp fix - reject instead of breaking connection
|
|
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '-';
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected as TO field too long");
|
|
|
|
user->Total.MsgsRejectedIn[Index]++;
|
|
return;
|
|
}
|
|
|
|
strlop(ptr, '-'); // Remove any (illegal) ssid
|
|
|
|
strcpy(FBBHeader->To, ptr);
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam;
|
|
|
|
if (strlen(ptr) > 12 ) goto badparam;
|
|
|
|
strcpy(FBBHeader->BID, ptr);
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam;
|
|
|
|
FBBHeader->Size = atoi(ptr);
|
|
|
|
goto ok;
|
|
|
|
badparam:
|
|
|
|
BBSputs(conn, "*** Protocol Error - Proposal format error\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
return;
|
|
|
|
ok:
|
|
|
|
// Check Filters
|
|
|
|
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++] = '-';
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by Filters");
|
|
|
|
user->Total.MsgsRejectedIn[Index]++;
|
|
}
|
|
|
|
// 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 ???)
|
|
|
|
else if (DoWeWantIt(conn, FBBHeader) == FALSE)
|
|
{
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '-';
|
|
user->Total.MsgsRejectedIn[Index]++;
|
|
}
|
|
else if ((RestartPtr = LookupRestart(conn, FBBHeader)) > 0)
|
|
{
|
|
conn->FBBReplyIndex += sprintf(&conn->FBBReplyChars[conn->FBBReplyIndex], "!%d", RestartPtr);
|
|
}
|
|
else if (LookupTempBID(FBBHeader->BID))
|
|
{
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '=';
|
|
}
|
|
else
|
|
{
|
|
|
|
// Save BID in temp list in case we are offered it again before completion
|
|
|
|
BIDRec * TempBID = AllocateTempBIDRecord();
|
|
strcpy(TempBID->BID, FBBHeader->BID);
|
|
TempBID->u.conn = conn;
|
|
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '+';
|
|
}
|
|
|
|
FBBHeader->B2Message = FALSE;
|
|
|
|
return;
|
|
|
|
case 'C': // B2 Proposal
|
|
|
|
if (conn->FBBMsgsSent)
|
|
FlagSentMessages(conn, user); // Mark previously sent messages
|
|
|
|
if (conn->DoReverse == FALSE) // Dont accept messages
|
|
return;
|
|
|
|
// Accumulate checksum
|
|
|
|
for (i=0; i< len; i++)
|
|
{
|
|
conn->FBBChecksum+=Buffer[i];
|
|
}
|
|
|
|
// Parse Header
|
|
|
|
// Find free line
|
|
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
FBBHeader = &conn->FBBHeaders[i];
|
|
|
|
if (FBBHeader->Format == 0)
|
|
break;
|
|
}
|
|
|
|
if (i == 5)
|
|
{
|
|
BBSputs(conn, "*** Protocol Error - Too Many Proposals\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
}
|
|
|
|
|
|
// FC EM A3EDD4P00P55 377 281 0
|
|
|
|
/*
|
|
|
|
FC Proposal code. Requires B2 SID feature.
|
|
Type Message type ( 1 or 2 alphanumeric characters
|
|
|
|
CM WinLink 2000 Control message
|
|
EM Encapsulated Message
|
|
ID Unique Message Identifier (max length 12 characters)
|
|
U-Size Uncompressed size of message
|
|
C-size Compressed size of message
|
|
|
|
*/
|
|
FBBHeader->Format = Buffer[1];
|
|
|
|
ptr = strtok_s(&Buffer[3], seps, &Context);
|
|
if (ptr == NULL) goto badparam2;
|
|
if (strlen(ptr) != 2) goto badparam2;
|
|
FBBHeader->MsgType = 'P'; //ptr[0];
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
|
|
if (ptr == NULL) goto badparam2;
|
|
|
|
// Relay In RO mode adds @MPS@R to the MID. Don't know why (yet!)
|
|
|
|
MPS = strlop(ptr, '@');
|
|
if (MPS)
|
|
ROChar = strlop(MPS, '@');
|
|
|
|
if (strlen(ptr) > 12 ) goto badparam;
|
|
strcpy(FBBHeader->BID, ptr);
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
if (ptr == NULL) goto badparam2;
|
|
FBBHeader->Size = atoi(ptr);
|
|
|
|
ptr = strtok_s(NULL, seps, &Context);
|
|
if (ptr == NULL) goto badparam2;
|
|
FBBHeader->CSize = atoi(ptr);
|
|
FBBHeader->B2Message = TRUE;
|
|
|
|
// If using BPQ Extensions (From To AT in proposal) Check Filters
|
|
|
|
Buffer[len - 1] = 0;
|
|
|
|
if (conn->BPQBBS)
|
|
{
|
|
char * From = strtok_s(NULL, seps, &Context);
|
|
char * ATBBS = strtok_s(NULL, seps, &Context);
|
|
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, FBBHeader->Size))
|
|
{
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '-';
|
|
user->Total.MsgsRejectedIn[Index]++;
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by Filters");
|
|
|
|
return;
|
|
}
|
|
}
|
|
goto ok2;
|
|
|
|
badparam2:
|
|
|
|
BBSputs(conn, "*** Protocol Error - Proposal format error\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
return;
|
|
|
|
ok2:
|
|
if (LookupBID(FBBHeader->BID))
|
|
{
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '-';
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check");
|
|
user->Total.MsgsRejectedIn[Index]++;
|
|
|
|
}
|
|
else if (FBBHeader->Size > MaxRXSize)
|
|
{
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '-';
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by Size Limit");
|
|
user->Total.MsgsRejectedIn[Index]++;
|
|
|
|
}
|
|
else if ((RestartPtr = LookupRestart(conn, FBBHeader)) > 0)
|
|
{
|
|
conn->FBBReplyIndex += sprintf(&conn->FBBReplyChars[conn->FBBReplyIndex], "!%d", RestartPtr);
|
|
}
|
|
|
|
else if (LookupTempBID(FBBHeader->BID))
|
|
{
|
|
memset(FBBHeader, 0, sizeof(struct FBBHeaderLine)); // Clear header
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = '=';
|
|
}
|
|
else
|
|
{
|
|
// Save BID in temp list in case we are offered it again before completion
|
|
|
|
BIDRec * TempBID = AllocateTempBIDRecord();
|
|
strcpy(TempBID->BID, FBBHeader->BID);
|
|
TempBID->u.conn = conn;
|
|
|
|
conn->FBBReplyChars[conn->FBBReplyIndex++] = 'Y';
|
|
}
|
|
|
|
return;
|
|
|
|
case '>':
|
|
|
|
// Optional Checksum
|
|
|
|
if (conn->DoReverse == FALSE) // Dont accept messages
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "Reverse Forwarding not allowed");
|
|
Disconnect(conn->BPQStream);
|
|
return;
|
|
}
|
|
|
|
if (len > 3)
|
|
{
|
|
int sum;
|
|
|
|
sscanf(&Buffer[3], "%x", &sum);
|
|
|
|
conn->FBBChecksum+=sum;
|
|
|
|
if (conn->FBBChecksum)
|
|
{
|
|
BBSputs(conn, "*** Proposal Checksum Error\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Return "FS ", followed by +-= for each proposal
|
|
|
|
conn->FBBReplyChars[conn->FBBReplyIndex] = 0;
|
|
conn->FBBReplyIndex = 0;
|
|
|
|
nodeprintfEx(conn, "FS %s\r", conn->FBBReplyChars);
|
|
|
|
// if all rejected, send proposals or prompt, else set up for first message
|
|
|
|
FBBHeader = &conn->FBBHeaders[0];
|
|
|
|
if (FBBHeader->MsgType == 0)
|
|
{
|
|
conn->FBBIndex = 0; // ready for first block;
|
|
memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine));
|
|
conn->FBBChecksum = 0;
|
|
|
|
if (!FBBDoForward(conn)) // Send proposal if anthing to forward
|
|
{
|
|
conn->InputMode = 0;
|
|
|
|
if (conn->DoReverse)
|
|
FBBputs(conn, "FF\r");
|
|
else
|
|
{
|
|
FBBputs(conn, "FQ\r");
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (conn->BBSFlags & FBBForwarding)
|
|
{
|
|
conn->InputMode = 'B';
|
|
}
|
|
|
|
CreateMessage(conn, FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->MsgType, FBBHeader->BID, NULL);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user)
|
|
{
|
|
struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block
|
|
int i;
|
|
|
|
conn->FBBMsgsSent = FALSE;
|
|
|
|
for (i=0; i < 5; i++)
|
|
{
|
|
FBBHeader = &conn->FBBHeaders[i];
|
|
|
|
if (FBBHeader && FBBHeader->MsgType) // Not a zapped entry
|
|
{
|
|
int Length=0;
|
|
char * MailBuffer = malloc(100);
|
|
char Title[100];
|
|
|
|
Length += sprintf(MailBuffer, "Message %d Held\r\n", FBBHeader->FwdMsg->number);
|
|
sprintf(Title, "Message %d Held - Rejected by Winlink", FBBHeader->FwdMsg->number);
|
|
SendMessageToSYSOP(Title, MailBuffer, Length);
|
|
|
|
FBBHeader->FwdMsg->status = 'H'; // Mark as Held
|
|
}
|
|
}
|
|
memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine));
|
|
SaveMessageDatabase();
|
|
}
|
|
|
|
|
|
|
|
VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user)
|
|
{
|
|
struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block
|
|
int i;
|
|
|
|
// Called if FBB command received after sending a block of messages . Flag as as sent.
|
|
|
|
conn->FBBMsgsSent = FALSE;
|
|
|
|
for (i=0; i < 5; i++)
|
|
{
|
|
FBBHeader = &conn->FBBHeaders[i];
|
|
|
|
if (FBBHeader && FBBHeader->MsgType) // Not a zapped entry
|
|
{
|
|
if ((conn->Paclink || conn->RMSExpress || conn->PAT) &&
|
|
// ((conn->UserPointer->flags & F_NTSMPS) == 0) &&
|
|
(FBBHeader->FwdMsg->type == 'P'))
|
|
{
|
|
// Kill Messages sent to paclink/RMS Express unless BBS FWD bit set
|
|
|
|
if (check_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber) == 0)
|
|
{
|
|
FlagAsKilled(FBBHeader->FwdMsg, FALSE);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
clear_fwd_bit(FBBHeader->FwdMsg->fbbs, user->BBSNumber);
|
|
set_fwd_bit(FBBHeader->FwdMsg->forw, user->BBSNumber);
|
|
|
|
// Only mark as forwarded if sent to all BBSs that should have it
|
|
|
|
if (memcmp(FBBHeader->FwdMsg->fbbs, zeros, NBMASK) == 0)
|
|
{
|
|
FBBHeader->FwdMsg->status = 'F'; // Mark as forwarded
|
|
FBBHeader->FwdMsg->datechanged=time(NULL);
|
|
}
|
|
|
|
#ifndef NOMQTT
|
|
if (MQTT)
|
|
MQTTMessageEvent(FBBHeader->FwdMsg);
|
|
#endif
|
|
|
|
FBBHeader->FwdMsg->Locked = 0; // Unlock
|
|
conn->UserPointer->ForwardingInfo->MsgCount--;
|
|
}
|
|
}
|
|
memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine));
|
|
SaveMessageDatabase();
|
|
}
|
|
|
|
|
|
VOID SetupNextFBBMessage(CIRCUIT * conn)
|
|
{
|
|
struct FBBHeaderLine * FBBHeader; // The Headers from an FBB forward block
|
|
|
|
memmove(&conn->FBBHeaders[0], &conn->FBBHeaders[1], 4 * sizeof(struct FBBHeaderLine));
|
|
|
|
memset(&conn->FBBHeaders[4], 0, sizeof(struct FBBHeaderLine));
|
|
|
|
FBBHeader = &conn->FBBHeaders[0];
|
|
|
|
if (FBBHeader->MsgType == 0)
|
|
{
|
|
conn->FBBIndex = 0; // ready for next block;
|
|
memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine));
|
|
|
|
conn->FBBChecksum = 0;
|
|
conn->InputMode = 0;
|
|
|
|
if (!FBBDoForward(conn)) // Send proposal if anthing to forward
|
|
{
|
|
conn->InputMode = 0;
|
|
FBBputs(conn, "FF\r");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (conn->BBSFlags & FBBForwarding)
|
|
conn->InputMode = 'B';
|
|
|
|
CreateMessage(conn, FBBHeader->From, FBBHeader->To, FBBHeader->ATBBS, FBBHeader->MsgType, FBBHeader->BID, NULL);
|
|
}
|
|
}
|
|
|
|
BOOL FBBDoForward(CIRCUIT * conn)
|
|
{
|
|
int i;
|
|
char proposal[100];
|
|
int proplen;
|
|
|
|
if (FindMessagestoForward(conn))
|
|
{
|
|
// Send Proposal Block
|
|
|
|
struct FBBHeaderLine * FBBHeader;
|
|
|
|
for (i=0; i < conn->FBBIndex; i++)
|
|
{
|
|
FBBHeader = &conn->FBBHeaders[i];
|
|
|
|
if (conn->BBSFlags & FBBB2Mode)
|
|
|
|
if (conn->BPQBBS)
|
|
|
|
// Add From and To Header for Filters
|
|
|
|
proplen = sprintf(proposal, "FC EM %s %d %d %s %s %s %c\r",
|
|
FBBHeader->BID,
|
|
FBBHeader->Size,
|
|
FBBHeader->CSize,
|
|
FBBHeader->From,
|
|
(FBBHeader->ATBBS[0]) ? FBBHeader->ATBBS : conn->UserPointer->Call,
|
|
FBBHeader->To,
|
|
FBBHeader->MsgType);
|
|
|
|
else
|
|
|
|
// FC EM A3EDD4P00P55 377 281 0
|
|
|
|
proplen = sprintf(proposal, "FC EM %s %d %d %d\r",
|
|
FBBHeader->BID,
|
|
FBBHeader->Size,
|
|
FBBHeader->CSize, 0);
|
|
|
|
else
|
|
proplen = sprintf(proposal, "%s %c %s %s %s %s %d\r",
|
|
(conn->BBSFlags & FBBCompressed) ? "FA" : "FB",
|
|
FBBHeader->MsgType,
|
|
FBBHeader->From,
|
|
(FBBHeader->ATBBS[0]) ? FBBHeader->ATBBS : conn->UserPointer->Call,
|
|
FBBHeader->To,
|
|
FBBHeader->BID,
|
|
FBBHeader->Size);
|
|
|
|
// Accumulate checksum
|
|
|
|
while(proplen > 0)
|
|
{
|
|
conn->FBBChecksum+=proposal[--proplen];
|
|
}
|
|
|
|
FBBputs(conn, proposal);
|
|
}
|
|
|
|
conn->FBBChecksum = - conn->FBBChecksum;
|
|
|
|
nodeprintfEx(conn, "F> %02X\r", conn->FBBChecksum);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID UnpackFBBBinary(CIRCUIT * conn)
|
|
{
|
|
int MsgLen, i, offset, n;
|
|
UCHAR * ptr;
|
|
|
|
loop:
|
|
|
|
if (conn->CloseAfterFlush) // Failed (or complete), so discard rest of input
|
|
{
|
|
conn->InputLen = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
ptr = conn->InputBuffer;
|
|
|
|
if (conn->InputLen < 2)
|
|
return; // All formats need at least two bytes
|
|
|
|
switch (*ptr)
|
|
{
|
|
case 1: // Header
|
|
|
|
MsgLen = ptr[1] + 2;
|
|
|
|
if (conn->InputLen < MsgLen)
|
|
return; // Wait for more
|
|
|
|
if (strlen(&ptr[2]) > 60)
|
|
{
|
|
memcpy(conn->TempMsg->title, &ptr[2], 60);
|
|
conn->TempMsg->title[60] = 0;
|
|
Debugprintf("FBB Subject too long - truncated, %s", &ptr[2]);
|
|
}
|
|
else
|
|
strcpy(conn->TempMsg->title, &ptr[2]);
|
|
|
|
offset = atoi(ptr+3+strlen(&ptr[2]));
|
|
|
|
ptr += MsgLen;
|
|
|
|
memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen);
|
|
|
|
conn->InputLen -= MsgLen;
|
|
|
|
conn->FBBChecksum = 0;
|
|
|
|
if (offset)
|
|
{
|
|
struct FBBRestartData * RestartRec;
|
|
|
|
// Trying to restart - make sure we have restart data
|
|
|
|
for (i = 1; i <= RestartCount; i++)
|
|
{
|
|
RestartRec = RestartData[i];
|
|
|
|
if ((RestartRec->UserPointer == conn->UserPointer)
|
|
&& (strcmp(RestartRec->TempMsg->bid, conn->TempMsg->bid) == 0))
|
|
{
|
|
if (RestartRec->TempMsg->length <= offset)
|
|
{
|
|
conn->TempMsg->length = RestartRec->TempMsg->length;
|
|
conn->MailBuffer = RestartRec->MailBuffer;
|
|
conn->MailBufferSize = RestartRec->MailBufferSize;
|
|
|
|
// FBB Seems to insert 6 Byte message
|
|
// It looks like the original csum and length - perhaps a a consistancy check
|
|
|
|
// But Airmail Sends the Restart Data in the next packet, move the check code.
|
|
|
|
conn->NeedRestartHeader = TRUE;
|
|
|
|
goto GotRestart;
|
|
}
|
|
else
|
|
{
|
|
BBSputs(conn, "*** Trying to restart from invalid position.\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
|
|
return;
|
|
}
|
|
|
|
// Remove Restart info
|
|
|
|
for (n = i; n < RestartCount; n++)
|
|
{
|
|
RestartData[n] = RestartData[n+1]; // move down all following entries
|
|
}
|
|
RestartCount--;
|
|
}
|
|
}
|
|
|
|
// No Restart Data
|
|
|
|
BBSputs(conn, "*** Trying to restart, but no restart data.\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
|
|
return;
|
|
}
|
|
|
|
// Create initial buffer of 10K. Expand if needed later
|
|
|
|
if (conn->MailBufferSize == 0)
|
|
{
|
|
// Dont allocate if restarting
|
|
|
|
conn->MailBuffer=malloc(10000);
|
|
conn->MailBufferSize=10000;
|
|
}
|
|
|
|
GotRestart:
|
|
|
|
if (conn->MailBuffer == NULL)
|
|
{
|
|
BBSputs(conn, "*** Failed to create Message Buffer\r");
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
|
|
return;
|
|
}
|
|
|
|
goto loop;
|
|
|
|
|
|
|
|
case 2: // Data Block
|
|
|
|
if (ptr[1] == 0)
|
|
MsgLen = 256;
|
|
else
|
|
MsgLen = ptr[1];
|
|
|
|
if (conn->InputLen < (MsgLen + 2))
|
|
return; // Wait for more
|
|
|
|
// If waiting for Restart Header, see if it has arrived
|
|
|
|
if (conn->NeedRestartHeader)
|
|
{
|
|
conn->NeedRestartHeader = FALSE;
|
|
|
|
if (MsgLen == 6)
|
|
{
|
|
ptr = conn->InputBuffer+2;
|
|
conn->InputLen -=8;
|
|
|
|
for (i=0; i<6; i++)
|
|
{
|
|
conn->FBBChecksum+=ptr[0];
|
|
ptr++;
|
|
}
|
|
memmove(conn->InputBuffer, ptr, conn->InputLen);
|
|
}
|
|
else
|
|
{
|
|
BBSputs(conn, "*** Restart Header Missing.\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
}
|
|
|
|
goto loop;
|
|
|
|
}
|
|
// Process it
|
|
|
|
ptr+=2;
|
|
|
|
for (i=0; i< MsgLen; i++)
|
|
{
|
|
conn->FBBChecksum+=ptr[i];
|
|
}
|
|
|
|
ptr-=2;
|
|
|
|
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], &ptr[2], MsgLen);
|
|
|
|
conn->TempMsg->length += MsgLen;
|
|
|
|
MsgLen +=2;
|
|
|
|
ptr += MsgLen;
|
|
|
|
memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen);
|
|
|
|
conn->InputLen -= MsgLen;
|
|
|
|
goto loop;
|
|
|
|
|
|
case 4: // EOM
|
|
|
|
// Process EOM
|
|
|
|
conn->FBBChecksum+=ptr[1];
|
|
|
|
if (conn->FBBChecksum == 0)
|
|
{
|
|
#ifndef LINBPQ
|
|
__try
|
|
{
|
|
#endif
|
|
conn->InputMode = 0; // So we won't save Restart data if decode fails
|
|
Decode(conn, 0); // Setup Next Message will reset InputMode if needed
|
|
#ifndef LINBPQ
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
BBSputs(conn, "*** Program Error Decoding Message\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
else
|
|
{
|
|
BBSputs(conn, "*** Message Checksum Error\r");
|
|
Flush(conn);
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
|
|
// Don't allow restart, as saved data is probably duff
|
|
|
|
conn->DontSaveRestartData = TRUE;
|
|
return;
|
|
}
|
|
ptr += 2;
|
|
|
|
memmove(conn->InputBuffer, ptr, conn->InputLen-2);
|
|
|
|
conn->InputLen -= 2;
|
|
|
|
goto loop;
|
|
|
|
default:
|
|
|
|
BBSputs(conn, "*** Protocol Error - Invalid Binary Message Format (Invalid Block Type)\r");
|
|
Flush(conn);
|
|
|
|
if (conn->CloseAfterFlush == 0)
|
|
{
|
|
// Dont do it more than once
|
|
|
|
conn->CloseAfterFlush = 20; // 2 Secs
|
|
|
|
// Don't allow restart, as saved data is probably duff
|
|
|
|
// Actually all but the last block is probably OK, but maybe
|
|
// not worth the risk of restarting
|
|
|
|
// Actually I think it is
|
|
|
|
if (conn->TempMsg->length > 256)
|
|
{
|
|
conn->TempMsg->length -= 256;
|
|
conn->DontSaveRestartData = FALSE;
|
|
}
|
|
else
|
|
conn->DontSaveRestartData = TRUE;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg)
|
|
{
|
|
struct tm * tm;
|
|
char * MsgBytes, * Save;
|
|
UCHAR * Compressed, * Compressedptr;
|
|
UCHAR * UnCompressed;
|
|
char * Title;
|
|
UCHAR * Output, * Outputptr;
|
|
int i, OrigLen, MsgLen, CompLen, DataOffset;
|
|
char Rline[80];
|
|
int RLineLen;
|
|
int Index;
|
|
time_t temp;
|
|
|
|
if (FwdMsg->type == 'P')
|
|
Index = PMSG;
|
|
else if (FwdMsg->type == 'B')
|
|
Index = BMSG;
|
|
else if (FwdMsg->type == 'T')
|
|
Index = TMSG;
|
|
|
|
MsgBytes = Save = ReadMessageFile(FwdMsg->number);
|
|
|
|
if (MsgBytes == 0)
|
|
{
|
|
MsgBytes = _strdup("Message file not found\r\n");
|
|
FwdMsg->length = (int)strlen(MsgBytes);
|
|
}
|
|
|
|
OrigLen = FwdMsg->length;
|
|
|
|
Title = FwdMsg->title;
|
|
|
|
Compressed = Compressedptr = zalloc(2 * OrigLen + 200);
|
|
Output = Outputptr = zalloc(2 * OrigLen + 200);
|
|
|
|
*Outputptr++ = 1;
|
|
*Outputptr++ = (int)strlen(Title) + 8;
|
|
strcpy(Outputptr, Title);
|
|
Outputptr += strlen(Title) +1;
|
|
sprintf(Outputptr, "%6d", conn->RestartFrom);
|
|
Outputptr += 7;
|
|
|
|
DataOffset = (int)(Outputptr - Output); // Used if restarting
|
|
|
|
memcpy(&temp, &FwdMsg->datereceived, sizeof(time_t));
|
|
tm = gmtime(&temp);
|
|
|
|
sprintf(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,
|
|
FwdMsg->number, BBSName, HRoute, RlineVer);
|
|
|
|
if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message
|
|
strcat(Rline, "\r\n");
|
|
|
|
RLineLen = (int)strlen(Rline);
|
|
|
|
MsgLen = OrigLen + RLineLen;
|
|
|
|
UnCompressed = zalloc(MsgLen+10);
|
|
|
|
strcpy(UnCompressed, Rline);
|
|
|
|
// If a B2 Message, Remove B2 Header
|
|
|
|
if (FwdMsg->B2Flags & B2Msg)
|
|
{
|
|
char * ptr;
|
|
int BodyLen = OrigLen;
|
|
|
|
// Remove all B2 Headers, and all but the first part.
|
|
|
|
ptr = strstr(MsgBytes, "Body:");
|
|
|
|
if (ptr)
|
|
{
|
|
BodyLen = atoi(&ptr[5]);
|
|
ptr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers
|
|
|
|
if (ptr)
|
|
ptr +=4;
|
|
else
|
|
ptr = MsgBytes;
|
|
|
|
}
|
|
else
|
|
ptr = MsgBytes;
|
|
|
|
if (memcmp(ptr, "R:", 2) == 0) // Already have RLines, so remove blank line after new R:line
|
|
RLineLen -= 2;
|
|
|
|
memcpy(&UnCompressed[RLineLen], ptr, BodyLen);
|
|
|
|
MsgLen = BodyLen + RLineLen;
|
|
}
|
|
else // Not B2 Message
|
|
{
|
|
memcpy(&UnCompressed[RLineLen], MsgBytes, OrigLen);
|
|
}
|
|
|
|
CompLen = Encode(UnCompressed, Compressed, MsgLen, conn->BBSFlags & FBBB1Mode, conn->BBSFlags & FBBCompressed);
|
|
|
|
conn->FBBChecksum = 0;
|
|
|
|
// If restarting, send the checksum and length as a single record, then data from the restart point
|
|
// The count includes the header, so adjust count and pointers
|
|
|
|
if (conn->RestartFrom)
|
|
{
|
|
*Outputptr++ = 2;
|
|
*Outputptr++ = 6;
|
|
|
|
for (i=0; i< 6; i++)
|
|
{
|
|
conn->FBBChecksum+=Compressed[i];
|
|
*Outputptr++ = Compressed[i];
|
|
}
|
|
|
|
for (i=conn->RestartFrom; i< CompLen; i++)
|
|
{
|
|
conn->FBBChecksum+=Compressed[i];
|
|
}
|
|
|
|
Compressedptr += conn->RestartFrom;
|
|
CompLen -= conn->RestartFrom;
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i< CompLen; i++)
|
|
{
|
|
conn->FBBChecksum+=Compressed[i];
|
|
}
|
|
}
|
|
|
|
while (CompLen > 250)
|
|
{
|
|
*Outputptr++ = 2;
|
|
*Outputptr++ = 250;
|
|
|
|
memcpy(Outputptr, Compressedptr, 250);
|
|
Outputptr += 250;
|
|
Compressedptr += 250;
|
|
CompLen -= 250;
|
|
}
|
|
|
|
*Outputptr++ = 2;
|
|
*Outputptr++ = CompLen;
|
|
|
|
memcpy(Outputptr, Compressedptr, CompLen);
|
|
|
|
Outputptr += CompLen;
|
|
|
|
*Outputptr++ = 4;
|
|
conn->FBBChecksum = - conn->FBBChecksum;
|
|
*Outputptr++ = conn->FBBChecksum;
|
|
|
|
if (conn->OpenBCM) // Telnet, so escape any 0xFF
|
|
{
|
|
unsigned char * ptr1 = Output;
|
|
unsigned char * ptr2 = Compressed; // Reuse Compressed buffer
|
|
size_t Len = Outputptr - Output;
|
|
unsigned char c;
|
|
|
|
while (Len--)
|
|
{
|
|
c = *(ptr1++);
|
|
*(ptr2++) = c;
|
|
if (c == 0xff) // FF becodes FFFF
|
|
*(ptr2++) = c;
|
|
}
|
|
|
|
QueueMsg(conn, Compressed, (int)(ptr2 - Compressed));
|
|
}
|
|
else
|
|
QueueMsg(conn, Output, (int)(Outputptr - Output));
|
|
|
|
free(Save);
|
|
free(Compressed);
|
|
free(UnCompressed);
|
|
free(Output);
|
|
|
|
}
|
|
|
|
BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline)
|
|
{
|
|
char * MsgBytes;
|
|
UCHAR * Compressed;
|
|
UCHAR * UnCompressed;
|
|
int OrigLen, MsgLen, B2HddrLen, CompLen;
|
|
char Date[20];
|
|
struct tm * tm;
|
|
char B2From[80];
|
|
char B2To[80];
|
|
struct MsgInfo * Msg = FBBHeader->FwdMsg;
|
|
struct UserInfo * FromUser;
|
|
int BodyLineToBody;
|
|
int RlineLen = (int)strlen(Rline) ;
|
|
char * TypeString;
|
|
#ifndef LINBPQ
|
|
struct _EXCEPTION_POINTERS exinfo;
|
|
|
|
__try {
|
|
#endif
|
|
|
|
if (Msg == NULL)
|
|
Debugprintf("Msg = NULL");
|
|
|
|
|
|
MsgBytes = ReadMessageFile(Msg->number);
|
|
|
|
if (MsgBytes == 0)
|
|
{
|
|
Debugprintf("B2 Message - Message File not found");
|
|
return FALSE;
|
|
}
|
|
|
|
UnCompressed = zalloc(Msg->length + 2000);
|
|
|
|
if (UnCompressed == NULL)
|
|
Debugprintf("B2 Message - zalloc for %d failed", Msg->length + 2000);
|
|
|
|
OrigLen = Msg->length;
|
|
|
|
// If a B2 Message add R:line at start of Body, but otherwise leave intact.
|
|
// Unless a message to Paclink, when we must remove any HA from the TO address
|
|
// Or to a CMS, when we remove HA from From or Reply-to
|
|
|
|
if (Msg->B2Flags & B2Msg)
|
|
{
|
|
char * ptr, *ptr2;
|
|
int BodyLen;
|
|
int BodyLineLen;
|
|
int Index;
|
|
|
|
MsgLen = OrigLen + RlineLen;
|
|
|
|
if (conn->Paclink)
|
|
{
|
|
// Remove any HA on the TO address
|
|
|
|
ptr = strstr(MsgBytes, "To:");
|
|
if (ptr)
|
|
{
|
|
ptr2 = strstr(ptr, "\r\n");
|
|
if (ptr2)
|
|
{
|
|
while (ptr < ptr2)
|
|
{
|
|
if (*ptr == '.' || *ptr == '@')
|
|
{
|
|
memset(ptr, ' ', ptr2 - ptr);
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (conn->WL2K)
|
|
{
|
|
// Remove any HA on the From or Reply-To address
|
|
|
|
ptr = strstr(MsgBytes, "From:");
|
|
if (ptr == NULL)
|
|
ptr = strstr(MsgBytes, "Reply-To:");
|
|
|
|
if (ptr)
|
|
{
|
|
ptr2 = strstr(ptr, "\r\n");
|
|
if (ptr2)
|
|
{
|
|
while (ptr < ptr2)
|
|
{
|
|
if (*ptr == '.' || *ptr == '@')
|
|
{
|
|
memset(ptr, ' ', ptr2 - ptr);
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Add R: Line at start of body. Will Need to Update Body Length
|
|
|
|
ptr = strstr(MsgBytes, "Body:");
|
|
|
|
if (ptr == 0)
|
|
{
|
|
Debugprintf("B2 Messages without Body: Line");
|
|
return FALSE;
|
|
}
|
|
ptr2 = strstr(ptr, "\r\n");
|
|
|
|
Index = (int)(ptr - MsgBytes); // Bytes Before Body: line
|
|
|
|
if (Index <= 0 || Index > MsgLen)
|
|
{
|
|
Debugprintf("B2 Message Body: line position invalid - %d", Index);
|
|
return FALSE;
|
|
}
|
|
|
|
// If message to saildocs adding an R: line will mess up the message processing, so add as an X header
|
|
|
|
if (strstr(MsgBytes, "To: query@saildocs.com"))
|
|
{
|
|
int x_Len;
|
|
|
|
memcpy(UnCompressed, MsgBytes, Index); // Up to Old Body;
|
|
x_Len = sprintf(&UnCompressed[Index], "x-R: %s", &Rline[2]);
|
|
MsgLen = OrigLen + x_Len;
|
|
Index +=x_Len;
|
|
goto copyRest;
|
|
}
|
|
|
|
BodyLen = atoi(&ptr[5]);
|
|
|
|
if (BodyLen < 0 || BodyLen > MsgLen)
|
|
{
|
|
Debugprintf("B2 Message Length from Body: line invalid - Msg len %d From Body %d", MsgLen, BodyLen);
|
|
return FALSE;
|
|
}
|
|
|
|
BodyLineLen = (int)(ptr2 - ptr) + 2;
|
|
MsgLen -= BodyLineLen; // Length of Body Line may change
|
|
|
|
ptr = strstr(ptr2, "\r\n\r\n"); // Blank line before Body
|
|
|
|
if (ptr == 0)
|
|
{
|
|
Debugprintf("B2 Message - No Blank Line before Body");
|
|
return FALSE;
|
|
}
|
|
|
|
ptr += 4;
|
|
|
|
ptr2 += 2; // Line Following Original Body: Line
|
|
|
|
BodyLineToBody = (int)(ptr - ptr2);
|
|
|
|
if (memcmp(ptr, "R:", 2) != 0) // No R line, so must be our message
|
|
{
|
|
strcat(Rline, "\r\n");
|
|
RlineLen += 2;
|
|
MsgLen += 2;
|
|
}
|
|
BodyLen += RlineLen;
|
|
|
|
memcpy(UnCompressed, MsgBytes, Index); // Up to Old Body;
|
|
BodyLineLen = sprintf(&UnCompressed[Index], "Body: %d\r\n", BodyLen);
|
|
|
|
MsgLen += BodyLineLen; // Length of Body Line may have changed
|
|
Index += BodyLineLen;
|
|
|
|
if (BodyLineToBody < 0 || BodyLineToBody > 1000)
|
|
{
|
|
Debugprintf("B2 Message - Body too far from Body Line - %d", BodyLineToBody);
|
|
return FALSE;
|
|
}
|
|
memcpy(&UnCompressed[Index], ptr2, BodyLineToBody); // Stuff Between Body: Line and Body
|
|
|
|
Index += BodyLineToBody;
|
|
|
|
memcpy(&UnCompressed[Index], Rline, RlineLen);
|
|
Index += RlineLen;
|
|
|
|
copyRest:
|
|
|
|
memcpy(&UnCompressed[Index], ptr, MsgLen - Index); // Rest of Message
|
|
|
|
FBBHeader->Size = MsgLen;
|
|
|
|
Compressed = zalloc(2 * MsgLen + 200);
|
|
#ifndef LINBPQ
|
|
__try {
|
|
#endif
|
|
CompLen = Encode(UnCompressed, Compressed, MsgLen, TRUE, conn->BBSFlags & FBBCompressed);
|
|
|
|
FBBHeader->CompressedMsg = Compressed;
|
|
FBBHeader->CSize = CompLen;
|
|
|
|
free(UnCompressed);
|
|
return TRUE;
|
|
#ifndef LINBPQ
|
|
} My__except_Routine("Encode B2Message");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (memcmp(MsgBytes, "R:", 2) != 0) // No R line, so must be our message
|
|
{
|
|
strcat(Rline, "\r\n");
|
|
RlineLen += 2;
|
|
}
|
|
|
|
MsgLen = OrigLen + RlineLen;
|
|
|
|
// if (conn->RestartFrom == 0)
|
|
// {
|
|
// // save time first sent, or checksum will be wrong when we restart
|
|
//
|
|
// FwdMsg->datechanged=time(NULL);
|
|
// }
|
|
|
|
tm = gmtime((time_t *)&Msg->datechanged);
|
|
|
|
sprintf(Date, "%04d/%02d/%02d %02d:%02d",
|
|
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
|
|
|
|
// We create the B2 Header
|
|
/*
|
|
MID: XR88I1J160EB
|
|
Date: 2009/07/25 18:17
|
|
Type: Private
|
|
From: SMTP:john.wiseman@ntlworld.com
|
|
To: G8BPQ
|
|
Subject: RE: RMS Test Message
|
|
Mbo: SMTP
|
|
Body: 213
|
|
|
|
*/
|
|
if (strcmp(Msg->to, "RMS") == 0) // Address is in via
|
|
strcpy(B2To, Msg->via);
|
|
else
|
|
if (Msg->via[0] && (!conn->Paclink))
|
|
sprintf(B2To, "%s@%s", Msg->to, Msg->via);
|
|
else
|
|
strcpy(B2To, Msg->to);
|
|
|
|
// Try to create a full from: addrsss so RMS Express can reply
|
|
|
|
strcpy(B2From, Msg->from);
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "B2 From %s", B2From);
|
|
|
|
if (strcmp(conn->Callsign, "RMS") != 0 && conn->WL2K == 0) // if going to RMS - just send calll
|
|
{
|
|
if (_stricmp(Msg->from, "SMTP:") == 0) // Address is in via
|
|
strcpy(B2From, Msg->emailfrom);
|
|
else
|
|
{
|
|
FromUser = LookupCall(Msg->from);
|
|
|
|
if (FromUser)
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "B2 From - Local User");
|
|
|
|
if (FromUser->HomeBBS[0])
|
|
sprintf(B2From, "%s@%s", Msg->from, FromUser->HomeBBS);
|
|
else
|
|
sprintf(B2From, "%s@%s", Msg->from, BBSName);
|
|
}
|
|
else
|
|
{
|
|
WPRecP WP = LookupWP(Msg->from);
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "B2 From - not local User");
|
|
|
|
if (WP)
|
|
sprintf(B2From, "%s@%s", Msg->from, WP->first_homebbs);
|
|
}
|
|
}
|
|
}
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "B2 From Finally %s", B2From);
|
|
|
|
if (Msg->type == 'P')
|
|
TypeString = "Private" ;
|
|
else if (Msg->type == 'B')
|
|
TypeString = "Bulletin";
|
|
else if (Msg->type == 'T')
|
|
TypeString = "Traffic";
|
|
|
|
B2HddrLen = sprintf(UnCompressed,
|
|
"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n"
|
|
"Content-Type: text/plain\r\nContent-Transfer-Encoding: 8bit\r\nBody: %d\r\n\r\n",
|
|
Msg->bid, Date, TypeString, B2From, B2To, Msg->title, BBSName, MsgLen);
|
|
|
|
|
|
memcpy(&UnCompressed[B2HddrLen], Rline, RlineLen);
|
|
memcpy(&UnCompressed[B2HddrLen + RlineLen], MsgBytes, OrigLen); // Rest of Message
|
|
|
|
MsgLen += B2HddrLen;
|
|
|
|
FBBHeader->Size = MsgLen;
|
|
|
|
Compressed = zalloc(2 * MsgLen + 200);
|
|
|
|
CompLen = Encode(UnCompressed, Compressed, MsgLen, TRUE, conn->BBSFlags & FBBCompressed);
|
|
|
|
FBBHeader->CompressedMsg = Compressed;
|
|
FBBHeader->CSize = CompLen;
|
|
|
|
free(UnCompressed);
|
|
|
|
return TRUE;
|
|
#ifndef LINBPQ
|
|
} My__except_Routine("CreateB2Message");
|
|
#endif
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader)
|
|
{
|
|
UCHAR * Compressed, * Compressedptr;
|
|
UCHAR * Output, * Outputptr;
|
|
int i, CompLen;
|
|
int Index;
|
|
|
|
if (FBBHeader->FwdMsg->type == 'P')
|
|
Index = PMSG;
|
|
else if (FBBHeader->FwdMsg->type == 'B')
|
|
Index = BMSG;
|
|
else if (FBBHeader->FwdMsg->type == 'T')
|
|
Index = TMSG;
|
|
|
|
Compressed = Compressedptr = FBBHeader->CompressedMsg;
|
|
|
|
Output = Outputptr = zalloc(FBBHeader->CSize + 10000);
|
|
|
|
*Outputptr++ = 1;
|
|
*Outputptr++ = (int)strlen(FBBHeader->FwdMsg->title) + 8;
|
|
strcpy(Outputptr, FBBHeader->FwdMsg->title);
|
|
Outputptr += strlen(FBBHeader->FwdMsg->title) +1;
|
|
sprintf(Outputptr, "%06d", conn->RestartFrom);
|
|
Outputptr += 7;
|
|
|
|
CompLen = FBBHeader->CSize;
|
|
|
|
conn->FBBChecksum = 0;
|
|
|
|
// If restarting, send the checksum and length as a single record, then data from the restart point
|
|
// The count includes the header, so adjust count and pointers
|
|
|
|
if (conn->RestartFrom)
|
|
{
|
|
*Outputptr++ = 2;
|
|
*Outputptr++ = 6;
|
|
|
|
for (i=0; i< 6; i++)
|
|
{
|
|
conn->FBBChecksum+=Compressed[i];
|
|
*Outputptr++ = Compressed[i];
|
|
}
|
|
|
|
for (i=conn->RestartFrom; i< CompLen; i++)
|
|
{
|
|
conn->FBBChecksum+=Compressed[i];
|
|
}
|
|
|
|
Compressedptr += conn->RestartFrom;
|
|
CompLen -= conn->RestartFrom;
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i< CompLen; i++)
|
|
{
|
|
conn->FBBChecksum+=Compressed[i];
|
|
}
|
|
conn->UserPointer->Total.MsgsSent[Index]++;
|
|
conn->UserPointer->Total.BytesForwardedOut[Index] += FBBHeader->FwdMsg->length;
|
|
|
|
}
|
|
|
|
while (CompLen > 256)
|
|
{
|
|
*Outputptr++ = 2;
|
|
*Outputptr++ = 0;
|
|
|
|
memcpy(Outputptr, Compressedptr, 256);
|
|
Outputptr += 256;
|
|
Compressedptr += 256;
|
|
CompLen -= 256;
|
|
}
|
|
|
|
*Outputptr++ = 2;
|
|
*Outputptr++ = CompLen;
|
|
|
|
memcpy(Outputptr, Compressedptr, CompLen);
|
|
|
|
Outputptr += CompLen;
|
|
|
|
*Outputptr++ = 4;
|
|
conn->FBBChecksum = - conn->FBBChecksum;
|
|
*Outputptr++ = conn->FBBChecksum;
|
|
|
|
if (conn->OpenBCM) // Telnet, so escape any 0xFF
|
|
{
|
|
unsigned char * ptr1 = Output;
|
|
unsigned char * ptr2 = Compressed; // Reuse Compressed buffer
|
|
int Len = (int)(Outputptr - Output);
|
|
unsigned char c;
|
|
|
|
while (Len--)
|
|
{
|
|
c = *(ptr1++);
|
|
*(ptr2++) = c;
|
|
if (c == 0xff) // FF becodes FFFF
|
|
*(ptr2++) = c;
|
|
}
|
|
|
|
QueueMsg(conn, Compressed, (int)(ptr2 - Compressed));
|
|
}
|
|
else
|
|
QueueMsg(conn, Output, (int)(Outputptr - Output));
|
|
|
|
free(Compressed);
|
|
free(Output);
|
|
}
|
|
|
|
// Restart Routines.
|
|
|
|
VOID SaveFBBBinary(CIRCUIT * conn)
|
|
{
|
|
// Disconnected during binary transfer
|
|
|
|
char Msg[120];
|
|
int i, len;
|
|
struct FBBRestartData * RestartRec = NULL;
|
|
|
|
if (conn->TempMsg == NULL)
|
|
return;
|
|
|
|
if (conn->TempMsg->length < 256)
|
|
return; // Not worth it.
|
|
|
|
// If we already have a restart record, reuse it
|
|
|
|
for (i = 1; i <= RestartCount; i++)
|
|
{
|
|
RestartRec = RestartData[i];
|
|
|
|
if ((RestartRec->UserPointer == conn->UserPointer)
|
|
&& (strcmp(RestartRec->TempMsg->bid, conn->TempMsg->bid) == 0))
|
|
{
|
|
// Fund it, so reuse
|
|
|
|
// If we have more data, reset retry count
|
|
|
|
if (RestartRec->TempMsg->length < conn->TempMsg->length)
|
|
RestartRec->Count = 0;;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RestartRec == NULL)
|
|
{
|
|
RestartRec = zalloc(sizeof (struct FBBRestartData));
|
|
|
|
GetSemaphore(&AllocSemaphore, 0);
|
|
|
|
RestartData=realloc(RestartData,(++RestartCount+1) * sizeof(void *));
|
|
RestartData[RestartCount] = RestartRec;
|
|
|
|
FreeSemaphore(&AllocSemaphore);
|
|
}
|
|
|
|
RestartRec->UserPointer = conn->UserPointer;
|
|
RestartRec->TempMsg = conn->TempMsg;
|
|
RestartRec->MailBuffer = conn->MailBuffer;
|
|
RestartRec->MailBufferSize = conn->MailBufferSize;
|
|
|
|
len = sprintf_s(Msg, sizeof(Msg), "Disconnect received from %s during Binary Transfer - %d Bytes Saved for restart",
|
|
conn->Callsign, conn->TempMsg->length);
|
|
|
|
WriteLogLine(conn, '|',Msg, len, LOG_BBS);
|
|
}
|
|
|
|
BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader)
|
|
{
|
|
int i, n;
|
|
|
|
struct FBBRestartData * RestartRec;
|
|
|
|
if ((conn->BBSFlags & FBBB1Mode) == 0)
|
|
return FALSE; // Only B1 & B2 support restart
|
|
|
|
for (i = 1; i <= RestartCount; i++)
|
|
{
|
|
RestartRec = RestartData[i];
|
|
|
|
if ((RestartRec->UserPointer == conn->UserPointer)
|
|
&& (strcmp(RestartRec->TempMsg->bid, FBBHeader->BID) == 0))
|
|
{
|
|
char Msg[120];
|
|
int len;
|
|
|
|
RestartRec->Count++;
|
|
|
|
if (RestartRec->Count > 3)
|
|
{
|
|
len = sprintf_s(Msg, sizeof(Msg), "Too many restarts for %s - Requesting restart from beginning",
|
|
FBBHeader->BID);
|
|
|
|
WriteLogLine(conn, '|',Msg, len, LOG_BBS);
|
|
|
|
// Remove restrt data
|
|
|
|
for (n = i; n < RestartCount; n++)
|
|
{
|
|
RestartData[n] = RestartData[n+1]; // move down all following entries
|
|
}
|
|
|
|
RestartCount--;
|
|
return FALSE;
|
|
}
|
|
|
|
len = sprintf_s(Msg, sizeof(Msg), "Restart Data found for %s - Requesting restart from %d",
|
|
FBBHeader->BID, RestartRec->TempMsg->length);
|
|
|
|
WriteLogLine(conn, '|',Msg, len, LOG_BBS);
|
|
|
|
return (RestartRec->TempMsg->length);
|
|
}
|
|
}
|
|
|
|
return FALSE; // Not Found
|
|
}
|
|
|
|
|
|
|
|
BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader)
|
|
{
|
|
struct MsgInfo * Msg;
|
|
BIDRec * BID;
|
|
int m;
|
|
|
|
if (RefuseBulls && FBBHeader->MsgType == 'B')
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by RefuseBulls");
|
|
return FALSE;
|
|
}
|
|
if (FBBHeader->Size > MaxRXSize)
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by Size Check");
|
|
return FALSE;
|
|
}
|
|
|
|
BID = LookupBID(FBBHeader->BID);
|
|
|
|
if (BID)
|
|
{
|
|
if (FBBHeader->MsgType == 'B')
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check");
|
|
return FALSE;
|
|
}
|
|
|
|
// Treat P messages to SYSOP@WW as Bulls
|
|
|
|
if (strcmp(FBBHeader->To, "SYSOP") == 0 && strcmp(FBBHeader->ATBBS, "WW") == 0)
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check");
|
|
return FALSE;
|
|
}
|
|
|
|
m = NumberofMessages;
|
|
|
|
while (m > 0)
|
|
{
|
|
Msg = MsgHddrPtr[m];
|
|
|
|
if (Msg->number == BID->u.msgno)
|
|
{
|
|
// if the same TO we will assume the same message
|
|
|
|
if (strcmp(Msg->to, FBBHeader->To) == 0)
|
|
{
|
|
// We have this message. If we have already forwarded it, we should accept it again
|
|
|
|
if ((Msg->status == 'N') || (Msg->status == 'Y')|| (Msg->status == 'H'))
|
|
{
|
|
Logprintf(LOG_BBS, conn, '?', "Message Rejected by BID Check");
|
|
return FALSE; // Dont want it
|
|
}
|
|
else
|
|
return TRUE; // Get it again
|
|
}
|
|
|
|
// Same number. but different message (why?) Accept for now
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
m--;
|
|
}
|
|
|
|
return TRUE; // A personal Message we have had before, but don't still have.
|
|
}
|
|
else
|
|
{
|
|
// We don't know the BID
|
|
|
|
return TRUE; // We want it
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|