diff --git a/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base b/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base
new file mode 100644
index 0000000..78e8a98
--- /dev/null
+++ b/.svn/pristine/10/102d41bc4cd0f242aaba204301966dc699e1ff06.svn-base
@@ -0,0 +1,2179 @@
+/*
+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"
+
+#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__)
+void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line);
+
+
+void DeleteRestartData(CIRCUIT * conn);
+
+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[];
+
+char RestartDir[MAX_PATH] = "";
+
+void GetRestartData()
+{
+ int i;
+ struct FBBRestartData Restart;
+ struct FBBRestartData * RestartRec;
+ char MsgFile[MAX_PATH];
+ FILE * hFile;
+ int FileSize;
+ struct stat STAT;
+ size_t ReadLen = 0;
+ time_t Age;
+
+ strcpy(RestartDir, MailDir);
+ strcat(RestartDir, "/Restart");
+
+ // Make sure RestartDir exists
+
+#ifdef WIN32
+ CreateDirectory(RestartDir, NULL); // Just in case
+#else
+ mkdir(RestartDir, S_IRWXU | S_IRWXG | S_IRWXO);
+ chmod(RestartDir, S_IRWXU | S_IRWXG | S_IRWXO);
+#endif
+
+ // look for restart files. These will be numbered from 1 up
+
+ for (i = 1; 1; i++)
+ {
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, i);
+
+ if (stat(MsgFile, &STAT) == -1)
+ break;
+
+ FileSize = STAT.st_size;
+
+ Age = time(NULL) - STAT.st_ctime;
+
+ if (Age > 86400 * 2) // Max 2 days
+ continue;
+
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == NULL)
+ break;
+
+ // Read Restart Record
+
+ fread(&Restart, 1, sizeof(struct FBBRestartData), hFile);
+
+ if ((Restart.MailBufferSize + sizeof(struct FBBRestartData)) != FileSize) // Duff file
+ {
+ fclose(hFile);
+ break;
+ }
+
+ RestartRec = zalloc(sizeof (struct FBBRestartData));
+
+ GetSemaphore(&AllocSemaphore, 0);
+
+ RestartData = realloc(RestartData,(++RestartCount+1) * sizeof(void *));
+ RestartData[RestartCount] = RestartRec;
+
+ FreeSemaphore(&AllocSemaphore);
+
+ memcpy(RestartRec, &Restart, sizeof(struct FBBRestartData));
+ RestartRec->MailBuffer = malloc(RestartRec->MailBufferSize);
+ ReadLen = fread(RestartRec->MailBuffer, 1, RestartRec->MailBufferSize, hFile);
+
+ Logprintf(LOG_BBS, 0, '?', "Restart Data for %s %s Len %d Loaded", RestartRec->Call, RestartRec->bid, RestartRec->length);
+ fclose(hFile);
+ }
+}
+
+
+void SaveRestartData()
+{
+ // Save restart data to file so we can reload on restart
+ // Restart data has pointers to buffers so we must save copy of data and reconstitue on restart
+
+ // Delete and resave all restart data to keep restart directory clean
+
+ int i, n = 1;
+ char MsgFile[MAX_PATH];
+ FILE * hFile;
+ size_t WriteLen=0;
+ struct FBBRestartData * RestartRec = NULL;
+ struct stat STAT;
+ time_t NOW = time(NULL);
+
+
+ for (i = 1; 1; i++)
+ {
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, i);
+
+ if (stat(MsgFile, &STAT) == -1)
+ break;
+
+ DeleteFile(MsgFile);
+ }
+
+ for (i = 1; i <= RestartCount; i++)
+ {
+ RestartRec = RestartData[i];
+
+ if (RestartRec == 0)
+ return; // Shouldn't happen!
+
+ if ((NOW - RestartRec->TimeCreated) > 86400 * 2) // Max 2 days
+ continue;
+
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/%d", RestartDir, n++);
+
+ hFile = fopen(MsgFile, "wb");
+
+ if (hFile)
+ {
+ WriteLen = fwrite(RestartRec, 1, sizeof(struct FBBRestartData), hFile); // Save Header
+ WriteLen = fwrite(RestartRec->MailBuffer, 1, RestartRec->MailBufferSize, hFile); // Save Data
+ fclose(hFile);
+ }
+ }
+}
+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 ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0)
+ && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0))
+ {
+ if (RestartRec->length <= offset)
+ {
+ conn->TempMsg->length = RestartRec->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--;
+ SaveRestartData();
+ }
+ }
+
+ // 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
+ DeleteRestartData(conn);
+ 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 ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0)
+ && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0))
+ {
+ // Found it, so reuse
+
+ // If we have more data, reset retry count
+
+ if (RestartRec->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->TimeCreated = time(NULL);
+ }
+
+ strcpy(RestartRec->Call, conn->UserPointer->Call);
+ RestartRec->length = conn->TempMsg->length;
+ strcpy(RestartRec->bid, conn->TempMsg->bid);
+ 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);
+
+ SaveRestartData();
+
+ WriteLogLine(conn, '|',Msg, len, LOG_BBS);
+}
+
+void DeleteRestartData(CIRCUIT * conn)
+{
+ struct FBBRestartData * RestartRec = NULL;
+ int i, n;
+
+ if (conn->TempMsg == NULL)
+ return;
+
+ for (i = 1; i <= RestartCount; i++)
+ {
+ RestartRec = RestartData[i];
+
+ if ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0)
+ && (strcmp(RestartRec->bid, conn->TempMsg->bid) == 0))
+ {
+ // Remove restrt data
+
+ for (n = i; n < RestartCount; n++)
+ {
+ RestartData[n] = RestartData[n+1]; // move down all following entries
+ }
+
+ RestartCount--;
+ SaveRestartData();
+ return;
+ }
+ }
+}
+
+
+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 ((strcmp(RestartRec->Call, conn->UserPointer->Call) == 0)
+ && (strcmp(RestartRec->bid, FBBHeader->BID) == 0))
+ {
+ char Msg[120];
+ int len;
+
+ RestartRec->Count++;
+
+ if (RestartRec->Count > 10)
+ {
+ 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--;
+ SaveRestartData();
+ return FALSE;
+ }
+
+ len = sprintf_s(Msg, sizeof(Msg), "Restart Data found for %s - Requesting restart from %d",
+ FBBHeader->BID, RestartRec->length);
+
+ WriteLogLine(conn, '|',Msg, len, LOG_BBS);
+
+ return (RestartRec->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
+ }
+}
+
+
+
+
diff --git a/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base b/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base
new file mode 100644
index 0000000..3ea8f94
--- /dev/null
+++ b/.svn/pristine/11/1105d6155b0f09c6edbdbc765bbd700d7d4d582a.svn-base
@@ -0,0 +1,402 @@
+/*
+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
+*/
+
+//
+// C replacement for TNCCode.asm
+//
+#define Kernel
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+#pragma data_seg("_BPQDATA")
+
+#include "time.h"
+#include "stdio.h"
+#include Sorry, User or Password is invalid - please try again Sorry, No sessions available - please try later "
+ "Select Required Template from %sKAM Pactor Status
");
+
+ Len += sprintf(&Buff[Len], "");
+
+ Len += sprintf(&Buff[Len], "
");
+
+ Len += sprintf(&Buff[Len], "", TNC->WebBuffer);
+ Len = DoScanLine(TNC, Buff, Len);
+
+ return Len;
+}
+
+
+void * KAMExtInit(EXTPORTDATA * PortEntry)
+{
+ char msg[500];
+ struct TNCINFO * TNC;
+ int port;
+ char * ptr;
+ char * TempScript;
+
+ port=PortEntry->PORTCONTROL.PORTNUMBER;
+
+ sprintf(msg,"KAM Pactor %s", PortEntry->PORTCONTROL.SerialPortName);
+ WritetoConsole(msg);
+
+ 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\n");
+ WritetoConsole(msg);
+
+ return ExtProc;
+ }
+ TNC->Port = port;
+ TNC->PortRecord = PortEntry;
+
+ TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KAM;
+
+ if (TNC->BusyWait == 0)
+ TNC->BusyWait = 10;
+
+ PortEntry->MAXHOSTMODESESSIONS = 11; // Default
+
+
+ if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
+ memcpy(TNC->NodeCall, MYNODECALL, 10);
+ else
+ ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
+
+ PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor
+ PortEntry->PORTCONTROL.PORTQUALITY = 0;
+ PortEntry->SCANCAPABILITIES = NONE; // No Scan Control
+
+ if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
+ TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
+
+ if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
+ PortEntry->PORTCONTROL.PORTPACLEN = 100;
+
+ PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort;
+ PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort;
+
+// TNC->SuspendPortProc = KAMSuspendPort;
+// TNC->ReleasePortProc = KAMReleasePort;
+
+
+
+ ptr=strchr(TNC->NodeCall, ' ');
+ if (ptr) *(ptr) = 0; // Null Terminate
+
+ // Set Essential Params and MYCALL
+
+ TempScript = malloc(4000);
+
+ strcpy(TempScript, "MARK 1400\r");
+ strcat(TempScript, "SPACE 1600\r");
+ strcat(TempScript, "SHIFT MODEM\r");
+ strcat(TempScript, "INV ON\r");
+ strcat(TempScript, "PTERRS 30\r"); // Default Retries
+ strcat(TempScript, "MAXUSERS 1/10\r");
+ strcat(TempScript, TNC->InitScript);
+
+ free(TNC->InitScript);
+ TNC->InitScript = TempScript;
+
+ // Others go on end so they can't be overriden
+
+ strcat(TNC->InitScript, "ECHO OFF\r");
+ strcat(TNC->InitScript, "XMITECHO ON\r");
+ strcat(TNC->InitScript, "TXFLOW OFF\r");
+ strcat(TNC->InitScript, "XFLOW OFF\r");
+ strcat(TNC->InitScript, "TRFLOW OFF\r");
+ strcat(TNC->InitScript, "AUTOCR 0\r");
+ strcat(TNC->InitScript, "AUTOLF OFF\r");
+ strcat(TNC->InitScript, "CRADD OFF\r");
+ strcat(TNC->InitScript, "CRSUP OFF\r");
+ strcat(TNC->InitScript, "CRSUP OFF/OFF\r");
+ strcat(TNC->InitScript, "LFADD OFF/OFF\r");
+ strcat(TNC->InitScript, "LFADD OFF\r");
+ strcat(TNC->InitScript, "LFSUP OFF/OFF\r");
+ strcat(TNC->InitScript, "LFSUP OFF\r");
+ strcat(TNC->InitScript, "RING OFF\r");
+ strcat(TNC->InitScript, "ARQBBS OFF\r");
+
+ // Set the ax.25 MYCALL
+
+
+ sprintf(msg, "MYCALL %s/%s\r", TNC->NodeCall, TNC->NodeCall);
+ strcat(TNC->InitScript, msg);
+
+ // look for the MAXUSERS config line, and get the limits
+
+ TNC->InitScript = _strupr(TNC->InitScript);
+
+ ptr = strstr(TNC->InitScript, "MAXUSERS");
+
+ if (ptr)
+ {
+ ptr = strchr(ptr,'/'); // to the separator
+ if (ptr)
+ PortEntry->MAXHOSTMODESESSIONS = atoi(++ptr) + 1;
+ }
+
+ if (PortEntry->MAXHOSTMODESESSIONS > 26)
+ PortEntry->MAXHOSTMODESESSIONS = 26;
+
+ 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->WebBuffer = zalloc(5000);
+
+#ifndef LINBPQ
+
+ CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 500, ForcedClose);
+
+
+ CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL);
+
+ CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL);
+
+ CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL);
+
+ CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);
+
+ CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);
+
+ CreateWindowEx(0, "STATIC", "Free Space", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);
+
+ CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL);
+ TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL);
+
+ TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
+ LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
+ 0,RigControlRow + 44,250,300, TNC->hDlg, NULL, hInstance, NULL);
+
+ TNC->ClientHeight = 500;
+ TNC->ClientWidth = 500;
+
+ MoveWindows(TNC);
+#endif
+ OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE);
+
+ WritetoConsole("\n");
+
+ return ExtProc;
+}
+
+
+
+void CheckRXKAM(struct TNCINFO * TNC)
+{
+ int Length, Len;
+ char debug[512] = "RX: ";
+
+ // 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;
+
+ memcpy(&debug[4], TNC->RXBuffer, TNC->RXLen);
+ debug[TNC->RXLen + 4] = 0;
+ WriteLogLine(TNC->Port, debug, TNC->RXLen + 4);
+
+ Length = TNC->RXLen;
+
+ // If first char != FEND, then probably a Terminal Mode Frame. Wait for CR on end
+
+ if (TNC->RXBuffer[0] != FEND)
+ {
+ // Char Mode Frame I think we need to see cmd: on end
+
+ // If we think we are in host mode, then to could be noise - just discard.
+
+ if (TNC->HostMode)
+ {
+ TNC->RXLen = 0; // Ready for next frame
+ return;
+ }
+
+ TNC->RXBuffer[TNC->RXLen] = 0;
+
+// if (TNC->RXBuffer[TNC->RXLen-2] != ':')
+ if (strstr(TNC->RXBuffer, "cmd:") == 0)
+ return; // Wait for rest of frame
+
+ // Complete Char Mode Frame
+
+ TNC->RXLen = 0; // Ready for next frame
+
+ if (TNC->HostMode == 0)
+ {
+ // We think TNC is in Terminal Mode
+ ProcessTermModeResponse(TNC);
+ return;
+ }
+ // We thought it was in Host Mode, but are wrong.
+
+ TNC->HostMode = FALSE;
+ return;
+ }
+
+ // Receiving a Host Mode frame
+
+ if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery
+ {
+ TNC->RXLen = 0; // Ready for next frame
+ return;
+ }
+
+ if (Length < 3) // Minimum Frame Sise
+ return;
+
+ if (TNC->RXBuffer[Length-1] != FEND)
+ return; // Wait till we have a full frame
+
+ ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer
+
+ TNC->RXLen = 0; // Ready for next frame
+
+
+ return;
+
+}
+
+VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len)
+{
+ UCHAR * FendPtr;
+ int NewLen;
+
+ // Split into KISS Packets. By far the most likely is a single KISS frame
+ // so treat as special case
+
+ if (rxbuffer[1] == FEND) // Two FENDS - probably got out of sync
+ {
+ rxbuffer++;
+ Len--;
+ }
+
+ FendPtr = memchr(&rxbuffer[1], FEND, Len-1);
+
+ if (FendPtr == &rxbuffer[Len-1])
+ {
+ ProcessKHOSTPacket(TNC, &rxbuffer[1], Len - 2);
+ return;
+ }
+
+ // Process the first Packet in the buffer
+
+ NewLen = (int)(FendPtr - rxbuffer - 1);
+
+ ProcessKHOSTPacket(TNC, &rxbuffer[1], NewLen);
+
+ // Loop Back
+
+ ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2);
+ return;
+
+}
+
+
+
+static BOOL WriteCommBlock(struct TNCINFO * TNC)
+{
+ char debug[512] = "TX: ";
+
+ memcpy(&debug[4], TNC->TXBuffer, TNC->TXLen);
+ debug[TNC->TXLen + 4] = 0;
+
+ WriteLogLine(TNC->Port, debug, TNC->TXLen + 4);
+
+ WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen);
+
+ return TRUE;
+}
+
+VOID KAMPoll(int Port)
+{
+ struct TNCINFO * TNC = TNCInfo[Port];
+ struct STREAMINFO * STREAM;
+
+ UCHAR * Poll = TNC->TXBuffer;
+ char Status[80];
+ int Stream;
+
+ if (TNC->PortRecord == 0)
+ Stream = 0;
+
+
+ // If Pactor Session has just been attached, drop back to cmd mode and set Pactor Call to
+ // the connecting user's callsign
+
+ for (Stream = 0; Stream <= MaxStreams; Stream++)
+ {
+ STREAM = &TNC->Streams[Stream];
+
+ if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0)
+ {
+ // New Attach
+
+ STREAM->Attached = TRUE;
+
+ if (Stream == 0) // HF Port
+ {
+ int calllen;
+ UCHAR TXMsg[1000] = "D20";
+ int datalen;
+ char Msg[80];
+
+ TNC->HFPacket = FALSE;
+ TNC->TimeInRX = 0;
+
+ calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall);
+ TNC->Streams[0].MyCall[calllen] = 0;
+
+ EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
+ if (TNC->VeryOldMode)
+ datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall);
+ else
+ datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall);
+ EncodeAndSend(TNC, TXMsg, datalen);
+ TNC->InternalCmd = 'M';
+
+ TNC->NeedPACTOR = 0; // Cancel enter Pactor
+
+ sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
+ SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
+
+ // Stop Scanning
+
+ sprintf(Msg, "%d SCANSTOP", TNC->Port);
+
+ Rig_Command( (TRANSPORTENTRY *) -1, Msg);
+
+ }
+ }
+ }
+
+ if (TNC->Timeout)
+ {
+ TNC->Timeout--;
+
+ if (TNC->Timeout) // Still waiting
+ return;
+
+ // Timed Out
+
+ if (TNC->HostMode == 0)
+ {
+ DoTermModeTimeout(TNC);
+ return;
+ }
+
+ // Timed out in host mode - Clear any connection and reinit the TNC
+
+ Debugprintf("KAM PACTOR - Link to TNC Lost");
+ TNC->TNCOK = FALSE;
+ TNC->HostMode = 0;
+ TNC->ReinitState = 0;
+
+ sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName);
+ SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
+
+ for (Stream = 0; Stream <= MaxStreams; Stream++)
+ {
+ PMSGWITHLEN buffptr;
+
+ STREAM = &TNC->Streams[Stream];
+
+ if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected
+ {
+ STREAM->Connected = FALSE; // Back to Command Mode
+ STREAM->ReportDISC = TRUE; // Tell Node
+ }
+
+ STREAM->FramesQueued = 0;
+
+ while(STREAM->BPQtoPACTOR_Q)
+ {
+ buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
+ ReleaseBuffer(buffptr);
+ }
+
+ while(STREAM->PACTORtoBPQ_Q)
+ {
+ buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);
+ ReleaseBuffer(buffptr);
+ }
+ }
+ }
+
+ for (Stream = 0; Stream <= MaxStreams; Stream++)
+ {
+ STREAM = &TNC->Streams[Stream];
+
+ if (STREAM->Attached)
+ CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);
+
+ }
+
+ // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence
+
+ if (!TNC->HostMode)
+ {
+ DoTNCReinit(TNC);
+ return;
+ }
+
+ if (TNC->BusyDelay) // Waiting to send connect
+ {
+ // Still Busy?
+
+ if (InterlockedCheckBusy(TNC) == 0)
+ {
+ // No, so send
+
+ EncodeAndSend(TNC, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd));
+ free(TNC->ConnectCmd);
+
+ TNC->Timeout = 50;
+ TNC->InternalCmd = 'C'; // So we dont send the reply to the user.
+ STREAM->Connecting = TRUE;
+
+ TNC->Streams[0].Connecting = TRUE;
+
+ sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
+ SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
+
+ TNC->BusyDelay = 0;
+ return;
+ }
+ else
+ {
+ // Wait Longer
+
+ TNC->BusyDelay--;
+
+ if (TNC->BusyDelay == 0)
+ {
+ // Timed out - Send Error Response
+
+ PMSGWITHLEN buffptr = GetBuff();
+
+ if (buffptr == 0) return; // No buffers, so ignore
+
+ buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r");
+
+ C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
+
+ free(TNC->ConnectCmd);
+
+ }
+ }
+ }
+
+ if (TNC->NeedPACTOR)
+ {
+ TNC->NeedPACTOR--;
+
+ if (TNC->NeedPACTOR == 0)
+ {
+ int datalen;
+ UCHAR TXMsg[80] = "D20";
+
+ if (TNC->VeryOldMode)
+ datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall);
+ else
+ datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall);
+ EncodeAndSend(TNC, TXMsg, datalen);
+
+ if (TNC->OldMode)
+ EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen
+ else
+ EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen
+
+ TNC->InternalCmd = 'T';
+ TNC->Timeout = 50;
+ TNC->IntCmdDelay--;
+
+ // Restart Scanning
+
+ sprintf(Status, "%d SCANSTART 15", TNC->Port);
+
+ Rig_Command( (TRANSPORTENTRY *) -1, Status);
+
+ return;
+ }
+ }
+
+ for (Stream = 0; Stream <= MaxStreams; Stream++)
+ {
+ STREAM = &TNC->Streams[Stream];
+
+ // If in HF Packet mode, normal flow control doesn't seem to work
+ // If more that 4 packets sent, send a status poll. and use response to
+ // reset frames outstanding
+
+ if ((Stream == 0) && (TNC->HFPacket) && (TNC->Streams[0].FramesOutstanding > 4))
+ {
+ EncodeAndSend(TNC, "C10S", 4);
+ TNC->InternalCmd = 'S';
+ TNC->Timeout = 50;
+ return;
+
+ }
+
+ if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q)
+ {
+ int datalen;
+ UCHAR TXMsg[1000] = "D20";
+ PMSGWITHLEN buffptr;
+ UCHAR * MsgPtr;
+ char Status[80];
+
+ if (STREAM->Connected)
+ {
+ int Next;
+
+ if (Stream > 0)
+ sprintf(TXMsg, "D1%c", Stream + '@');
+ else if (TNC->HFPacket)
+ memcpy(TXMsg, "D2A", 3);
+ else
+ {
+ // Pactor
+
+ // Limit amount in TX, so we keep some on the TX Q and don't send turnround too early
+
+ if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200)
+ continue;
+
+ // Dont send if IRS State
+ // If in IRS state for too long, force turnround
+
+ if (TNC->TXRXState == 'R')
+ {
+ if (TNC->TimeInRX++ > 15)
+ EncodeAndSend(TNC, "T", 1); // Changeover to ISS
+ else
+ goto Poll;
+ }
+ TNC->TimeInRX = 0;
+ }
+
+ buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
+ STREAM->FramesQueued--;
+
+ datalen = buffptr->Len;
+ MsgPtr = buffptr->Data;
+
+ if (TNC->SwallowSignon && Stream == 0)
+ {
+ TNC->SwallowSignon = FALSE;
+ if (strstr(MsgPtr, "Connected")) // Discard *** connected
+ {
+ ReleaseBuffer(buffptr);
+ return;
+ }
+ }
+
+ Next = 0;
+ STREAM->bytesTXed += datalen;
+
+ if (Stream == 0)
+ {
+ while (datalen > 100) // Limit Pactor Sends
+ {
+ memcpy(&TXMsg[3], &MsgPtr[Next], 100);
+ EncodeAndSend(TNC, TXMsg, 103);
+ Next += 100;
+ datalen -= 100;
+
+ WritetoTrace(TNC, &TXMsg[3], 100);
+ }
+ }
+
+ memcpy(&TXMsg[3], &MsgPtr[Next], datalen);
+ EncodeAndSend(TNC, TXMsg, datalen + 3);
+
+ WritetoTrace(TNC, &TXMsg[3], datalen);
+
+ ReleaseBuffer(buffptr);
+
+ if (Stream == 0)
+ {
+ sprintf(Status, "RX %d TX %d ACKED %d ",
+ TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
+ SetWindowText(TNC->xIDC_TRAFFIC, Status);
+
+ if ((TNC->HFPacket == 0) && (TNC->Streams[0].BPQtoPACTOR_Q == 0)) // Nothing following
+ {
+ EncodeAndSend(TNC, "E", 1); // Changeover when all sent
+ }
+ }
+
+ if (STREAM->Disconnecting)
+ {
+ Debugprintf("Send with Disc Pending, Q = %x", STREAM->BPQtoPACTOR_Q);
+ if (STREAM->BPQtoPACTOR_Q == 0) // All Sent
+
+ // KAM doesnt have a tidy close!
+
+ STREAM->DisconnectingTimeout = 100; // Give 5 secs to get to other end
+ }
+ return;
+ }
+ else // Not Connected
+ {
+ buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
+ datalen = buffptr->Len;
+ MsgPtr = buffptr->Data;
+
+ // Command. Do some sanity checking and look for things to process locally
+
+ datalen--; // Exclude CR
+ MsgPtr[datalen] = 0; // Null Terminate
+ _strupr(MsgPtr);
+
+ if ((Stream == 0) && 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(buffptr->Data, "%s", &MsgPtr[40]);
+ C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
+ }
+ return;
+ }
+
+ if (_memicmp(MsgPtr, "D\r", 2) == 0)
+ {
+ STREAM->ReportDISC = TRUE; // Tell Node
+ return;
+ }
+
+ if ((Stream == 0) && memcmp(MsgPtr, "HFPACKET", 8) == 0)
+ {
+ TNC->HFPacket = TRUE;
+ buffptr->Len = sprintf(buffptr->Data, "KAM} 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);
+ STREAM->Connecting = TRUE;
+
+ // If Stream 0, Convert C CALL to PACTOR CALL
+
+ if (Stream == 0)
+ {
+ if (TNC->HFPacket)
+ datalen = sprintf(TXMsg, "C2AC %s", TNC->Streams[0].RemoteCall);
+ else
+ datalen = sprintf(TXMsg, "C20PACTOR %s", TNC->Streams[0].RemoteCall);
+
+ // If Pactor, check busy detecters on any interlocked ports
+
+ if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0)
+ {
+ // Channel Busy. Wait
+
+ TNC->ConnectCmd = _strdup(TXMsg);
+
+ sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
+ SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
+
+ TNC->BusyDelay = TNC->BusyWait * 10;
+
+ return;
+ }
+
+ TNC->OverrideBusy = FALSE;
+
+ sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s",
+ TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
+ SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
+ }
+ else
+ datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall);
+
+ EncodeAndSend(TNC, TXMsg, datalen);
+ TNC->Timeout = 50;
+ TNC->InternalCmd = 'C'; // So we dont send the reply to the user.
+ ReleaseBuffer(buffptr);
+ STREAM->Connecting = TRUE;
+
+ return;
+ }
+
+ if (memcmp(MsgPtr, "GTOR ", 5) == 0) // GTOR Connect
+ {
+ memcpy(STREAM->RemoteCall, &MsgPtr[5], 9);
+ STREAM->Connecting = TRUE;
+
+ // If Stream 0, Convert C CALL to PACTOR CALL
+
+ if (Stream == 0)
+ {
+ datalen = sprintf(TXMsg, "C20GTOR %s", TNC->Streams[0].RemoteCall);
+
+ // If Pactor, check busy detecters on any interlocked ports
+
+ if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0)
+ {
+ // Channel Busy. Wait
+
+ TNC->ConnectCmd = _strdup(TXMsg);
+
+ sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
+ SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
+
+ TNC->BusyDelay = TNC->BusyWait * 10;
+
+ return;
+ }
+
+ TNC->OverrideBusy = FALSE;
+
+ sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s",
+ TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
+ SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
+ }
+ else
+ datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall);
+
+ EncodeAndSend(TNC, TXMsg, datalen);
+ TNC->Timeout = 50;
+ TNC->InternalCmd = 'C'; // So we dont send the reply to the user.
+ ReleaseBuffer(buffptr);
+ STREAM->Connecting = TRUE;
+
+ return;
+ }
+
+ if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect
+ {
+ if (Stream == 0)
+ {
+ if (TNC->HFPacket)
+ EncodeAndSend(TNC, "C2AD", 4); // ??Return to packet mode??
+ else
+ EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
+
+ TNC->NeedPACTOR = 50;
+ }
+ else
+ {
+ sprintf(TXMsg, "C1%cD", Stream + '@');
+ EncodeAndSend(TNC, TXMsg, 4);
+ TNC->CmdStream = Stream;
+ TNC->Timeout = 50;
+ }
+
+ TNC->Timeout = 0; // Don't expect a response
+ STREAM->Connecting = FALSE;
+ STREAM->ReportDISC = TRUE;
+ ReleaseBuffer(buffptr);
+
+ return;
+ }
+
+ // Other Command ??
+
+ if (Stream > 0)
+ datalen = sprintf(TXMsg, "C1%c%s", Stream + '@', MsgPtr);
+ else
+ datalen = sprintf(TXMsg, "C20%s", MsgPtr);
+ EncodeAndSend(TNC, TXMsg, datalen);
+ ReleaseBuffer(buffptr);
+ TNC->Timeout = 50;
+ TNC->InternalCmd = 0;
+ TNC->CmdStream = Stream;
+
+ }
+ }
+ }
+
+Poll:
+
+ // Need to poll data and control channel (for responses to commands)
+
+ // Also check status if we have data buffered (for flow control)
+
+ if (TNC->TNCOK)
+ {
+ if (TNC->IntCmdDelay == 50)
+ {
+ EncodeAndSend(TNC, "C10S", 4);
+ TNC->InternalCmd = 'S';
+ TNC->Timeout = 50;
+ TNC->IntCmdDelay--;
+ return;
+ }
+
+ if (TNC->IntCmdDelay <=0)
+ {
+ if (TNC->VeryOldMode == FALSE)
+ {
+ EncodeAndSend(TNC, "?", 1);
+ TNC->InternalCmd = '?';
+ TNC->Timeout = 50;
+ }
+ TNC->IntCmdDelay = 100; // Every 30
+ return;
+ }
+ else
+ TNC->IntCmdDelay--;
+ }
+
+ return;
+
+}
+
+static VOID DoTNCReinit(struct TNCINFO * TNC)
+{
+ UCHAR * Poll = TNC->TXBuffer;
+
+ if (TNC->ReinitState == 1) // Forcing back to Term
+ TNC->ReinitState = 0; // Got Response, so must be back in term mode
+
+ if (TNC->ReinitState == 0)
+ {
+ // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode
+
+ sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName);
+ SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
+
+ Poll[0] = 13;
+ TNC->TXLen = 1;
+
+ WriteCommBlock(TNC);
+ TNC->Timeout = 50;
+
+ return;
+ }
+
+ if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands
+ {
+ char * start, * end;
+ int len;
+
+ start = TNC->InitPtr;
+
+ if (*(start) == 0) // End of Script
+ {
+ // Put into Host Mode
+
+ memcpy(Poll, "INTFACE HOST\r", 13);
+
+ TNC->TXLen = 13;
+ WriteCommBlock(TNC);
+ TNC->Timeout = 50;
+
+ TNC->ReinitState = 4; // Need Reset
+
+ return;
+ }
+
+ end = strchr(start, 13);
+ len = (int)(++end - start);
+ TNC->InitPtr = end;
+ memcpy(Poll, start, len);
+
+ TNC->TXLen = len;
+ WriteCommBlock(TNC);
+ TNC->Timeout = 50;
+
+ return;
+
+ }
+}
+
+static VOID DoTermModeTimeout(struct TNCINFO * TNC)
+{
+ UCHAR * Poll = TNC->TXBuffer;
+
+ if (TNC->ReinitState == 0)
+ {
+ //Checking if in Terminal Mode - Try to set back to Term Mode
+
+ TNC->ReinitState = 1;
+ Poll[0] = 3;
+ Poll[1] = 0x58; // ?? Back to cmd: mode ??
+ TNC->TXLen = 2;
+
+ Poll[0] = 0xc0;
+ Poll[1] = 'Q'; // ?? Back to cmd: mode ??
+ Poll[2] = 0xc0;
+ TNC->TXLen = 3;
+
+ WriteCommBlock(TNC);
+
+ return;
+ }
+ if (TNC->ReinitState == 1)
+ {
+ // Forcing back to Term Mode
+
+ TNC->ReinitState = 0;
+ DoTNCReinit(TNC); // See if worked
+ return;
+ }
+
+ if (TNC->ReinitState == 3)
+ {
+ // Entering Host Mode
+
+ // Assume ok
+
+ TNC->HostMode = TRUE;
+ return;
+ }
+}
+
+
+
+static VOID ProcessTermModeResponse(struct TNCINFO * TNC)
+{
+ UCHAR * Poll = TNC->TXBuffer;
+
+ if (TNC->ReinitState == 0 || TNC->ReinitState == 1)
+ {
+ // Testing if in Term Mode. It is, so can now send Init Commands
+
+ TNC->InitPtr = TNC->InitScript;
+ TNC->ReinitState = 2;
+ DoTNCReinit(TNC); // Send First Command
+ return;
+ }
+ if (TNC->ReinitState == 2)
+ {
+ // Sending Init Commands
+
+ DoTNCReinit(TNC); // Send Next Command
+ return;
+ }
+
+ if (TNC->ReinitState == 4) // Send INTFACE, Need RESET
+ {
+ TNC->ReinitState = 5;
+
+ memcpy(Poll, "RESET\r", 6);
+
+ TNC->TXLen = 6;
+ WriteCommBlock(TNC);
+ TNC->Timeout = 50;
+ TNC->HostMode = TRUE; // Should now be in Host Mode
+ TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs
+
+ return;
+ }
+
+ if (TNC->ReinitState == 5) // RESET sent
+ {
+ TNC->ReinitState = 5;
+
+ return;
+ }
+
+
+
+}
+
+VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len)
+{
+ PMSGWITHLEN buffptr;
+ char * Buffer = &Msg[3]; // Data portion of frame
+ char * Call;
+ char Status[80];
+ int Stream = 0;
+ struct STREAMINFO * STREAM;
+
+ // Any valid frame is an ACK
+
+ TNC->TNCOK = TRUE;
+
+ Len = KissDecode(Msg, Msg, Len); // Remove KISS transparency
+
+ if (Msg[1] == '0' && Msg[2] == '0')
+ Stream = 0;
+ else
+ if (Msg[1] == '2') Stream = 0; else Stream = Msg[2] - '@';
+
+ STREAM = &TNC->Streams[Stream];
+
+ // See if Poll Reply or Data
+
+ Msg[Len] = 0; // Terminate
+
+ if (Msg[0] == 'M') // Monitor
+ {
+ DoMonitor(TNC, Msg, Len);
+ return;
+ }
+
+
+ if (Msg[0] == 'E') // Data Echo
+ {
+ if (Msg[1] == '2') // HF Port
+ {
+ if (TNC->Streams[0].bytesTXed)
+ TNC->Streams[0].BytesAcked += Len - 3; // We get an ack before the first send
+
+ sprintf(Status, "RX %d TX %d ACKED %d ",
+ TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
+ SetWindowText(TNC->xIDC_TRAFFIC, Status);
+
+ if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked < 500)
+ TNC->Streams[0].FramesOutstanding = 0;
+ }
+ return;
+ }
+
+ if (Msg[0] == 'D') // Data
+ {
+ // Pass to Appl
+
+ buffptr = GetBuff();
+ if (buffptr == NULL) return; // No buffers, so ignore
+
+ Len-=3; // Remove Header
+
+ buffptr->Len = Len; // Length
+ STREAM->bytesRXed += Len;
+ memcpy(buffptr->Data, Buffer, Len);
+
+ C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
+
+ if (Stream == 0)
+ {
+ sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ",
+ TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
+ SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
+ }
+
+ WritetoTrace(TNC, Buffer, Len);
+
+ return;
+ }
+
+
+ if (Msg[0] == 'C') // Command Reponse
+ {
+ TNC->Timeout = 0;
+
+ // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc
+
+ // See if a response to internal command
+
+ if (TNC->InternalCmd)
+ {
+ // Process it
+
+ if (TNC->InternalCmd == 'S') // Status
+ {
+ char * Line;
+ char * ptr;
+
+ // Message is line giving free bytes, followed by a line for each active (packet) stream
+
+ // FREE BYTES 1366/5094
+ // A/2 #1145(12) CONNECTED to KE7XO-3
+ // S/2 CONNECTED to NLV
+
+ // each line is teminated by CR, and by the time it gets here it is null terminated
+
+ //FREE BYTES 2628
+ //A/H #80(1) CONNECTED to DK0MNL..
+
+ if (TNC->HFPacket)
+ TNC->Streams[0].FramesOutstanding = 0;
+
+ Line = strchr(&Msg[3], 13);
+ if (Line == 0) return;
+
+ *(Line) = 0;
+
+ ptr = strchr(&Msg[13], '/');
+ TNC->Mem1 = atoi(&Msg[13]);
+ if (ptr)
+ TNC->Mem2 = atoi(++ptr);
+ else
+ TNC->Mem2 = 0;
+
+ SetWindowText(TNC->xIDC_BUFFERS, &Msg[14]);
+ strcpy(TNC->WEB_BUFFERS, &Msg[14]);
+
+ while (Line[1] != 0) // End of stream
+ {
+ Stream = Line[1] - '@';
+ STREAM = &TNC->Streams[Stream];
+
+ if (Line[5] == '#')
+ {
+ STREAM->BytesOutstanding = atoi(&Line[6]);
+ ptr = strchr(&Line[6], '(');
+ if (ptr)
+ STREAM->FramesOutstanding = atoi(++ptr);
+ }
+ else
+ {
+ STREAM->BytesOutstanding = 0;
+ STREAM->FramesOutstanding = 0;
+ }
+
+ Line = strchr(&Line[1], 13);
+ }
+ return;
+ }
+ return;
+ }
+
+
+ WritetoTrace(TNC, Buffer, Len);
+
+
+ // Pass to Appl
+
+ Stream = TNC->CmdStream;
+
+
+ buffptr = GetBuff();
+
+ if (buffptr == NULL) return; // No buffers, so ignore
+
+ buffptr->Len = sprintf(buffptr->Data,"KAM} %s", Buffer);
+
+ C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
+
+ return;
+ }
+
+ if (Msg[0] == 'I') // ISS/IRS State
+ {
+ if (Msg[2] == '1')
+ {
+ strcpy(TNC->WEB_TXRX, "Sender");
+ SetWindowText(TNC->xIDC_TXRX, "Sender");
+ TNC->TXRXState = 'S';
+ }
+ else
+ {
+ strcpy(TNC->WEB_TXRX, "Receiver");
+ SetWindowText(TNC->xIDC_TXRX, "Receiver");
+ TNC->TXRXState = 'R';
+ }
+ return;
+ }
+
+ if (Msg[0] == '?') // Status
+ {
+ TNC->Timeout = 0;
+ return;
+ }
+
+ if (Msg[0] == 'S') // Status
+ {
+ if (Len < 4)
+ {
+ // Reset Response FEND FEND S00 FEND
+
+ TNC->Timeout = 0;
+
+ sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName);
+ SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
+
+ return;
+ }
+
+ // Pass to Appl
+
+ if (strstr(Buffer, "STANDBY>") || strstr(Buffer, "*** DISCONNECTED"))
+ {
+ 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)
+ {
+ // Connect Failed
+
+ buffptr = GetBuff();
+ if (buffptr == 0) return; // No buffers, so ignore
+
+ 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->FramesOutstanding = 0;
+
+ return;
+ }
+
+ // Must Have been connected or disconnecting - Release Session
+
+ STREAM->Connecting = FALSE;
+ STREAM->Connected = FALSE; // Back to Command Mode
+ STREAM->FramesOutstanding = 0;
+
+ if (STREAM->Disconnecting == FALSE)
+ STREAM->ReportDISC = TRUE; // Tell Node
+
+ STREAM->Disconnecting = FALSE;
+
+ if (Stream == 0)
+ {
+ // Need to reset Pactor Call in case it was changed
+
+ EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
+ TNC->NeedPACTOR = 20;
+ }
+
+ return;
+ }
+
+ if (Msg[2] == '0')
+ Call = strstr(Buffer, " ", TNC->WEB_COMMSSTATE);
+ Len += sprintf(&Buff[Len], "Comms State %s ", TNC->WEB_TNCSTATE);
+ Len += sprintf(&Buff[Len], "TNC State %s ", TNC->WEB_MODE);
+ Len += sprintf(&Buff[Len], "Mode %s ", TNC->WEB_STATE);
+ Len += sprintf(&Buff[Len], "Status %s ", TNC->WEB_TXRX);
+ Len += sprintf(&Buff[Len], "TX/RX State %s ", TNC->WEB_BUFFERS);
+ Len += sprintf(&Buff[Len], "Free Space %s ", TNC->WEB_TRAFFIC);
+ Len += sprintf(&Buff[Len], "Traffic %s BPQ32 Mail Server %s Access
"
+ "Please enter Callsign and Password to access WebMail
"
+ "";
+
+static char MsgInputPage[] = ""
+ "Webmail Interface - Message Input Form
"
+ "";
+
+static char CheckFormMsgPage[] = ""
+ "Webmail Forms Interface - Check Message
"
+ "";
+
+
+extern char * WebMailTemplate;
+extern char * WebMailMsgTemplate;
+extern char * jsTemplate;
+
+static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"};
+
+static struct HTTPConnectionInfo * WebSessionList = NULL; // active WebMail sessions
+
+#ifdef LINBPQ
+UCHAR * GetBPQDirectory();
+#endif
+
+void UndoTransparency(char * input);
+
+#ifndef LINBPQ
+
+void UndoTransparency(char * input)
+{
+ char * ptr1, * ptr2;
+ char c;
+ int hex;
+
+ if (input == NULL)
+ return;
+
+ ptr1 = ptr2 = input;
+
+ // Convert any %xx constructs
+
+ while (1)
+ {
+ c = *(ptr1++);
+
+ if (c == 0)
+ break;
+
+ if (c == '%')
+ {
+ c = *(ptr1++);
+ if(isdigit(c))
+ hex = (c - '0') << 4;
+ else
+ hex = (tolower(c) - 'a' + 10) << 4;
+
+ c = *(ptr1++);
+ if(isdigit(c))
+ hex += (c - '0');
+ else
+ hex += (tolower(c) - 'a' + 10);
+
+ *(ptr2++) = hex;
+ }
+ else if (c == '+')
+ *(ptr2++) = 32;
+ else
+ *(ptr2++) = c;
+ }
+ *ptr2 = 0;
+}
+#endif
+
+void ReleaseWebMailStruct(WebMailInfo * WebMail)
+{
+ // release any malloc'ed resources
+
+ if (WebMail == NULL)
+ return;
+
+ FreeWebMailFields(WebMail);
+ free(WebMail);
+ return;
+}
+
+VOID FreeWebMailMallocs()
+{
+ // called when closing. Not really needed, but simplifies tracking down real memory leaks
+
+ struct HTTPConnectionInfo * Session, * SaveNext;
+ int i;
+ Session = WebSessionList;
+
+ while (Session)
+ {
+ SaveNext = Session->Next;
+
+ // Release amy malloc'ed resouces
+
+ ReleaseWebMailStruct(Session->WebMail);
+ free(Session);
+ Session = SaveNext;
+ }
+
+ for (i = 0; i < FormDirCount; i++)
+ {
+ struct HtmlFormDir * Dir = HtmlFormDirs[i];
+
+ int j;
+
+ for (j = 0; j < Dir->FormCount; j++)
+ {
+ free(Dir->Forms[j]->FileName);
+ free(Dir->Forms[j]);
+ }
+
+ if (Dir->DirCount)
+ {
+ struct HtmlFormDir * SubDir;
+
+ int k, l;
+
+ for (l = 0; l < Dir->DirCount; l++)
+ {
+ SubDir = Dir->Dirs[l];
+
+ for (k = 0; k < Dir->Dirs[l]->FormCount; k++)
+ {
+ free(SubDir->Forms[k]->FileName);
+ free(SubDir->Forms[k]);
+ }
+ free(SubDir->DirName);
+ free(SubDir->Forms);
+ free(SubDir->FormSet);
+
+ free(Dir->Dirs[l]);
+ }
+ }
+ free(Dir->DirName);
+ free(Dir->Forms);
+ free(Dir->FormSet);
+ free(Dir);
+ }
+
+ free(HtmlFormDirs);
+ return;
+}
+
+char * initMultipartUnpack(char ** Input)
+{
+ // Check if Multipart and return Boundary. Update Input to first part
+
+ // look through header for Content-Type line, and if multipart
+ // find boundary string.
+
+ char * ptr, * ptr2;
+ char Boundary[128];
+ BOOL Multipart = FALSE;
+
+ ptr = *Input;
+
+ while(*ptr != 13)
+ {
+ ptr2 = strchr(ptr, 10); // Find CR
+
+ while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line
+ ptr2 = strchr(&ptr2[1], 10); // Find CR
+
+ if (_memicmp(ptr, "Content-Type: ", 14) == 0)
+ {
+ char Line[256] = "";
+ char * ptr3;
+ size_t len = ptr2-ptr-14;
+
+ if (len >255)
+ return NULL;
+
+ memcpy(Line, &ptr[14], len);
+
+ if (_memicmp(Line, "Multipart/", 10) == 0)
+ {
+ ptr3 = stristr(Line, "boundary");
+ if (ptr3)
+ {
+ ptr3+=9;
+
+ if ((*ptr3) == '"')
+ ptr3++;
+
+ strcpy(Boundary, ptr3);
+ ptr3 = strchr(Boundary, '"');
+ if (ptr3) *ptr3 = 0;
+ ptr3 = strchr(Boundary, 13); // CR
+ if (ptr3) *ptr3 = 0;
+ break;
+ }
+ else
+ return NULL; // Can't do anything without a boundary ??
+ }
+ }
+ ptr = ptr2;
+ ptr++;
+ }
+
+ // Find First part - there is a boundary before it
+
+ ptr = strstr(ptr2, Boundary);
+
+ // Next should be crlf then part
+
+ ptr = strstr(ptr, "\r\n");
+
+ if (ptr)
+ ptr += 2; // Over CRLF
+
+ *Input = ptr; // Return first part or NULL
+ return _strdup(Boundary);
+}
+
+BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End)
+{
+ // Format seems to be
+/*
+ ------WebKitFormBoundaryABJaEbBWB5SuAHmq
+ Content-Disposition: form-data; name="Subj"
+
+ subj
+ ------WebKitFormBoundaryABJaEbBWB5SuAHmq
+ Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt"
+ Content-Type: text/plain
+
+ c:\exiftool "-filenameSave Attachments ", Key, Msg->number);
+
+ WebMail->Files = 0;
+
+ ptr1 = MsgBytes;
+
+ // ptr += sprintf(ptr, "Message has Attachments\r\n\r\n");
+
+ while(*ptr1 != 13)
+ {
+ ptr2 = strchr(ptr1, 10); // Find CR
+
+ if (memcmp(ptr1, "Body: ", 6) == 0)
+ {
+ BodyLen = atoi(&ptr1[6]);
+ }
+
+ if (memcmp(ptr1, "File: ", 6) == 0)
+ {
+ char * ptr3 = strchr(&ptr1[6], ' '); // Find Space
+ *(ptr2 - 1) = 0;
+
+ WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]);
+ WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]);
+ *(ptr2 - 1) = ' '; // put space back
+ }
+
+ ptr1 = ptr2;
+ ptr1++;
+ }
+
+ ptr1 += 2; // Over Blank Line and Separator
+
+ // ptr1 is pointing to body. Save for possible reply
+
+ WebMail->Body = malloc(BodyLen + 2);
+ memcpy(WebMail->Body, ptr1, BodyLen);
+ WebMail->Body[BodyLen] = 0;
+
+ *(ptr1 + BodyLen) = 0;
+
+ ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body
+
+ ptr1 += BodyLen + 2; // to first file
+
+ // Save pointers to file
+
+ attptr = ptr1;
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ WebMail->FileBody[i] = malloc(WebMail->FileLen[i]);
+ memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]);
+ attptr += (WebMail->FileLen[i] + 2);
+ }
+
+ // if first (only??) attachment is XML and filename
+ // starts "RMS_Express_Form" process as HTML Form
+
+ if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0)
+ {
+ int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments"
+ free(MsgBytes);
+
+ // Flag as read
+
+ if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
+ {
+ if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
+ {
+ if (Msg->status != 'Y')
+ {
+ Msg->status = 'Y';
+ Msg->datechanged=time(NULL);
+ SaveMessageDatabase();
+ SendMessageReadEvent(Session->Callsign, Msg);
+ }
+ }
+ }
+
+ return Len;
+ }
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ int n;
+ char * p = ptr1;
+ char c;
+
+ // Check if message is probably binary
+
+ int BinCount = 0;
+
+ NewLen = WebMail->FileLen[i];
+
+ for (n = 0; n < NewLen; n++)
+ {
+ c = *p;
+
+ if (c==0 || (c & 128))
+ BinCount++;
+
+ p++;
+
+ }
+
+ if (BinCount > NewLen/10)
+ {
+ // File is probably Binary
+
+ ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]);
+ }
+ else
+ {
+ *(ptr1 + NewLen) = 0;
+ ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]);
+ RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own
+
+ ptr += sprintf(ptr, "%s\r\r", ptr1);
+
+ User->Total.MsgsSent[Index] ++;
+ User->Total.BytesForwardedOut[Index] += NewLen;
+ }
+
+ ptr1 += WebMail->FileLen[i];
+ ptr1 +=2; // Over separator
+ }
+
+ free(Save);
+
+ ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from);
+
+ RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own
+
+ if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
+ {
+ if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
+ {
+ if (Msg->status != 'Y')
+ {
+ Msg->status = 'Y';
+ Msg->datechanged=time(NULL);
+ SaveMessageDatabase();
+ SendMessageReadEvent(Session->Callsign, Msg);
+ }
+ }
+ }
+
+ if (DisplayHTML && stristr(Message, ""))
+ DisplayStyle = "div"; // Use div so HTML and XML are interpreted
+
+ return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle);
+ }
+
+ // Remove B2 Headers (up to the File: Line)
+
+ // ptr1 = strstr(MsgBytes, "Body:");
+
+ // if (ptr1)
+ // MsgBytes = ptr1;
+ }
+
+ // Body may have cr cr lf which causes double space
+
+ crcrptr = strstr(MsgBytes, "\r\r\n");
+
+ while (crcrptr)
+ {
+ *crcrptr = ' ';
+ crcrptr = strstr(crcrptr, "\r\r\n");
+ }
+
+ // Remove lf chars
+
+ msgLen = RemoveLF(MsgBytes, msgLen);
+
+ User->Total.MsgsSent[Index] ++;
+ // User->Total.BytesForwardedOut[Index] += Length;
+
+ // if body not UTF-8, convert it
+
+ if (WebIsUTF8(MsgBytes, msgLen) == FALSE)
+ {
+ int code = TrytoGuessCode(MsgBytes, msgLen);
+
+ UCHAR * UTF = malloc(msgLen * 3);
+
+ if (code == 437)
+ msgLen = Convert437toUTF8(MsgBytes, msgLen, UTF);
+ else if (code == 1251)
+ msgLen = Convert1251toUTF8(MsgBytes, msgLen, UTF);
+ else
+ msgLen = Convert1252toUTF8(MsgBytes, msgLen, UTF);
+
+ free(MsgBytes);
+ Save = MsgBytes = UTF;
+
+ MsgBytes[msgLen] = 0;
+ }
+
+ // ptr += sprintf(ptr, "%s", MsgBytes);
+
+ memcpy(ptr, MsgBytes, msgLen);
+ ptr += msgLen;
+ ptr[0] = 0;
+
+ free(Save);
+
+ ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from);
+
+ if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
+ {
+ if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
+ {
+ if (Msg->status != 'Y')
+ {
+ Msg->status = 'Y';
+ Msg->datechanged=time(NULL);
+ SaveMessageDatabase();
+ SendMessageReadEvent(Session->Callsign, Msg);
+ }
+ }
+ }
+ }
+ else
+ {
+ ptr += sprintf(ptr, "File for Message %d not found\r", Number);
+ }
+
+ if (DisplayHTML && stristr(Message, ""))
+ DisplayStyle = "div"; // Use div so HTML and XML are interpreted
+
+
+ return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle);
+}
+
+int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number)
+{
+ struct MsgInfo * Msg;
+ char Message[100] = "";
+
+ Msg = GetMsgFromNumber(Number);
+
+ if (Msg == NULL)
+ {
+ sprintf(Message, "Message %d not found", Number);
+ goto returnit;
+ }
+
+ if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg))
+ {
+ FlagAsKilled(Msg, TRUE);
+ sprintf(Message, "Message #%d Killed\r", Number);
+ goto returnit;
+ }
+
+ sprintf(Message, "Not your message\r");
+
+returnit:
+ return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div");
+}
+
+void freeKeys(KeyValues * Keys)
+{
+ while (Keys->Key)
+ {
+ free(Keys->Key);
+ free(Keys->Value);
+ Keys++;
+ }
+}
+
+void FreeWebMailFields(WebMailInfo * WebMail)
+{
+ // release any malloc'ed resources
+
+ int i;
+ char * SaveReply;
+ int * SaveRlen;
+
+ if (WebMail == NULL)
+ return;
+
+ if (WebMail->txtFile)
+ free(WebMail->txtFile);
+
+ if (WebMail->txtFileName)
+ free(WebMail->txtFileName);
+
+ if (WebMail->InputHTMLName)
+ free(WebMail->InputHTMLName);
+
+ if (WebMail->DisplayHTMLName)
+ free(WebMail->DisplayHTMLName);
+
+ if (WebMail->ReplyHTMLName)
+ free(WebMail->ReplyHTMLName);
+
+ if (WebMail->To)
+ free(WebMail->To);
+ if (WebMail->CC)
+ free(WebMail->CC);
+ if (WebMail->Subject)
+ free(WebMail->Subject);
+ if (WebMail->BID)
+ free(WebMail->BID);
+ if (WebMail->Body)
+ free(WebMail->Body);
+ if (WebMail->XML)
+ free(WebMail->XML);
+ if (WebMail->XMLName)
+ free(WebMail->XMLName);
+
+ if (WebMail->OrigTo)
+ free(WebMail->OrigTo);
+ if (WebMail->OrigSubject)
+ free(WebMail->OrigSubject);
+ if (WebMail->OrigBID)
+ free(WebMail->OrigBID);
+ if (WebMail->OrigBody)
+ free(WebMail->OrigBody);
+
+ freeKeys(WebMail->txtKeys);
+ freeKeys(WebMail->XMLKeys);
+
+ for (i = 0; i < WebMail->Files; i++)
+ {
+ free(WebMail->FileBody[i]);
+ free(WebMail->FileName[i]);
+ }
+
+ if (WebMail->Header)
+ free(WebMail->Header);
+ if (WebMail->Footer)
+ free(WebMail->Footer);
+
+ SaveReply = WebMail->Reply;
+ SaveRlen = WebMail->RLen;
+
+#ifndef WIN32
+ if (WebMail->iconv_toUTF8)
+ iconv_close(WebMail->iconv_toUTF8);
+#endif
+
+ memset(WebMail, 0, sizeof(WebMailInfo));
+
+ WebMail->Reply = SaveReply;
+ WebMail->RLen = SaveRlen;
+
+ return;
+}
+
+
+void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen)
+{
+ char * URLParams = strlop(Key, '&');
+ int ReplyLen;
+ char Appl = 'M';
+
+ // Webmail doesn't use the normal Mail Key.
+
+ // webscript.js doesn't need a key
+
+ if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0)
+ {
+ if (jsTemplate)
+ free(jsTemplate);
+
+ jsTemplate = GetTemplateFromFile(2, "webscript.js");
+
+ ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
+ "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate);
+ *RLen = ReplyLen;
+ return;
+ }
+
+ // Neither do js or file downloads
+
+ // This could be a request for a Template file
+ // WebMail/Local_Templates/My Forms/inc/logo_ad63.png
+ // WebMail/Standard Templates/
+
+
+ if (_memicmp(NodeURL, "/WebMail/Local", 14) == 0 || (_memicmp(NodeURL, "/WebMail/Standard", 17) == 0))
+ {
+ int FileSize;
+ char * MsgBytes;
+ char MsgFile[512];
+ FILE * hFile;
+ size_t ReadLen;
+ char TimeString[64];
+ char FileTimeString[64];
+ struct stat STAT;
+ char * FN = &NodeURL[9];
+ char * fileBit = FN;
+ char * ext;
+ char Type[64] = "Content-Type: text/html\r\n";
+
+ UndoTransparency(FN);
+ ext = strchr(FN, '.');
+
+ sprintf(MsgFile, "%s/%s", BPQDirectory, FN);
+
+ while (strchr(fileBit, '/'))
+ fileBit = strlop(fileBit, '/');
+
+ if (stat(MsgFile, &STAT) == -1)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == 0)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ FileSize = STAT.st_size;
+ MsgBytes = malloc(FileSize + 1);
+ ReadLen = fread(MsgBytes, 1, FileSize, hFile);
+
+ fclose(hFile);
+
+ FormatTime2(FileTimeString, STAT.st_ctime);
+ FormatTime2(TimeString, time(NULL));
+
+ ext++;
+
+ if (_stricmp(ext, "js") == 0)
+ strcpy(Type, "Content-Type: text/javascript\r\n");
+
+ if (_stricmp(ext, "css") == 0)
+ strcpy(Type, "Content-Type: text/css\r\n");
+
+ if (_stricmp(ext, "pdf") == 0)
+ strcpy(Type, "Content-Type: application/pdf\r\n");
+
+ if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 ||
+ _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0)
+ strcpy(Type, "Content-Type: image\r\n");
+
+ // File may be binary so output header then copy in message
+
+ *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
+ "%s"
+ "Date: %s\r\n"
+ "Last-Modified: %s\r\n"
+ "\r\n", FileSize, Type,TimeString, FileTimeString);
+
+ memcpy(&Reply[*RLen], MsgBytes, FileSize);
+ *RLen += FileSize;
+ free (MsgBytes);
+ return;
+ }
+
+ //
+
+ if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0)
+ {
+ int FileSize;
+ char * MsgBytes;
+ char MsgFile[512];
+ FILE * hFile;
+ size_t ReadLen;
+ char TimeString[64];
+ char FileTimeString[64];
+ struct stat STAT;
+ char * FN = &NodeURL[16];
+ char * fileBit = FN;
+ char * ext;
+ char Type[64] = "Content-Type: text/html\r\n";
+
+
+ UndoTransparency(FN);
+ ext = strchr(FN, '.');
+
+ sprintf(MsgFile, "%s/%s", BPQDirectory, FN);
+
+ while (strchr(fileBit, '/'))
+ fileBit = strlop(fileBit, '/');
+
+ if (stat(MsgFile, &STAT) == -1)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == 0)
+ {
+ *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
+ return;
+ }
+
+ FileSize = STAT.st_size;
+ MsgBytes = malloc(FileSize + 1);
+ ReadLen = fread(MsgBytes, 1, FileSize, hFile);
+
+ fclose(hFile);
+
+ FormatTime2(FileTimeString, STAT.st_ctime);
+ FormatTime2(TimeString, time(NULL));
+
+ ext++;
+
+ if (_stricmp(ext, "js") == 0)
+ strcpy(Type, "Content-Type: text/javascript\r\n");
+
+ if (_stricmp(ext, "css") == 0)
+ strcpy(Type, "Content-Type: text/css\r\n");
+
+ if (_stricmp(ext, "pdf") == 0)
+ strcpy(Type, "Content-Type: application/pdf\r\n");
+
+ if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "jpeg") == 0 || _stricmp(ext, "png") == 0 ||
+ _stricmp(ext, "gif") == 0 || _stricmp(ext, "bmp") == 0 || _stricmp(ext, "ico") == 0)
+ strcpy(Type, "Content-Type: image\r\n");
+
+ // File may be binary so output header then copy in message
+
+ *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
+ "%s"
+ "Date: %s\r\n"
+ "Last-Modified: %s\r\n"
+ "\r\n", FileSize, Type,TimeString, FileTimeString);
+
+ memcpy(&Reply[*RLen], MsgBytes, FileSize);
+ *RLen += FileSize;
+ free (MsgBytes);
+ return;
+ }
+
+ Session = NULL;
+
+ if (Key && Key[0])
+ Session = FindWMSession(Key);
+
+ if (Session == NULL)
+ {
+ // Lost Session
+
+ if (LOCAL)
+ {
+ Session = AllocateWebMailSession();
+
+ Key = Session->Key;
+
+ if (SYSOPCall[0])
+ Session->User = LookupCall(SYSOPCall);
+ else
+ Session->User = LookupCall(BBSName);
+
+ if (Session->User)
+ {
+ strcpy(NodeURL, "/WebMail/WebMail");
+ Session->WebMailSkip = 0;
+ Session->WebMailLastUsed = time(NULL);
+ }
+ }
+ else
+ {
+ // Send Login Page unless Signon request
+
+ if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0)
+ {
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+ }
+ }
+
+ if (strcmp(Method, "POST") == 0)
+ {
+ if (_stricmp(NodeURL, "/WebMail/Signon") == 0)
+ {
+ char * msg = strstr(input, "\r\n\r\n"); // End of headers
+ char * user, * password, * Key;
+ char Msg[128];
+ int n;
+
+ if (msg)
+ {
+ struct UserInfo * User;
+
+ if (strstr(msg, "Cancel=Cancel"))
+ {
+ *RLen = sprintf(Reply, "");
+ return;
+ }
+ // Webmail Gets Here with a dummy Session
+
+ Session = AllocateWebMailSession();
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+
+ Key = Session->Key;
+
+ user = strtok_s(&msg[9], "&", &Key);
+ password = strtok_s(NULL, "=", &Key);
+ password = Key;
+
+ Session->User = User = LookupCall(user);
+
+ if (User)
+ {
+ // Check Password
+
+ if (password[0] && strcmp(User->pass, password) == 0)
+ {
+ // send Message Index
+
+ Session->WebMailLastUsed = time(NULL);
+ Session->WebMailSkip = 0;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+ Session->WebMailMine = FALSE;
+
+ if (WebMailTemplate)
+ {
+ free(WebMailTemplate);
+ WebMailTemplate = NULL;
+ }
+
+ if (User->flags & F_Excluded)
+ {
+ n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user));
+ WriteLogLine(NULL, '|',Msg, n, LOG_BBS);
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user));
+ WriteLogLine(NULL, '|',Msg, n, LOG_BBS);
+
+ return;
+ }
+
+ }
+
+ // Bad User or Pass
+
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+ }
+
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+ if (_stricmp(NodeURL, "/WebMail/EMSave") == 0)
+ {
+ // Save New Message
+
+ SaveNewMessage(Session, input, Reply, RLen, Key, InputLen);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/Submit") == 0)
+ {
+ // Get the POST data from the page and place in message
+
+ char * param = strstr(input, "\r\n\r\n"); // End of headers
+ WebMailInfo * WebMail = Session->WebMail;
+
+ if (WebMail == NULL)
+ return; // Can't proceed if we have no info on form
+
+ ProcessFormInput(Session, input, Reply, RLen, InputLen);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0)
+ {
+ // Save New Message
+
+ SaveTemplateMessage(Session, input, Reply, RLen, Key);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0)
+ {
+ SendTemplateSelectScreen(Session, input, InputLen);
+ return;
+ }
+
+ // End of POST section
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0)
+ {
+ Session->Key[0] = 0;
+ Session->WebMailLastUsed = 0;
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+
+ if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) ||
+ (_stricmp(NodeURL, "/WebMail") == 0) ||
+ (_stricmp(NodeURL, "/WebMail/") == 0))
+ {
+ // Entry from Menu if signed in, continue. If not and Localhost
+ // signin as sysop.
+
+ if (Session->User == NULL)
+ {
+ // Not yet signed in
+
+ if (LOCAL)
+ {
+ // Webmail Gets Here with a dummy Session
+
+ Session = AllocateWebMailSession();
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+ Key = Session->Key;
+
+ if (SYSOPCall[0])
+ Session->User = LookupCall(SYSOPCall);
+ else
+ Session->User = LookupCall(BBSName);
+
+ if (Session->User)
+ {
+ strcpy(NodeURL, "/WebMail/WebMail");
+ Session->WebMailSkip = 0;
+ Session->WebMailLastUsed = time(NULL);
+ }
+ }
+ else
+ {
+ // Send Login Page
+
+ ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
+ *RLen = ReplyLen;
+ return;
+ }
+ }
+ }
+
+ Session->WebMail->Reply = Reply;
+ Session->WebMail->RLen = RLen;
+
+ if (_stricmp(NodeURL, "/WebMail/WebMail") == 0)
+ {
+ if (WebMailTemplate)
+ {
+ free(WebMailTemplate);
+ WebMailTemplate = NULL;
+ }
+
+ Session->WebMailSkip = 0;
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMAll") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMB") == 0)
+ {
+ Session->WebMailSkip = 0;
+ strcpy(Session->WebMailTypes, "B");
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMP") == 0)
+ {
+ Session->WebMailSkip = 0;
+ strcpy(Session->WebMailTypes, "P");
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMT") == 0)
+ {
+ Session->WebMailSkip = 0;
+ strcpy(Session->WebMailTypes, "T");
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMMine") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = TRUE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMtoMe") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = FALSE;
+ Session->WebMailMyTX = FALSE;
+ Session->WebMailMyRX = TRUE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMfromMe") == 0)
+ {
+ Session->WebMailSkip = 0;
+ Session->WebMailTypes[0] = 0;
+ Session->WebMailMine = TRUE;
+ Session->WebMailMyTX = TRUE;
+ Session->WebMailMyRX = FALSE;
+
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+
+ if (_stricmp(NodeURL, "/WebMail/WMSame") == 0)
+ {
+ *RLen = SendWebMailHeader(Reply, Session->Key, Session);
+ return;
+ }
+
+ if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0)
+ {
+ // Auto Refresh Version of index page. Uses Web Sockets
+
+ char Page[4096];
+
+ char WebSockPage[] =
+ "\r\n"
+ " \r\n"
+ " \r\n"
+ " \r\n"
+ " \r\n"
+ "\r\n"
+ "\r\n"
+
+ "\r\n"
+ " %s Webmail Interface - User %s - Message List
\r\n"
+ "
\r\n"
+ "\r\n"
+ "\r\n"
+ " Bulls \r\n"
+ "Personal \r\n"
+ "NTS \r\n"
+ "All Types \r\n"
+ "Mine \r\n"
+ "My Sent \r\n"
+ "My Rxed \r\n"
+ "Auto Refresh \r\n"
+ "Send Message \r\n"
+ "Logout \r\n"
+ "Node Menu
\r\n"
+
+ "
"
+ "
| Reply | " + "Kill Message | " + "Display as Text | " + "Next | " + "Previous | " + "Back to List | " + "